[asterisk-dev] A general locking strategy in Asterisk - completely missing

Hans Petter Selasky hselasky at c2i.net
Sun Feb 11 16:26:10 MST 2007


Hi,

I'm debugging a deadlock in Asterisk 1.2.13 for someone and I am a little 
frustrated that there are no clear locking guidelines in Asterisk. In my 
channel driver I have tried to make a list of the locking order, at least:

 *
 * 1. cc_mutex_lock(&do_periodic_lock); (private lock)
 *
 * 2. cc_mutex_lock(&modlock); (See Asterisk source code)
 *
 * 3. cc_mutex_lock(&chlock); (See Asterisk source code)
 *
 * 4. cc_mutex_lock(&cd->pbx_chan->lock); (struct ast_channel *pbx_chan)
 *
 * 5. cc_mutex_lock(&p_app->lock); (my private channel driver lock)
 *
 * 6. cc_mutex_lock(&capi_global_lock); (my global lock)
 *
 * 7. cc_mutex_lock(&capi_verbose_lock); (my verbose lock)

My conclution is that Asterisk is a single locked system by design.

For example when I am at level 5) I cannot call:

ast_update_use_count();
ast_channel_free();
ast_hangup();

And many more, simply because these functions lock &modlock or &chlock, which 
are mutexes at a higher level. Of course this is no problem if these locks 
are already locked, but if the CPU jumps in at level 5, and I want to hangup 
a channel atomically, then I cannot do that, because I have to unlock the 
mutex at level 5, and lock the mutex at level 3 first. This breaks atomicity. 
So in other words, to allow me to do what I want to do, I need to lock mutex 
2 & 3 & 4 first, before I lock mutex 5. Asterisk is a single locked system in 
other words, hence I always have to lock mutex 2 no matter what I do, to 
ensure 100% atomicity.

Here is one root of the problem, see "channel_find_locked()" in the Asterisk 
sources:

lock(&chlock);
lock(&pbx_chan->lock);
unlock(&chlock);
  do something();
unlock(&pbx_chan->lock);

It locks "chlock" when it traverses the channel-list. Then it locks the 
channel's lock. Then it unlocks the "chlock". This is headless programming.

The solution is this (so that &chlock gets as far down as possible in the 
locking hiherarchy):

lock(&chlock);
unlock(&chlock);

lock(&pbx_chan->lock);
  do something();
unlock(&pbx_chan->lock);

Of course this has a problem: The channel might be freed in the unlocked gap. 
But hey. There is something called refcounts. You need to keep refcounts:

lock(&chlock);
pbx_chan->refcount ++;
unlock(&chlock);

lock(&pbx_chan->lock);
  do something();
unlock(&pbx_chan->lock);

lock(&chlock);
pbx_chan->refcount --;
if (pbx_chan->refcount == 0) {
	free(pbx_chan);
}
unlock(&chlock);

--HPS

RULE #1: The Gianter mutex should be locked last.


More information about the asterisk-dev mailing list