Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 342973) +++ channels/chan_sip.c (working copy) @@ -4550,16 +4550,24 @@ static void sip_destroy_peer(struct sip_peer *peer) { ast_debug(3, "Destroying SIP peer %s\n", peer->name); - if (peer->outboundproxy) + + /* + * Remove any mailbox event subscriptions for this peer before + * we destroy anything. An event subscription callback may be + * happening right now. + */ + clear_peer_mailboxes(peer); + + if (peer->outboundproxy) { ao2_ref(peer->outboundproxy, -1); - peer->outboundproxy = NULL; + peer->outboundproxy = NULL; + } /* Delete it, it needs to disappear */ if (peer->call) { dialog_unlink_all(peer->call); peer->call = dialog_unref(peer->call, "peer->call is being unset"); } - if (peer->mwipvt) { /* We have an active subscription, delete it */ dialog_unlink_all(peer->mwipvt); @@ -4587,7 +4595,6 @@ } if (peer->dnsmgr) ast_dnsmgr_release(peer->dnsmgr); - clear_peer_mailboxes(peer); if (peer->socket.tcptls_session) { ao2_ref(peer->socket.tcptls_session, -1); @@ -16518,7 +16525,6 @@ * on _every_ dialog after processing _every_ incoming SIP/UDP packet, or * potentially even more often when the scheduler has entries to run. */ - static int dialog_needdestroy(void *dialogobj, void *arg, int flags) { struct sip_pvt *dialog = dialogobj; @@ -16564,10 +16570,9 @@ } sip_pvt_unlock(dialog); - /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */ - /* the CMP_MATCH will unlink this dialog from the dialog hash table */ - dialog_unlink_all(dialog); - return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */ + + /* This dialog needs to be destroyed. */ + return CMP_MATCH; } sip_pvt_unlock(dialog); @@ -25230,8 +25235,10 @@ static void *do_monitor(void *data) { int res; + int reloading; time_t t; - int reloading; + struct ao2_iterator *iter; + struct sip_pvt *dialog; /* Add an I/O event to our SIP UDP socket */ if (sipsock > -1) @@ -25266,13 +25273,21 @@ of time since the last time we did it (when MWI is being sent, we can get back to this point every millisecond or less) */ - ao2_t_callback(dialogs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy, &t, - "callback to remove dialogs w/needdestroy"); + /* + * We cannot hold the dialogs container lock when we destroy a + * dialog because of potential deadlocks. Instead we return a + * temporary container iterator of dialogs needing destruction. + */ + iter = ao2_t_callback(dialogs, OBJ_MULTIPLE, dialog_needdestroy, &t, + "callback to remove dialogs w/needdestroy"); + if (iter) { + for (; (dialog = ao2_iterator_next(iter)); ao2_ref(dialog, -1)) { + /* Now destroy the found dialogs that need to be destroyed. */ + dialog_unlink_all(dialog); + } + ao2_iterator_destroy(iter); + } - /* the old methodology would be to restart the search for dialogs to delete with every - dialog that was found and destroyed, probably because the list contents would change, - so we'd need to restart. This isn't the best thing to do with callbacks. */ - /* XXX TODO The scheduler usage in this module does not have sufficient * synchronization being done between running the scheduler and places * scheduling tasks. As it is written, any scheduled item may not run