[Asterisk-code-review] pbx: Deadlock between contexts container and context merge l... (asterisk[11])

Kevin Harwell asteriskteam at digium.com
Thu Dec 24 14:54:08 CST 2015


Kevin Harwell has uploaded a new change for review.

  https://gerrit.asterisk.org/1855

Change subject: pbx: Deadlock between contexts container and context_merge locks
......................................................................

pbx: Deadlock between contexts container and context_merge locks

Recent changes (ASTERISK-25394 commit 2bd27d12223fe33b58c453965ed5c6ed3af7c4f5)
introduced the possibility of a deadlock. Due to the mentioned modifications
ast_change_hints now needs to keep both merge/delete and state callbacks from
occurring while it executes. Unfortunately, sometimes ast_change_hints can be
called with the contexts container locked. When this happens it's possible for
another thread to grab the context_merge_lock before the thread calling into
ast_change_hints does and then try to obtain the contexts container lock. This
of course causes a deadlock between the two threads. The thread calling into
ast_change_hints waits for the other thread to release context_merge_lock and
the other thread is waiting on that one to release the contexts container lock.

Unfortunately, there is not a great way to fix this problem. When hints change
the merge/delete cannot run, nor the state callbacks. Picking the least of
evils this patch adds yet another lock: state_callbacks_lock. This lock is held
during state callbacks and ast_change_hints. In order to keep the merge/delete
code from executing at the same time ast_change_hints locks the contexts
container as well. The context_merge_lock no longer needs to be held during
ast_change_hints, thus relieving the possible tension between threads.

ASTERISK-25640 #close
Reported by: Krzysztof Trempala

Change-Id: I7f67ce4b4c94b731c23932cc2ba9e0a4a720813d
---
M main/pbx.c
1 file changed, 26 insertions(+), 6 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/55/1855/1

diff --git a/main/pbx.c b/main/pbx.c
index 0165c88..0b48f99 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1450,6 +1450,11 @@
  */
 AST_MUTEX_DEFINE_STATIC(context_merge_lock);
 
+/*!
+ * \brief Lock used to protect state data during callbacks
+ */
+AST_MUTEX_DEFINE_STATIC(state_callbacks_lock);
+
 static AST_RWLIST_HEAD_STATIC(apps, ast_app);
 
 static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
@@ -5317,6 +5322,7 @@
 	}
 
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+	ast_mutex_lock(&state_callbacks_lock);
 	i = ao2_iterator_init(hints, 0);
 	for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
 		struct ast_state_cb *state_cb;
@@ -5408,6 +5414,7 @@
 		ao2_iterator_destroy(&cb_iter);
 	}
 	ao2_iterator_destroy(&i);
+	ast_mutex_unlock(&state_callbacks_lock);
 	ast_mutex_unlock(&context_merge_lock);
 
 	res = 0;
@@ -5528,12 +5535,14 @@
 	strcpy(cmpdevice->hintdevice, sc->dev);
 
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+	ast_mutex_lock(&state_callbacks_lock);
 	dev_iter = ao2_t_callback(hintdevices,
 		OBJ_POINTER | OBJ_MULTIPLE,
 		hintdevice_cmp_multiple,
 		cmpdevice,
 		"find devices in container");
 	if (!dev_iter) {
+		ast_mutex_unlock(&state_callbacks_lock);
 		ast_mutex_unlock(&context_merge_lock);
 		ast_free(hint_app);
 		ast_free(sc);
@@ -5630,6 +5639,7 @@
 
 		ao2_cleanup(device_state_info);
 	}
+	ast_mutex_unlock(&state_callbacks_lock);
 	ast_mutex_unlock(&context_merge_lock);
 
 	ao2_iterator_destroy(dev_iter);
@@ -6013,7 +6023,14 @@
 		return -1;
 	}
 
-	ast_mutex_lock(&context_merge_lock); /* Hold off ast_merge_contexts_and_delete and state changes */
+	/* Lock state callbacks so hints can be manipulated */
+	ast_mutex_lock(&state_callbacks_lock);
+
+	/*
+	 * Hold off ast_merge_contexts_and_delete, but do it by holding the contexts lock.
+	 * If the context_merge_lock is held there is the potential for a deadlock.
+	 */
+	ast_rdlock_contexts();
 
 	ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
 
@@ -6024,7 +6041,8 @@
 	hint = ao2_find(hints, oe, OBJ_UNLINK);
 	if (!hint) {
 		ao2_unlock(hints);
-		ast_mutex_unlock(&context_merge_lock);
+		ast_unlock_contexts();
+		ast_mutex_unlock(&state_callbacks_lock);
 		ast_free(hint_app);
 		return -1;
 	}
@@ -6065,9 +6083,10 @@
 
 	ao2_unlock(hints);
 
-	/* Locking for state callbacks is respected here and only the context_merge_lock lock is
-	 * held during the state callback invocation. This will stop the normal state callback
-	 * thread from being able to handle incoming state changes if they occur.
+	/* Locking for state callbacks is respected here and only the context_merge_lock and
+	 * state_callbacks_lock are held during the state callback invocation. This will stop
+	 * the normal state callback thread from being able to handle incoming state changes
+	 * if they occur.
 	 */
 
 	/* Determine if presence state has changed due to the change of the hint extension */
@@ -6150,7 +6169,8 @@
 
 	ao2_ref(hint, -1);
 
-	ast_mutex_unlock(&context_merge_lock);
+	ast_unlock_contexts();
+	ast_mutex_unlock(&state_callbacks_lock);
 
 	ast_free(hint_app);
 	ast_free(previous_message);

-- 
To view, visit https://gerrit.asterisk.org/1855
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7f67ce4b4c94b731c23932cc2ba9e0a4a720813d
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: 11
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>



More information about the asterisk-code-review mailing list