[LAD] some questions about writing a jack client

Gabriel M. Beddingfield gabriel at teuton.org
Wed Nov 25 12:48:14 UTC 2009


Hi Mutil,

On Wed, 25 Nov 2009, mutil wrote:

> 	I am trying to write a c++/qt4 jack client which mix 2 inputs
> and has a jack output. The problem is that currently jack gets
> zombified too often and I would like some info on how can I debug
> the exact reason this happens. I suspect it's the way the app is
> written and I am planning to rewrite it but I need some advices
> first.

I've found that zombies are actually very hard to
debug.  They happen when your application takes "too
long" in the process() cycle (for whatever reason).  It
could be that you did a realtime violation (like
allocating memory), or it could be that you used an
O(N^10) algorithm.

When you start jackd, it says something like:

configuring for 48000Hz, period = 1024 frames (21.3 ms), buffer = 4 periods

The 21.3 ms is the time for /EVERYONE/ to get their
audio processing done.  If someone takes to long, jackd
puts them to death.

But, getting a 'jack zombie' is kind of like a
hit-and-run.  By the time you get declared a zombie,
the 'thing' that caused it has sped off down the road
anonymously.  I think I've even seen it where the wrong
application gets marked as a zombie.  By the time jackd
marks your program as a zombie, your O(N^10) algorithm
might be done... and it looks like `int j=5;` caused
the problem.  :-)

In the end, the best way I found to fix this problem is
to statically audit your code for "real time
violations."  If your program is large, Doxygen has a
feature to create static call graphs... which I found
helpful.

The JACK Documentation for jack_set_process_callback()
says:

     The code in the supplied function must be suitable
     for real-time execution. That means that it cannot
     call functions that might block for a long
     time. This includes malloc, free, printf,
     pthread_mutex_lock, sleep, wait, poll, select,
     pthread_join, pthread_cond_wait, etc, etc.

Notice that locking a mutex is in the list.  However,
it /is/ possible to use a mutex and be realtime
safe... but this means that anything between lock and
unlock must /also/ be realtime safe.  In other words,
anything done while the mutex is locked is essentially
happening in the process() function that you've set as
a callback.  Consider this:

     QMutex mutex;
     float* buf;

     foo() {
         mutex.lock();
         buf = new float[4096];
         mutex.unlock();
     }

     int process(jack_nframes_t nframes) {
         mutex.lock();
         for(int j=0 ; j<4096 ; ++j) {
             buf[j] = 0.0;
         }
         mutex.unlock();
         return 0;
     }

process() starts by locking a mutex.  If foo() already
has it locked, that means that process() is going to
have to WAIT until foo() unlocks it.  But foo() is
allocating memory dynamically (which you can't do in
realtime sections of code).  That's essentially the
same as this:

     float* buf;

     int process(jack_nframes_t nframes) {
         buf = new float[4096];
         for(int j=0 ; j<4096 ; ++j ) {
             buf[j] = 0.0;
         }
         return 0;
     }

So you see, by using a mutex... you have to consider
those sections of code as being a part of your
process() function.

> 	The way it's currently written makes use of mutexes and locks
> to pass the data beetween a custom global buffer. Is this the reason
> for not synchronizing

Is the custom global buffer allocating memory while the
mutex is locked?  If so, that's your problem.

> things? Should I use a ringbuffer (which, to my understanding is
> lock-free and doesn't need mutexes) to get the work done, and if so,
> are there any jack2 example of this

A ringbuffer is a good way to do this.

I don't know of any examples using the JACK API (maybe
someone else does), but google for "producer consumer"
and you'll get some good info on the subject.

A note about using Qt in JACK applications: QString is
not good to use in realtime sections.  It does a lot of
dynamic memory allocation behind the scenes.  I know
for sure that its constructors and its assignment
operators allocate memory dynamically -- which means
you can't create a new QString or even copy one while
you're in a realtime section of code.

Hope this helps,
Gabriel



More information about the Linux-audio-dev mailing list