[asterisk-commits] irroot: branch irroot/distrotech-customers-1.8 r328931 - /team/irroot/distrot...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jul 20 07:33:18 CDT 2011


Author: irroot
Date: Wed Jul 20 07:33:13 2011
New Revision: 328931

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=328931
Log:
Deadlocks dealing with dialplan hints during reload.
https://reviewboard.asterisk.org/r/1313/
Merging diff for testing

Modified:
    team/irroot/distrotech-customers-1.8/main/pbx.c

Modified: team/irroot/distrotech-customers-1.8/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/main/pbx.c?view=diff&rev=328931&r1=328930&r2=328931
==============================================================================
--- team/irroot/distrotech-customers-1.8/main/pbx.c (original)
+++ team/irroot/distrotech-customers-1.8/main/pbx.c Wed Jul 20 07:33:13 2011
@@ -934,9 +934,17 @@
  * See \ref AstExtState
  */
 struct ast_hint {
-	struct ast_exten *exten;	/*!< Extension */
+	/*!
+	 * \brief Hint extension
+	 *
+	 * \note
+	 * Will never be NULL while the hint is in the hints container.
+	 */
+	struct ast_exten *exten;
+	struct ao2_container *callbacks; /*!< Callback container for this extension */
 	int laststate;			/*!< Last known state */
-	struct ao2_container *callbacks; /*!< Callback container for this extension */
+	char context_name[AST_MAX_CONTEXT];/*!< Context of destroyed hint extension. */
+	char exten_name[AST_MAX_EXTENSION];/*!< Extension of destroyed hint extension. */
 };
 
 /* --- Hash tables of various objects --------*/
@@ -1181,6 +1189,11 @@
  * https://issues.asterisk.org/view.php?id=17643
  */
 AST_MUTEX_DEFINE_STATIC(conlock);
+
+/*!
+ * \brief Lock to hold off restructuring of hints by ast_merge_contexts_and_delete.
+ */
+AST_MUTEX_DEFINE_STATIC(context_merge_lock);
 
 static AST_RWLIST_HEAD_STATIC(apps, ast_app);
 
@@ -4190,27 +4203,34 @@
 	return AST_EXTENSION_NOT_INUSE;
 }
 
+static int ast_extension_state3(struct ast_str *hint_app)
+{
+	char *cur;
+	char *rest;
+	struct ast_devstate_aggregate agg;
+
+	/* One or more devices separated with a & character */
+	rest = ast_str_buffer(hint_app);
+
+	ast_devstate_aggregate_init(&agg);
+	while ((cur = strsep(&rest, "&"))) {
+		ast_devstate_aggregate_add(&agg, ast_device_state(cur));
+	}
+
+	return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
+}
+
 /*! \brief Check state of extension by using hints */
 static int ast_extension_state2(struct ast_exten *e)
 {
-	struct ast_str *hint = ast_str_thread_get(&extensionstate_buf, 16);
-	char *cur, *rest;
-	struct ast_devstate_aggregate agg;
-
-	if (!e)
+	struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+
+	if (!e || !hint_app) {
 		return -1;
-
-	ast_devstate_aggregate_init(&agg);
-
-	ast_str_set(&hint, 0, "%s", ast_get_extension_app(e));
-
-	rest = ast_str_buffer(hint);	/* One or more devices separated with a & character */
-
-	while ( (cur = strsep(&rest, "&")) ) {
-		ast_devstate_aggregate_add(&agg, ast_device_state(cur));
-	}
-
-	return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
+	}
+
+	ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
+	return ast_extension_state3(hint_app);
 }
 
 /*! \brief Return extension_state as string */
