Re: Another Threading Issue
Re: Another Threading Issue
- Subject: Re: Another Threading Issue
- From: Philippe Wicker <email@hidden>
- Date: Wed, 17 Dec 2003 22:20:22 +0100
On Wednesday, December 17, 2003, at 08:05 PM, Jeremy Jurksztowicz
wrote:
I had a similar problem to solve a few times ago. A bunch of variables
had to be updated as a result of some user commands and this update had
to be synchronized with the rendering thread. The solution was to use a
non blocking FIFO. Data to atomically update are pushed - as one block
of data - by the main thread into the FIFO, and pulled out by the
render thread at the beginning of each render cycle. I don't know
wether this approach fits your needs or not. As far as I have
understood, the fifo::push should be called in setEnv1, and the
fifo::pull in pullDataStream. Using this method, you don't need anymore
to lock the sharedMutex.
A non blocking fifo is not very complicated to design. I have my own
code (a C++ class) that I can provide you if you wish. Another
alternative is to look in the list archive for a Bill Stewart posting
about a queue to push midi notes (sorry, I don't remember the exact
subject). The queue code was targeted at MIDI notes exchange but can
easily be modified to fit general needs.
I hate to beat a dead horse but...
I have recently ported a windows app to CoreAudio, using the HAL.
My audio supplier class provides PCM wave data from a buffer stored on
memory. The way the data is provided to
the IO proc depends on parameters in the class instance of course. In
the windows code I was doing something like this:
class Provider {
Envelope * env1;
float data1;
// ... Many more variables ...
public:
void pullDataStream (float ** buffs, unsigned int frames,
unsigned int channels) {
Lock lck(sharedMutex);
if(data1 => somethingOrOther)
doThisOrThat();
// ... Get all the data ...
env1->applySectionAndIncrement(buffs,frames,channels);
lck.unlock();
}
void setEnv1 (Envelope* env) {
Lock lck(sharedMutex);
delete env1;
env1 = env;
if(env1) env1->syncronizeWithThis(this); // Complex method
to make sure the envelope is synced.
}
// Blah, Blah, Blah, you get the idea...
};
While this code worked, and still works just fine, I keep seeing posts
about how no blocking calls should be made in the
IO proc, including memory allocations and THREAD LOCKS. Seeing as I
have to pass data to the io thread while it is running,
how do I get it there without locking? So of course I did an
exhaustive search of the mailing list archives, and lo and behold
I get all sorts of stuff on the circular buffer. Great!
BUT! As you can see in the example above, In the protected section of
setEnv() there is more than just a parameter assignment,
there is also some complex code to sync the param (in this case an
envelope) with the rest of the classes sound data. This code
must either:
1-Run in a protected block on the main thread.
2-Run in the IO thread each and every cycle of getting audio data!
The reason behind 2 is that if I can atomicaly change the envelope
parameter, It does me no good (rather harmful, really) if it is
not synced up before ANY MORE audio data is processed. Sure I could
put another member, env1DidChange of boolean type,
and update that as well, but what if the envelope is required between
calls to set it and it's boolean changed flag?
This is an artificial example, but this pattern of behavior is very
common to this class, which can do some wonderful things
because it handles such high level abstractions such as Envelopes,
Filters, etc... rather than having all its parameters low
level floating point primitives.
So my first idea was to change my provider class to an AudioUnit, they
provide auto synchronization... right? ... Right?!
I looked up and down in the AUBase class to see if there was thread
syncing, and eureka! I see something about an
AUElement class, and getting parameters, and AHA! In the AUBase class
there is SafeGetElement()! To my disappointment
this has nothing to do with thread safety (or did I miss something?).
So perhaps some example AudioUnit code will tell me
what I need to know...? No thread syncing in ANY example code I have
downloaded...
So, here are my questions:
1. If I implement audio units to specification, do I have to implement
any thread safety measures if it will only be accessed by the
IO thread AND the main thread. If no syncing is needed, should I
only access my AU through the C AudioUnits API, or can I safely
call its class methods directly? (from the AUBase source code this
seems unlikely)
2. If I decide not to implement an AU and reuse my provider class,
would it be reasonably good practice to pull its audio data in a
feeder thread, locking to my hearts content (only one lock per IO
cylcle is required), and pumping the data through a circular buffer
to the high priority IO thread?
I know that threading issues are common on this list, and am sorry If
my late night archive browsing missed the obvious answers.
Thank you for your help.
Jeremy Jurksztowicz
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.
Philippe Wicker
email@hidden
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.