--- asterisk-1.6.2.14.orig/channels/chan_sip.c +++ asterisk-1.6.2.14/channels/chan_sip.c @@ -12978,7 +12978,15 @@ static int cb_extensionstate(char *conte { struct sip_pvt *p = data; - sip_pvt_lock(p); + if( sip_pvt_trylock(p) ) { + /* This can deadlock when handle_statechange is active and we + * also use ast_get_hint with the pvt locked, because lock ordering + * differs, but cannot be changed. + * + * This error return gets handle_statechange to back off and try + * again. */ + return -1; + } switch(state) { case AST_EXTENSION_DEACTIVATED: /* Retry after a while */ --- asterisk-1.6.2.14.orig/main/pbx.c +++ asterisk-1.6.2.14/main/pbx.c @@ -3918,12 +3918,30 @@ static int handle_statechange(void *data /* For general callbacks */ AST_LIST_TRAVERSE(&statecbs, cblist, entry) { - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + while( cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data) == -1 ) { + /* The callback failed to get a lock, unlock (but keep ref on hint[s]), sleep and retry */ + ao2_unlock(hint); + ao2_unlock(hints); + ast_unlock_contexts(); + usleep(1); + ast_rdlock_contexts(); + ao2_lock(hints); + ao2_lock(hint); + } } /* For extension callbacks */ AST_LIST_TRAVERSE(&hint->callbacks, cblist, entry) { - cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + while( cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data) == -1 ) { + /* The callback failed to get a lock, unlock (but keep ref on hint[s]), sleep and retry */ + ao2_unlock(hint); + ao2_unlock(hints); + ast_unlock_contexts(); + usleep(1); + ast_rdlock_contexts(); + ao2_lock(hints); + ao2_lock(hint); + } } hint->laststate = state; /* record we saw the change */