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

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


Kevin Harwell has uploaded a new change for review.

  https://gerrit.asterisk.org/1857

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, 27 insertions(+), 6 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/57/1857/1

diff --git a/main/pbx.c b/main/pbx.c
index 202940e..de0fac9 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1496,6 +1496,11 @@
 AST_MUTEX_DEFINE_STATIC(context_merge_lock);
 
 /*!
+ * \brief Lock used to protect state data during callbacks
+ */
+AST_MUTEX_DEFINE_STATIC(state_callbacks_lock);
+
+/*!
  * \brief Registered applications container.
  *
  * It is sorted by application name.
@@ -5442,12 +5447,14 @@
 	strcpy(cmpdevice->hintdevice, dev_state->device);
 
 	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_SEARCH_OBJECT | 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);
 		return;
@@ -5543,6 +5550,7 @@
 
 		ao2_cleanup(device_state_info);
 	}
+	ast_mutex_unlock(&state_callbacks_lock);
 	ast_mutex_unlock(&context_merge_lock);
 
 	ao2_iterator_destroy(dev_iter);
@@ -5946,7 +5954,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. */
 
@@ -5957,7 +5972,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;
 	}
@@ -5998,9 +6014,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 */
@@ -6083,7 +6100,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);
@@ -12111,12 +12129,14 @@
 	strcpy(cmpdevice->hintdevice, presence_state->provider);
 
 	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);
 		return;
@@ -12193,6 +12213,7 @@
 		ao2_iterator_destroy(&cb_iter);
 	}
 	ao2_iterator_destroy(dev_iter);
+	ast_mutex_unlock(&state_callbacks_lock);
 	ast_mutex_unlock(&context_merge_lock);
 
 	ast_free(hint_app);

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

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



More information about the asterisk-code-review mailing list