Re: AU Threading Question
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