@@ -4251,71 +4271,93 @@
 static int handle_statechange(void *datap)
 {
 	struct ast_hint *hint;
-	struct ast_str *str;
+	struct ast_str *hint_app;
 	struct statechange *sc = datap;
 	struct ao2_iterator i;
 	struct ao2_iterator cb_iter;
-
-	if (!(str = ast_str_create(1024))) {
+	char context_name[AST_MAX_CONTEXT];
+	char exten_name[AST_MAX_EXTENSION];
+
+	hint_app = ast_str_create(1024);
+	if (!hint_app) {
 		return -1;
 	}
 
+	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
 	i = ao2_iterator_init(hints, 0);
 	for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
 		struct ast_state_cb *state_cb;
 		char *cur, *parse;
 		int state;
 
-		ast_str_set(&str, 0, "%s", ast_get_extension_app(hint->exten));
-		parse = str->str;
-
-		while ( (cur = strsep(&parse, "&")) ) {
+		ao2_lock(hint);
+		if (!hint->exten) {
+			/* The extension has already been destroyed */
+			ao2_unlock(hint);
+			continue;
+		}
+
+		/* Does this hint monitor the device that changed state? */
+		ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+		parse = hint_app->str;
+		while ((cur = strsep(&parse, "&"))) {
 			if (!strcasecmp(cur, sc->dev)) {
+				/* The hint monitors the device. */
 				break;
 			}
 		}
 		if (!cur) {
+			/* The hint does not monitor the device. */
+			ao2_unlock(hint);
 			continue;
 		}
 
-		/* Get device state for this hint */
-		state = ast_extension_state2(hint->exten);
-		if ((state == -1) || (state == hint->laststate)) {
+		/*
+		 * Save off strings in case the hint extension gets destroyed
+		 * while we are notifying the watchers.
+		 */
+		ast_copy_string(context_name,
+			ast_get_context_name(ast_get_extension_context(hint->exten)),
+			sizeof(context_name));
+		ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+			sizeof(exten_name));
+
+		ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+		ao2_unlock(hint);
+
+		/*
+		 * Get device state for this hint.
+		 *
+		 * NOTE: We cannot hold any locks while determining the hint
+		 * device state or notifying the watchers without causing a
+		 * deadlock.  (conlock, hints, and hint)
+		 */
+		state = ast_extension_state3(hint_app);
+		if (state == hint->laststate) {
 			continue;
 		}
 
-		/* Device state changed since last check - notify the watchers */
-
-		ao2_lock(hints);
-		ao2_lock(hint);
-
-		if (hint->exten == NULL) {
-			/* the extension has been destroyed */
-			ao2_unlock(hint);
-			ao2_unlock(hints);
-			continue;
-		}
+		/* Device state changed since last check - notify the watchers. */
+		hint->laststate = state;	/* record we saw the change */
 
 		/* For general callbacks */
 		cb_iter = ao2_iterator_init(statecbs, 0);
 		for (state_cb = ao2_iterator_next(&cb_iter); state_cb; ao2_ref(state_cb, -1), state_cb = ao2_iterator_next(&cb_iter)) {
-			state_cb->callback(hint->exten->parent->name, hint->exten->exten, state, state_cb->data);
+			state_cb->callback(context_name, exten_name, state, state_cb->data);
 		}
 		ao2_iterator_destroy(&cb_iter);
 
 		/* For extension callbacks */
 		cb_iter = ao2_iterator_init(hint->callbacks, 0);
 		for (state_cb = ao2_iterator_next(&cb_iter); state_cb; ao2_ref(state_cb, -1), state_cb = ao2_iterator_next(&cb_iter)) {
-			state_cb->callback(hint->exten->parent->name, hint->exten->exten, state, state_cb->data);
+			state_cb->callback(context_name, exten_name, state, state_cb->data);
 		}
 		ao2_iterator_destroy(&cb_iter);
-
-		hint->laststate = state;	/* record we saw the change */
-		ao2_unlock(hint);
-		ao2_unlock(hints);
 	}
 	ao2_iterator_destroy(&i);
-	ast_free(str);
+	ast_mutex_unlock(&context_merge_lock);
+
+	ast_free(hint_app);
 	ast_free(sc);
 	return 0;
 }
@@ -4327,32 +4369,33 @@
 	struct ast_hint *hint;
 	struct ast_state_cb *state_cb;
 	struct ast_exten *e;
+	int id;
 
 	/* If there's no context and extension:  add callback to statecbs list */
 	if (!context && !exten) {
-		ao2_lock(hints);
+		/* Prevent multiple adds from adding the same callback at the same time. */
+		ao2_lock(statecbs);
 
 		state_cb = ao2_find(statecbs, callback, 0);
 		if (state_cb) {
 			state_cb->data = data;
 			ao2_ref(state_cb, -1);
-			ao2_unlock(hints);
+			ao2_unlock(statecbs);
 			return 0;
 		}
 
 		/* Now insert the callback */
 		if (!(state_cb = ao2_alloc(sizeof(*state_cb), NULL))) {
-			ao2_unlock(hints);
+			ao2_unlock(statecbs);
 			return -1;
 		}
 		state_cb->id = 0;
 		state_cb->callback = callback;
 		state_cb->data = data;
-
 		ao2_link(statecbs, state_cb);
+
 		ao2_ref(state_cb, -1);
-
-		ao2_unlock(hints);
+		ao2_unlock(statecbs);
 		return 0;
 	}
 
@@ -4379,31 +4422,31 @@
 		}
 	}
 
