Index: main/pbx.c =================================================================== --- main/pbx.c (revision 344043) +++ main/pbx.c (working copy) @@ -7100,7 +7100,7 @@ char data[1]; }; -AST_LIST_HEAD(store_hints, store_hint); +AST_LIST_HEAD_NOLOCK(store_hints, store_hint); static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar) { @@ -7225,7 +7225,8 @@ struct ast_context *tmp; struct ast_context *oldcontextslist; struct ast_hashtab *oldtable; - struct store_hints store = AST_LIST_HEAD_INIT_VALUE; + struct store_hints hints_stored = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + struct store_hints hints_removed = AST_LIST_HEAD_NOLOCK_INIT_VALUE; struct store_hint *saved_hint; struct ast_hint *hint; struct ast_exten *exten; @@ -7295,7 +7296,7 @@ saved_hint->exten = saved_hint->data + strlen(saved_hint->context) + 1; strcpy(saved_hint->exten, hint->exten->exten); ao2_unlock(hint); - AST_LIST_INSERT_HEAD(&store, saved_hint, list); + AST_LIST_INSERT_HEAD(&hints_stored, saved_hint, list); } } @@ -7311,7 +7312,7 @@ * Restore the watchers for hints that can be found; notify * those that cannot be restored. */ - while ((saved_hint = AST_LIST_REMOVE_HEAD(&store, list))) { + while ((saved_hint = AST_LIST_REMOVE_HEAD(&hints_stored, list))) { struct pbx_find_info q = { .stacklen = 0 }; exten = pbx_find_extension(NULL, NULL, &q, saved_hint->context, saved_hint->exten, @@ -7333,13 +7334,11 @@ /* Find the hint in the hints container */ hint = exten ? ao2_find(hints, exten, 0) : NULL; if (!hint) { - /* this hint has been removed, notify the watchers */ - while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) { - thiscb->callback(saved_hint->context, saved_hint->exten, - AST_EXTENSION_REMOVED, thiscb->data); - /* Ref that we added when putting into saved_hint->callbacks */ - ao2_ref(thiscb, -1); - } + /* + * Notify watchers of this removed hint later when we aren't + * encumberd by so many locks. + */ + AST_LIST_INSERT_HEAD(&hints_removed, saved_hint, list); } else { ao2_lock(hint); while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) { @@ -7350,12 +7349,28 @@ hint->laststate = saved_hint->laststate; ao2_unlock(hint); ao2_ref(hint, -1); + ast_free(saved_hint); } - ast_free(saved_hint); } ao2_unlock(hints); ast_unlock_contexts(); + + /* + * Notify watchers of all removed hints with the same lock + * environment as handle_statechange(). + */ + while ((saved_hint = AST_LIST_REMOVE_HEAD(&hints_removed, list))) { + /* this hint has been removed, notify the watchers */ + while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) { + thiscb->callback(saved_hint->context, saved_hint->exten, + AST_EXTENSION_REMOVED, thiscb->data); + /* Ref that we added when putting into saved_hint->callbacks */ + ao2_ref(thiscb, -1); + } + ast_free(saved_hint); + } + ast_mutex_unlock(&context_merge_lock); endlocktime = ast_tvnow();