• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: AU Threading Question
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: AU Threading Question


  • Subject: Re: AU Threading Question
  • From: Brian Willoughby <email@hidden>
  • Date: Fri, 08 Apr 2011 01:54:14 -0700


On Apr 7, 2011, at 10:16, Stephen Blinkhorn wrote:
By now I should know better about all this. However, I have a question about threading issues within an AU.

In the render thread of our AU instruments we calculate various peak meter level readings and store this data into a plain C float array. The array is a member of the AU class itself. In the Cocoa view an NSTimer fires off a GetProperty() event which performs a memcpy of the C array which the view uses to update the meters.

Is there some potential for blocking here if the Render thread is writing the peak data when the GetProperty() call comes in?
I don't understand your question at all, but perhaps there is a misunderstanding of the difference between what it means for two threads to access the same data versus what it means for a thread to call a blocking function or enter a mutex condition.

The whole issue with multithreaded programming is that there is never any automatic protection that your code will not enter the same function twice, nor that it will not read and write the same data from more than one thread at a time. In other words, just because two threads access the same data doesn't mean that only one is allowed to execute at a time.

What you seem to be wanting to ask is whether there is some potential for what is call a "race condition," where a consumer thread happens to read a complex data set in the middle of the producer thread changing that same data.

There will not be any blocking unless you call a blocking function (e.g., disk access, memory allocation), or you set up a specific mutex condition that purposely causes a block.

The short answer to the question I assume you're asking is: Yes, there is potential for a race condition here.

While your Cocoa view thread is right in the middle of the memcpy() function, the operating system is perfectly free to switch to another thread, and it probably will do so at the worst possible time. It is entirely possible that the Render thread will change the data before memcpy() is finished. When that happens, what you will have is a situation where the first few entries in your view's C float array will have old peak meter levels, and the remaining entries in the array will have new peak meter levels. As far as I recall, individual float values are updated atomically, so you will never end up with a nonsense peak meter level which combines partial bits from old and new values.

In some respects, this potential is not problematic, because the displayed values will always be valid snapshots of one recent value or another, just perhaps from different times. However, if it is important to you that each of your meters always shows the same instant in time as the rest, then you should take steps to deal with the race condition.


Or is it possible that the Render method will have to wait for the GetProperty()read to finish before writing new values?

Are you asking whether the Render method will be automatically blocked because some other thread is inside GetProperty()? ... or are you asking whether you should add your own code to specifically and purposely block the Render thread until the GetProperty() read is finished?


If I take your question literally, as written, then: No, the processor has no idea that reading or writing the values in your C float array are considered as a single operation by you and the users of your Cocoa UI, and therefore the Render method will not automatically wait.

As far as the processor is concerned, the entire array is just separate memory locations that can be read or written in any order at any time, and threading will allow them to be written by one thread in the middle of being read by another thread. In other words, nothing is going to automatically make the Render thread stop while some other thread is reading these values. No part of the code is aware of the special purpose of these memory locations, or the tie-in between the writer (producer) and reader (consumer). You, as the programmer, must establish the rules and enforce them.

The problem is that the first and most basic technique of multi- threaded programming would suggest that you grab a mutex whenever writing or reading the array, such that each thread must wait for the other to finish with the entire array before the other thread is allowed to start with the array. Unfortunately, real-time audio does not work when you try to grab a mutex in the Render thread, so basically, you have to allow the writer (producer) thread to change the array any time that it wants to. In other words, the most basic multi-threading solutions will not work with CoreAudio. There are non-blocking variants of the mutex calls, but then your Render thread would be forced to completely skip the update of the peak meter values any time that the Cocoa thread happens to hold the mutex. This might actually be fine, since the Cocoa view is "slow" compared to the Render thread, anyway.


I would like to ask you to step back for a moment and consider what your program is doing, as designed at the moment.


It appears that you are updating the peak meter levels on every Render call (or at least you haven't specified any mechanism in your code to limit how frequently these values are updated). With typical buffer sizes, this represents an update rate of maybe 10 times per second (4096 frames per buffer @ 44,100 frames per second) to as high as maybe 345 times per second (128 frames per buffer @ 44,100 frames per second). With low latency and high sample rates, which are quite popular these days, you could easily have hundreds or thousands of updates per second.

At the same time, you have an NSTimer polling your data - hopefully at a more sensible rate like 24 Hz or so, certainly less than 60 Hz at the most. But that NSTimer is not synchronized with the Render updates, and if your Render thread is updating the values hundreds or thousands of times per second it's highly likely that an update will change part of the array in the middle of your Cocoa view copying the values. This is a textbook example of a race condition.


A better approach would be to reduce the update rate so that it is as slow as the display rate, and then you'd have much less likelihood of hitting that race condition. Also, instead of polling the data for changes at a rate that is not synchronized to the updates, why not just have the Render thread tell the Cocoa view that something has changed? This avoids polling entirely, and should eliminate the race condition unless the Cocoa view is so slow to respond that it catches the next update (if your Cocoa view is too slow to keep up, then you should slow the update rate anyway).


The basic textbook multi-threading solution would be to create a semaphore that the Render thread will signal each time the peak meter levels have been updated. The Cocoa view thread would simply wait for this semaphore to fire (instead of waiting for an arbitrary NSTimer to fire), and then the update would occur (pending thread scheduling delays based on priority and such). Unfortunately, the CoreAudio Render engine and the Cocoa view may not be on the same computer, so you can't share a semaphore between the two (at least not legally in a way that will survive changes to CoreAudio). Thankfully, CoreAudio provides a notification system for property updates. All you need to do is have the Render thread send a notification on each of your peak meter level updates, and then register your Cocoa view for changes on this parameter. Your Cocoa view will then be called every time there is an update.

Thus, if you slow your Render updates to these shared values to a reasonable rate, you're saving a little processing time. Only generate update notifications at the rate you want changes displayed. You also avoid race conditions (for the most part) and avoid polling data (polling wastes CPU, usually unnecessarily).

My apologies if I misunderstood your question or if you already knew all of this.

Brian Willoughby
Sound Consulting

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: AU Threading Question
      • From: Stephen Blinkhorn <email@hidden>
    • Re: AU Threading Question
      • From: tahome izwah <email@hidden>
References: 
 >AU Threading Question (From: Stephen Blinkhorn <email@hidden>)

  • Prev by Date: Re: Reverb algorithm/source for RemoteIO on iPhone
  • Next by Date: Re: AU Threading Question
  • Previous by thread: AU Threading Question
  • Next by thread: Re: AU Threading Question
  • Index(es):
    • Date
    • Thread