-	/* Find the hint in the list of hints */
+	/* Find the hint in the hints container */
+	ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
 	hint = ao2_find(hints, e, 0);
-
 	if (!hint) {
+		ao2_unlock(hints);
 		return -1;
 	}
 
 	/* Now insert the callback in the callback list  */
 	if (!(state_cb = ao2_alloc(sizeof(*state_cb), NULL))) {
 		ao2_ref(hint, -1);
+		ao2_unlock(hints);
 		return -1;
 	}
-
-	state_cb->id = stateid++;		/* Unique ID for this callback */
+	id = stateid++;		/* Unique ID for this callback */
+	state_cb->id = id;
 	state_cb->callback = callback;	/* Pointer to callback routine */
 	state_cb->data = data;		/* Data for the callback */
-
-	ao2_lock(hint);
 	ao2_link(hint->callbacks, state_cb);
+
 	ao2_ref(state_cb, -1);
-	ao2_unlock(hint);
-
 	ao2_ref(hint, -1);
-
-	return state_cb->id;
+	ao2_unlock(hints);
+
+	return id;
 }
 
 /*! \brief Remove a watcher from the callback list */
@@ -4427,35 +4470,29 @@
 	struct ast_state_cb *p_cur = NULL;
 	int ret = -1;
 
-	if (!id && !callback) {
-		return -1;
-	}
-
 	if (!id) {	/* id == 0 is a callback without extension */
-		ao2_lock(hints);
+		if (!callback) {
+			return ret;
+		}
 		p_cur = ao2_find(statecbs, callback, OBJ_UNLINK);
 		if (p_cur) {
 			ret = 0;
 			ao2_ref(p_cur, -1);
 		}
-		ao2_unlock(hints);
 	} else { /* callback with extension, find the callback based on ID */
 		struct ast_hint *hint;
 
-		ao2_lock(hints);
+		ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
 		hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
-		ao2_unlock(hints);
-
 		if (hint) {
-			ao2_lock(hint);
 			p_cur = ao2_find(hint->callbacks, &id, OBJ_UNLINK);
 			if (p_cur) {
 				ret = 0;
 				ao2_ref(p_cur, -1);
 			}
-			ao2_unlock(hint);
 			ao2_ref(hint, -1);
 		}
+		ao2_unlock(hints);
 	}
 
 	return ret;
@@ -4470,39 +4507,126 @@
 	return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
 }
 
