Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 343441)
+++ channels/chan_sip.c	(working copy)
@@ -1073,6 +1073,12 @@
 	}
 }
 
+/*!
+ * \details
+ * This container holds the dialogs that will be destroyed immediately
+ */
+struct ao2_container *dialogs_needdestroy;
+
 /*! \brief
  * Here we implement the container for dialogs (sip_pvt), defining
  * generic wrapper functions to ease the transition from the current
@@ -2924,6 +2930,13 @@
 	}
 }
 
+/*!
+ * \brief Unlink a dialog from the dialogs container, as well as any other places
+ * that it may be currently stored.
+ *
+ * \note A reference to the dialog must be held before calling this function, and this
+ * function does not release that reference
+ */
 void dialog_unlink_all(struct sip_pvt *dialog)
 {
 	struct sip_pkt *cp;
@@ -4550,9 +4563,13 @@
 static void sip_destroy_peer(struct sip_peer *peer)
 {
 	ast_debug(3, "Destroying SIP peer %s\n", peer->name);
-	if (peer->outboundproxy)
+
+	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) {
@@ -5665,7 +5682,8 @@
 {
 	if (mwi->call) {
 		mwi->call->mwi = NULL;
-		sip_destroy(mwi->call);
+		dialog_unlink_all(mwi->call);
+		mwi->call = dialog_unref(mwi->call, "unref dialog after unlink_all");
 	}
 	
 	AST_SCHED_DEL(sched, mwi->resub);
@@ -12141,7 +12159,11 @@
 	ast_sip_ouraddrfor(&mwi->call->sa, &mwi->call->ourip, mwi->call);
 	build_contact(mwi->call);
 	build_via(mwi->call);
+
+	ao2_t_unlink(dialogs, mwi->call, "Removing dialog from container before changing caller id");
 	build_callid_pvt(mwi->call);
+	ao2_t_link(dialogs, mwi->call, "Adding dialog back to container after changing caller id");
+
 	ast_set_flag(&mwi->call->flags[0], SIP_OUTGOING);
 	
 	/* Associate the call with us */
@@ -16578,9 +16600,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);
+
+		/* This dialog needs to be destroyed */
+		ao2_t_link(dialogs_needdestroy, dialog, "Link dialog for destruction");
 		return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */
 	}
 
@@ -25116,15 +25138,18 @@
 		peer_mailboxes_to_str(&mailbox_str, peer);
 		ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
 	}
+
 	ao2_lock(peer);
-
+	ao2_ref(peer, +1);
 	if (peer->mwipvt) {
 		/* Base message on subscription */
 		p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt-- should this be done?");
 	} else {
+		ao2_unlock(peer);
+
 		/* Build temporary dialog for this message */
 		if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) {
-			ao2_unlock(peer);
+			ao2_ref(peer, -1);
 			return -1;
 		}
 
@@ -25133,12 +25158,14 @@
 		 * the peer's socket information to the sip_pvt we just allocated
 		 */
 		set_socket_transport(&p->socket, 0);
+		ao2_lock(peer);
 		if (create_addr_from_peer(p, peer)) {
 			/* Maybe they're not registered, etc. */
 			dialog_unlink_all(p);
 			dialog_unref(p, "unref dialog p just created via sip_alloc");
 			/* sip_destroy(p); */
 			ao2_unlock(peer);
+			ao2_ref(peer, -1);
 			return 0;
 		}
 		/* Recalculate our side, and recalculate Call ID */
@@ -25158,15 +25185,21 @@
 
 	/* We have multiple threads (mwi events and monitor retransmits) working with this PVT and as we modify the sip history if that's turned on,
 	   we really need to have a lock on it */
+	ao2_unlock(peer);
+
 	sip_pvt_lock(p);
+	ao2_lock(peer);
 
 	/* Send MWI */
 	ast_set_flag(&p->flags[0], SIP_OUTGOING);
 	/* the following will decrement the refcount on p as it finishes */
 	transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
+
+	ao2_unlock(peer);
 	sip_pvt_unlock(p);
+
+	ao2_ref(peer, -1);
 	dialog_unref(p, "unref dialog ptr p just before it goes out of scope at the end of sip_send_mwi_to_peer.");
-	ao2_unlock(peer);
 	return 0;
 }
 
@@ -25245,6 +25278,8 @@
 	int res;
 	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)
@@ -25279,8 +25314,18 @@
 		   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,
+		/* We cannot hold the dialogs container lock when we destroy a
+		 * dialog because of potential deadlocks.  Instead we link the
+		 * doomed dialog into dialogs_needdestroy and then iterate over
+		 * that container destroying the dialogs
+		 */
+		ao2_t_callback(dialogs, OBJ_NODATA, dialog_needdestroy, &t,
 				"callback to remove dialogs w/needdestroy");
+		iter = ao2_iterator_init(dialogs_needdestroy, AO2_ITERATOR_UNLINK);
+		for (; (dialog = ao2_iterator_next(&iter)); ao2_ref(dialog, -1)) {
+			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,
@@ -29670,8 +29715,9 @@
 	peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers");
 	peers_by_ip = ao2_t_container_alloc(HASH_PEER_SIZE, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip");
 	dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
+	dialogs_needdestroy = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs_needdestroy");
 	threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table");
-	if (!peers || !peers_by_ip || !dialogs || !threadt) {
+	if (!peers || !peers_by_ip || !dialogs || !dialogs_needdestroy || !threadt) {
 		ast_log(LOG_ERROR, "Unable to create primary SIP container(s)\n");
 		return AST_MODULE_LOAD_FAILURE;
 	}
@@ -29929,6 +29975,7 @@
 	ao2_t_ref(peers, -1, "unref the peers table");
 	ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
 	ao2_t_ref(dialogs, -1, "unref the dialogs table");
+	ao2_t_ref(dialogs_needdestroy, -1, "unref the dialogs_needdestroy table");
 	ao2_t_ref(threadt, -1, "unref the thread table");
 	ao2_t_ref(sip_monitor_instances, -1, "unref the sip_monitor_instances table");
 
