I had an occasional deadlock in an audio program I'm developing.
After realizing my original design was full of race conditions, I
refactored it in a way that should not have had deadlocks or race
To my dismay, my occasional deadlock turned into one that happened
every time I ran my program!
ZooLib comes with automatic deadlock detection. I'd never used it
before, but apparently to enable it one needs to install a deadlock
hander function that gets called when a deadlock occurs. Mine just
called ZAssert( false ).
The mystery deepened when I found my deadlock handler never got
called. Some further debugging found that the count of the ZMutex was
zero when I tried to acquire it, but it still never gets acquired.
What was really weird was that in the place where it gets stuck, just
a few lines of code before it successfully acquired and released the
My application runs on both Windows and Mac OS X. I tried my new code
on OS X, and found the deadlock never happened. It worked flawlessly
the first time I tried it.
This suggested that the fault lay in my Windows audio output code, as
there is very little else of my client code that's different between
the two platforms. It had worked before, I hadn't changed it in my
refactoring. However, it had a bug in which the sound wouldn't start
for a couple seconds after I told it to.
In tracing down into ZMutex::Acquire, I found that the implementation
of it is quite complex. I began to suspect my audio output was
corrupting its memory, so what I did was to start commenting off
different bits of the output driver until I got the deadlock to go
To my astonishment, the culprit was the following line of code at the
start of the thread that actually sends the audio samples to the sound
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
This is a Win32 system call. When I wrote the Windows output driver
months ago, I was having some performance problems and found it would
help keep my sound playing steadily.
Not only did deleting it make the deadlock go away, but a bug in which
the audio didn't start playing for a couple seconds also went away.
Now when you click my app's Play button, the music plays immediately.
It also turns out that I don't need to raise my output thread priority
anymore to keep the music playing steadily. I don't know what was
wrong before, but it must have been some other bug that I've since
I don't think this indicates any kind of bug in ZooLib. I'm posting
it to let you know that things won't work right if you raise a
thread's priority. On the other hand, if you have some good reason
for doing so, you're going to have to expect to have to hack on
ZThread.cpp to make it work.
I'm very pleased to have found this bug as the delay in output and the
deadlocks were a severe usability problem for my Windows users. What
I think was happened was than when I signaled my output driver's
semaphore to tell it to start sending samples to the Windows WaveOut
driver, it took a couple seconds for the call to complete. I didn't
have the first clue what was wrong.
Perhaps someone with a deeper understanding of Windows threading can
explain these phenomena.
My audio app is Ogg Frog at http://www.oggfrog.com/ It is to be GNU
GPL Free Software. I expect to release it in February.
However, the core audio processing engine will be placed under the MIT
License, which is also ZooLib's license, as it is my intention to
provide a cross-platform audio architecture for ZooLib. I have sound
output now on Mac OS X and Windows, and will soon on Linux and BeOS.
I have decoders now for Ogg Vorbis and MP3. (The libmad MP3 decoder I
use is GPL though.) I'm going to add decoders for FLAC, Ogg FLAC, WAV
and probably speex.
mdcrawford at gmail dot com