+/*!
+ * \internal
+ * \brief Destroy the given hint object.
+ *
+ * \param obj Hint to destroy.
+ *
+ * \return Nothing
+ */
+static void destroy_hint(void *obj)
+{
+	struct ast_hint *hint = obj;
+
+	if (hint->callbacks) {
+		struct ast_state_cb *state_cb;
+		const char *context_name;
+		const char *exten_name;
+
+		if (hint->exten) {
+			context_name = ast_get_context_name(ast_get_extension_context(hint->exten));
+			exten_name = ast_get_extension_name(hint->exten);
+			hint->exten = NULL;
+		} else {
+			/* The extension has already been destroyed */
+			context_name = hint->context_name;
+			exten_name = hint->exten_name;
+		}
+		while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
+			/* Notify with -1 and remove all callbacks */
+			/* NOTE: The casts will not be needed for v1.10 and later */
+			state_cb->callback((char *) context_name, (char *) exten_name,
+				AST_EXTENSION_DEACTIVATED, state_cb->data);
+			ao2_ref(state_cb, -1);
+		}
+		ao2_ref(hint->callbacks, -1);
+	}
+}
+
+/*! \brief Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+	/* Cleanup the Notifys if hint is removed */
+	struct ast_hint *hint;
+
+	if (!e) {
+		return -1;
+	}
+
+	hint = ao2_find(hints, e, OBJ_UNLINK);
+	if (!hint) {
+		return -1;
+	}
+
+	/*
+	 * The extension is being destroyed so we must save some
+	 * information to notify that the extension is deactivated.
+	 */
+	ao2_lock(hint);
+	ast_copy_string(hint->context_name,
+		ast_get_context_name(ast_get_extension_context(hint->exten)),
+		sizeof(hint->context_name));
+	ast_copy_string(hint->exten_name, ast_get_extension_name(hint->exten),
+		sizeof(hint->exten_name));
+	hint->exten = NULL;
+	ao2_unlock(hint);
+
+	ao2_ref(hint, -1);
+
+	return 0;
+}
+
 /*! \brief Add hint to hint list, check initial extension state */
 static int ast_add_hint(struct ast_exten *e)
 {
-	struct ast_hint *hint;
+	struct ast_hint *hint_new;
+	struct ast_hint *hint_found;
 
 	if (!e) {
 		return -1;
 	}
 
+	/*
+	 * We must create the hint we wish to add before determining if
+	 * it is already in the hints container to avoid possible
+	 * deadlock when getting the current extension state.
+	 */
+	hint_new = ao2_alloc(sizeof(*hint_new), destroy_hint);
+	if (!hint_new) {
+		return -1;
+	}
+
+	/* Initialize new hint. */
+	hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
+	if (!hint_new->callbacks) {
+		ao2_ref(hint_new, -1);
+		return -1;
+	}
+	hint_new->exten = e;
+	hint_new->laststate = ast_extension_state2(e);
+
+	/* Prevent multiple add hints from adding the same hint at the same time. */
+	ao2_lock(hints);
+
 	/* Search if hint exists, do nothing */	
-	hint = ao2_find(hints, e, 0);
-	if (hint) {
-		ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
-		ao2_ref(hint, -1);
+	hint_found = ao2_find(hints, e, 0);
+	if (hint_found) {
+		ao2_ref(hint_found, -1);
+		ao2_unlock(hints);
+		ao2_ref(hint_new, -1);
+		ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n",
+			ast_get_extension_name(e), ast_get_extension_app(e));
 		return -1;
 	}
 
-	ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
-
-	if (!(hint = ao2_alloc(sizeof(*hint), NULL))) {
-		return -1;
-	}
-	if (!(hint->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp))) {
-		return -1;
-	}
-
-	/* Initialize and insert new item at the top */
-	hint->exten = e;
-	hint->laststate = ast_extension_state2(e);
-
-	ao2_link(hints, hint);
-
-	ao2_ref(hint, -1);
+	/* Add new hint to the hints container */
+	ast_debug(2, "HINTS: Adding hint %s: %s\n",
+		ast_get_extension_name(e), ast_get_extension_app(e));
+	ao2_link(hints, hint_new);
+
+	ao2_unlock(hints);
+	ao2_ref(hint_new, -1);
 
 	return 0;
 }
@@ -4512,49 +4636,29 @@
 {
 	struct ast_hint *hint;
 
-	hint = ao2_find(hints, oe, 0);
-
+	if (!oe || !ne) {
+		return -1;
+	}
+
+	ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
+
+	/*
+	 * Unlink the hint from the hints container as the extension
+	 * name (which is the hash value) could change.
+	 */
+	hint = ao2_find(hints, oe, OBJ_UNLINK);
 	if (!hint) {
+		ao2_unlock(hints);
 		return -1;
 	}
 
+	/* Update the hint and put it back in the hints container. */
 	ao2_lock(hint);
 	hint->exten = ne;
 	ao2_unlock(hint);
-	ao2_ref(hint, -1);
-
-	return 0;
-}
-
-/*! \brief Remove hint from extension */
-static int ast_remove_hint(struct ast_exten *e)
-{
-	/* Cleanup the Notifys if hint is removed */
-	struct ast_hint *hint;
-	struct ast_state_cb *state_cb;
-
-	if (!e) {
-		return -1;
-	}
-
-	hint = ao2_find(hints, e, 0);
-
-	if (!hint) {
-		return -1;
-	}
-	ao2_lock(hint);
-
-	while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
-		/* Notify with -1 and remove all callbacks */
-		state_cb->callback(hint->exten->parent->name, hint->exten->exten,
-			AST_EXTENSION_DEACTIVATED, state_cb->data);
-		ao2_ref(state_cb, -1);
-	}
-
-	hint->exten = NULL;
-	ao2_unlink(hints, hint);
-	ao2_ref(hint->callbacks, -1);
-	ao2_unlock(hint);
+	ao2_link(hints, hint);
+
+	ao2_unlock(hints);
 	ao2_ref(hint, -1);
 
 	return 0;
@@ -5803,13 +5907,19 @@
 
 	i = ao2_iterator_init(hints, 0);
 	for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
