[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