-
+		ao2_lock(hint);
+		if (!hint->exten) {
+			/* The extension has already been destroyed */
+			ao2_unlock(hint);
+			continue;
+		}
 		watchers = ao2_container_count(hint->callbacks);
 		ast_cli(a->fd, "   %20s@%-20.20s: %-20.20s  State:%-15.15s Watchers %2d\n",
 			ast_get_extension_name(hint->exten),
 			ast_get_context_name(ast_get_extension_context(hint->exten)),
 			ast_get_extension_app(hint->exten),
 			ast_extension_state2str(hint->laststate), watchers);
+		ao2_unlock(hint);
 		num++;
 	}
 	ao2_iterator_destroy(&i);
@@ -5835,13 +5945,20 @@
 
 	/* walk through all hints */
 	i = ao2_iterator_init(hints, 0);
-	for (hint = ao2_iterator_next(&i); hint; ao2_unlock(hint), ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
+	for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
 		ao2_lock(hint);
-		if (!strncasecmp(word, hint->exten ? ast_get_extension_name(hint->exten) : "", wordlen) && ++which > state) {
+		if (!hint->exten) {
+			/* The extension has already been destroyed */
+			ao2_unlock(hint);
+			continue;
+		}
+		if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) {
 			ret = ast_strdup(ast_get_extension_name(hint->exten));
 			ao2_unlock(hint);
+			ao2_ref(hint, -1);
 			break;
 		}
+		ao2_unlock(hint);
 	}
 	ao2_iterator_destroy(&i);
 
@@ -5877,9 +5994,14 @@
 	
 	extenlen = strlen(a->argv[3]);
 	i = ao2_iterator_init(hints, 0);
-	for (hint = ao2_iterator_next(&i); hint; ao2_unlock(hint), ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
+	for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
 		ao2_lock(hint);
-		if (!strncasecmp(hint->exten ? ast_get_extension_name(hint->exten) : "", a->argv[3], extenlen)) {
+		if (!hint->exten) {
+			/* The extension has already been destroyed */
+			ao2_unlock(hint);
+			continue;
+		}
+		if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
 			watchers = ao2_container_count(hint->callbacks);
 			ast_cli(a->fd, "   %20s@%-20.20s: %-20.20s  State:%-15.15s Watchers %2d\n",
 				ast_get_extension_name(hint->exten),
@@ -5888,6 +6010,7 @@
 				ast_extension_state2str(hint->laststate), watchers);
 			num++;
 		}
+		ao2_unlock(hint);
 	}
 	ao2_iterator_destroy(&i);
 	if (!num)
@@ -7075,7 +7198,7 @@
 	struct ast_context *oldcontextslist;
 	struct ast_hashtab *oldtable;
 	struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
-	struct store_hint *this;
+	struct store_hint *saved_hint;
 	struct ast_hint *hint;
 	struct ast_exten *exten;
 	int length;
@@ -7100,6 +7223,7 @@
 	 */
 
 	begintime = ast_tvnow();
+	ast_mutex_lock(&context_merge_lock);/* Serialize ast_merge_contexts_and_delete */
 	ast_rdlock_contexts();
 	iter = ast_hashtab_start_traversal(contexts_table);
 	while ((tmp = ast_hashtab_next(iter))) {
@@ -7107,42 +7231,43 @@
 	}
 	ast_hashtab_end_traversal(iter);
 
-	while (ao2_trylock(hints)) {
-		ast_unlock_contexts();
-		usleep(1);
-		ast_rdlock_contexts();
-	}
+	ao2_lock(hints);
 	writelocktime = ast_tvnow();
 
 	/* preserve all watchers for hints */
 	i = ao2_iterator_init(hints, AO2_ITERATOR_DONTLOCK);
 	for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
 		if (ao2_container_count(hint->callbacks)) {
-
-			length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
-			if (!(this = ast_calloc(1, length))) {
-				continue;
-			}
 			ao2_lock(hint);
-
-			if (hint->exten == NULL) {
+			if (!hint->exten) {
+				/* The extension has already been destroyed. (Should never happen here) */
 				ao2_unlock(hint);
 				continue;
 			}
 
-			/* this removes all the callbacks from the hint into 'this'. */
+			length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2
+				+ sizeof(*saved_hint);
+			if (!(saved_hint = ast_calloc(1, length))) {
+				ao2_unlock(hint);
+				continue;
+			}
+
+			/* This removes all the callbacks from the hint into saved_hint. */
 			while ((thiscb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
-				AST_LIST_INSERT_TAIL(&this->callbacks, thiscb, entry);
-				/* We intentionally do not unref thiscb to account for the non-ao2 reference in this->callbacks */
-			}
-
-			this->laststate = hint->laststate;
-			this->context = this->data;
-			strcpy(this->data, hint->exten->parent->name);
-			this->exten = this->data + strlen(this->context) + 1;
-			strcpy(this->exten, hint->exten->exten);
+				AST_LIST_INSERT_TAIL(&saved_hint->callbacks, thiscb, entry);
+				/*
+				 * We intentionally do not unref thiscb to account for the
+				 * non-ao2 reference in saved_hint->callbacks
+				 */
+			}
+
+			saved_hint->laststate = hint->laststate;
+			saved_hint->context = saved_hint->data;
+			strcpy(saved_hint->data, hint->exten->parent->name);
+			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, this, list);
+			AST_LIST_INSERT_HEAD(&store, saved_hint, list);
 		}
 	}
 
@@ -7154,48 +7279,55 @@
 	contexts_table = exttable;
 	contexts = *extcontexts;
 
-	/* restore the watchers for hints that can be found; notify those that
-	   cannot be restored
-	*/
-	while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
+	/*
+	 * Restore the watchers for hints that can be found; notify
+	 * those that cannot be restored.
+	 */
+	while ((saved_hint = AST_LIST_REMOVE_HEAD(&store, list))) {
 		struct pbx_find_info q = { .stacklen = 0 };
-		exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
-		/* If this is a pattern, dynamically create a new extension for this
+
+		exten = pbx_find_extension(NULL, NULL, &q, saved_hint->context, saved_hint->exten,
+			PRIORITY_HINT, NULL, "", E_MATCH);
+		/*
+		 * If this is a pattern, dynamically create a new extension for this
 		 * particular match.  Note that this will only happen once for each
 		 * individual extension, because the pattern will no longer match first.
 		 */
 		if (exten && exten->exten[0] == '_') {
-			ast_add_extension_nolock(exten->parent->name, 0, this->exten, PRIORITY_HINT, NULL,
-				0, exten->app, ast_strdup(exten->data), ast_free_ptr, exten->registrar);
+			ast_add_extension_nolock(exten->parent->name, 0, saved_hint->exten,
+				PRIORITY_HINT, NULL, 0, exten->app, ast_strdup(exten->data), ast_free_ptr,
+				exten->registrar);
 			/* rwlocks are not recursive locks */
-			exten = ast_hint_extension_nolock(NULL, this->context, this->exten);
-		}
-
-		/* Find the hint in the list of hints */
-		hint = ao2_find(hints, exten, 0);
-		if (!exten || !hint) {
+			exten = ast_hint_extension_nolock(NULL, saved_hint->context,
+				saved_hint->exten);
+		}
+
+		/* Find the hint in the hints container */
+		if (!exten || !(hint = ao2_find(hints, exten, 0))) {
 			/* this hint has been removed, notify the watchers */
-			while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) {
-				thiscb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, thiscb->data);
-				ao2_ref(thiscb, -1);  /* Ref that we added when putting into this->callbacks */
+			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);
 			}
 		} else {
 			ao2_lock(hint);
-			while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) {
+			while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
 				ao2_link(hint->callbacks, thiscb);
-				ao2_ref(thiscb, -1);  /* Ref that we added when putting into this->callbacks */
-			}
-			hint->laststate = this->laststate;
+				/* Ref that we added when putting into saved_hint->callbacks */
+				ao2_ref(thiscb, -1);
+			}
+			hint->laststate = saved_hint->laststate;
 			ao2_unlock(hint);
-		}
-		ast_free(this);
-		if (hint) {
 			ao2_ref(hint, -1);
 		}
+		ast_free(saved_hint);
 	}
 
 	ao2_unlock(hints);
 	ast_unlock_contexts();
+	ast_mutex_unlock(&context_merge_lock);
 	endlocktime = ast_tvnow();
 
 	/*
@@ -10286,11 +10418,18 @@
 static int hint_hash(const void *obj, const int flags)
 {
 	const struct ast_hint *hint = obj;
-
-	int res = -1;
-
-	if (ast_get_extension_name(hint->exten)) {
-		res = ast_str_case_hash(ast_get_extension_name(hint->exten));
+	const char *exten_name;
+	int res;
+
+	exten_name = ast_get_extension_name(hint->exten);
+	if (ast_strlen_zero(exten_name)) {
+		/*
+		 * If the exten or extension name isn't set, return 0 so that
+		 * the ao2_find() search will start in the first bucket.
+		 */
+		res = 0;
+	} else {
+		res = ast_str_case_hash(exten_name);
 	}
 
 	return res;




More information about the asterisk-commits mailing list