Index: include/asterisk.h =================================================================== --- include/asterisk.h (revision 293798) +++ include/asterisk.h (working copy) @@ -109,6 +109,7 @@ void ast_autoservice_init(void); /*!< Provided by autoservice.c */ int ast_fd_init(void); /*!< Provided by astfd.c */ int ast_test_init(void); /*!< Provided by test.c */ +int ast_pbx_init(void); /*!< Provided by pbx.c */ /* Many headers need 'ast_channel' to be defined */ struct ast_channel; Index: main/asterisk.c =================================================================== --- main/asterisk.c (revision 293798) +++ main/asterisk.c (working copy) @@ -2765,6 +2765,7 @@ ast_utils_init(); tdd_init(); ast_fd_init(); + ast_pbx_init(); if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); Index: main/pbx.c =================================================================== --- main/pbx.c (revision 293798) +++ main/pbx.c (working copy) @@ -60,6 +60,7 @@ #include "asterisk/app.h" #include "asterisk/stringfields.h" #include "asterisk/threadstorage.h" +#include "asterisk/astobj2.h" /*! * \note I M P O R T A N T : @@ -197,7 +198,6 @@ struct ast_exten *exten; /*!< Extension */ int laststate; /*!< Last known state */ struct ast_state_cb *callbacks; /*!< Callback list for this extension */ - AST_LIST_ENTRY(ast_hint) list; /*!< Pointer to next hint in list */ }; static const struct cfextension_states { @@ -503,13 +503,16 @@ static AST_LIST_HEAD_STATIC(switches, ast_switch); static int stateid = 1; + /* WARNING: - When holding this list's lock, do _not_ do anything that will cause conlock + When holding this container's lock, do _not_ do anything that will cause conlock to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete function will take the locks in conlock/hints order, so any other paths that require both locks must also take them in that order. */ -static AST_LIST_HEAD_STATIC(hints, ast_hint); +static struct ao2_container *hints; + +/* XXX TODO Convert this to an astobj2 container, too. */ struct ast_state_cb *statecbs; /* @@ -2012,15 +2015,14 @@ { struct ast_hint *hint; struct ast_dynamic_str *str; + struct ao2_iterator i; if (!(str = ast_dynamic_str_create(1024))) { return; } - ast_rdlock_contexts(); - AST_LIST_LOCK(&hints); - - AST_LIST_TRAVERSE(&hints, hint, list) { + 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 *cblist; char *cur, *parse; int state; @@ -2029,34 +2031,47 @@ parse = str->str; while ( (cur = strsep(&parse, "&")) ) { - if (!strcasecmp(cur, device)) + if (!strcasecmp(cur, device)) { break; + } } - if (!cur) + if (!cur) { continue; + } /* Get device state for this hint */ state = ast_extension_state2(hint->exten); - if ((state == -1) || (state == hint->laststate)) + if ((state == -1) || (state == hint->laststate)) { continue; + } /* Device state changed since last check - notify the watchers */ + ast_rdlock_contexts(); + ao2_lock(hints); /* For general callbacks */ - for (cblist = statecbs; cblist; cblist = cblist->next) + for (cblist = statecbs; cblist; cblist = cblist->next) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + } + //caused problems here + //ao2_unlock(hints); + //ast_unlock_contexts(); /* For extension callbacks */ - for (cblist = hint->callbacks; cblist; cblist = cblist->next) + for (cblist = hint->callbacks; cblist; cblist = cblist->next) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + } hint->laststate = state; /* record we saw the change */ + ao2_unlock(hint); + + ao2_unlock(hints); + ast_unlock_contexts(); } - AST_LIST_UNLOCK(&hints); - ast_unlock_contexts(); + ao2_iterator_destroy(&i); ast_free(str); } @@ -2070,19 +2085,19 @@ /* If there's no context and extension: add callback to statecbs list */ if (!context && !exten) { - AST_LIST_LOCK(&hints); + ao2_lock(hints); for (cblist = statecbs; cblist; cblist = cblist->next) { if (cblist->callback == callback) { cblist->data = data; - AST_LIST_UNLOCK(&hints); + ao2_unlock(hints); return 0; } } /* Now insert the callback */ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { - AST_LIST_UNLOCK(&hints); + ao2_unlock(hints); return -1; } cblist->id = 0; @@ -2092,7 +2107,7 @@ cblist->next = statecbs; statecbs = cblist; - AST_LIST_UNLOCK(&hints); + ao2_unlock(hints); return 0; } @@ -2105,106 +2120,133 @@ return -1; } - /* Find the hint in the list of hints */ - AST_LIST_LOCK(&hints); + hint = ao2_find(hints, e, 0); - AST_LIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == e) - break; - } - if (!hint) { - /* We have no hint, sorry */ - AST_LIST_UNLOCK(&hints); return -1; } /* Now insert the callback in the callback list */ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { - AST_LIST_UNLOCK(&hints); + ao2_ref(hint, -1); return -1; } cblist->id = stateid++; /* Unique ID for this callback */ cblist->callback = callback; /* Pointer to callback routine */ cblist->data = data; /* Data for the callback */ + ao2_lock(hint); cblist->next = hint->callbacks; hint->callbacks = cblist; + ao2_unlock(hint); - AST_LIST_UNLOCK(&hints); + ao2_ref(hint, -1); + return cblist->id; } +static int find_hint_by_cb_id(void *obj, void *arg, int flags) +{ + const struct ast_hint *hint = obj; + int *id = arg; + struct ast_state_cb *cb; + + for (cb = hint->callbacks; cb; cb = cb->next) { + if (cb->id == *id) { + return CMP_MATCH | CMP_STOP; + } + } + + return 0; +} + /*! \brief ast_extension_state_del: Remove a watcher from the callback list */ int ast_extension_state_del(int id, ast_state_cb_type callback) { struct ast_state_cb **p_cur = NULL; /* address of pointer to us */ int ret = -1; - if (!id && !callback) + if (!id && !callback) { return -1; + } - AST_LIST_LOCK(&hints); - if (!id) { /* id == 0 is a callback without extension */ + ao2_lock(hints); for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) { - if ((*p_cur)->callback == callback) + if ((*p_cur)->callback == callback) { break; + } } + if (p_cur && *p_cur) { + struct ast_state_cb *cur = *p_cur; + *p_cur = cur->next; + free(cur); + ret = 0; + } + ao2_unlock(hints); } else { /* callback with extension, find the callback based on ID */ struct ast_hint *hint; - AST_LIST_TRAVERSE(&hints, hint, list) { + + hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id); + + if (hint) { + ao2_lock(hint); for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) { - if ((*p_cur)->id == id) + if ((*p_cur)->id == id) { break; + } } - if (*p_cur) /* found in the inner loop */ - break; + if (p_cur && *p_cur) { + struct ast_state_cb *cur = *p_cur; + *p_cur = cur->next; + free(cur); + ret = 0; + } + ao2_unlock(hint); + ao2_ref(hint, -1); } } - if (p_cur && *p_cur) { - struct ast_state_cb *cur = *p_cur; - *p_cur = cur->next; - free(cur); - ret = 0; - } - AST_LIST_UNLOCK(&hints); + return ret; } +static void ast_hint_destroy(void *obj) +{ + /* ast_remove_hint() takes care of most things before object destruction */ +} + /*! \brief ast_add_hint: Add hint to hint list, check initial extension state */ static int ast_add_hint(struct ast_exten *e) { struct ast_hint *hint; - if (!e) + if (!e) { return -1; + } - AST_LIST_LOCK(&hints); + hint = ao2_find(hints, e, 0); - /* Search if hint exists, do nothing */ - AST_LIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == e) { - AST_LIST_UNLOCK(&hints); - if (option_debug > 1) - ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); - return -1; - } + if (hint) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); + return -1; } if (option_debug > 1) ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); - if (!(hint = ast_calloc(1, sizeof(*hint)))) { - AST_LIST_UNLOCK(&hints); + if (!(hint = ao2_alloc(sizeof(*hint), ast_hint_destroy))) { return -1; } + /* Initialize and insert new item at the top */ hint->exten = e; hint->laststate = ast_extension_state2(e); - AST_LIST_INSERT_HEAD(&hints, hint, list); - AST_LIST_UNLOCK(&hints); + ao2_link(hints, hint); + + ao2_ref(hint, -1); + return 0; } @@ -2212,19 +2254,19 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne) { struct ast_hint *hint; - int res = -1; - AST_LIST_LOCK(&hints); - AST_LIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == oe) { - hint->exten = ne; - res = 0; - break; - } + hint = ao2_find(hints, oe, 0); + + if (!hint) { + return -1; } - AST_LIST_UNLOCK(&hints); - return res; + ao2_lock(hint); + hint->exten = ne; + ao2_unlock(hint); + ao2_ref(hint, -1); + + return 0; } /*! \brief ast_remove_hint: Remove hint from extension */ @@ -2233,34 +2275,30 @@ /* Cleanup the Notifys if hint is removed */ struct ast_hint *hint; struct ast_state_cb *cblist, *cbprev; - int res = -1; - if (!e) + if (!e) { return -1; + } - AST_LIST_LOCK(&hints); - AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) { - if (hint->exten == e) { - cbprev = NULL; - cblist = hint->callbacks; - while (cblist) { - /* Notify with -1 and remove all callbacks */ - cbprev = cblist; - cblist = cblist->next; - cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data); - free(cbprev); - } - hint->callbacks = NULL; - AST_LIST_REMOVE_CURRENT(&hints, list); - free(hint); - res = 0; - break; - } + hint = ao2_find(hints, e, 0); + + if (!hint) { + return -1; } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&hints); - return res; + cbprev = NULL; + cblist = hint->callbacks; + while (cblist) { + cbprev = cblist; + cblist = cblist->next; + cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data); + ast_free(cbprev); + } + hint->callbacks = NULL; + ao2_unlink(hints, hint); + ao2_ref(hint, -1); + + return 0; } @@ -3267,18 +3305,21 @@ int num = 0; int watchers; struct ast_state_cb *watcher; + struct ao2_iterator i; - if (AST_LIST_EMPTY(&hints)) { + if (ao2_container_count(hints) == 0) { ast_cli(fd, "There are no registered dialplan hints\n"); return RESULT_SUCCESS; } - /* ... we have hints ... */ + ast_cli(fd, "\n -= Registered Asterisk Dial Plan Hints =-\n"); - AST_LIST_LOCK(&hints); - AST_LIST_TRAVERSE(&hints, hint, list) { + + i = ao2_iterator_init(hints, 0); + for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) { watchers = 0; - for (watcher = hint->callbacks; watcher; watcher = watcher->next) + for (watcher = hint->callbacks; watcher; watcher = watcher->next) { watchers++; + } ast_cli(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)), @@ -3286,9 +3327,11 @@ ast_extension_state2str(hint->laststate), watchers); num++; } + ao2_iterator_destroy(&i); + ast_cli(fd, "----------------\n"); ast_cli(fd, "- %d hints registered\n", num); - AST_LIST_UNLOCK(&hints); + return RESULT_SUCCESS; } @@ -3980,6 +4023,7 @@ struct ast_exten *exten; int length; struct ast_state_cb *thiscb, *prevcb; + struct ao2_iterator i; /* it is very important that this function hold the hint list lock _and_ the conlock during its operation; not only do we need to ensure that the list of contexts @@ -3990,17 +4034,21 @@ other code paths that use this order */ ast_wrlock_contexts(); - AST_LIST_LOCK(&hints); + ao2_lock(hints); /* preserve all watchers for hints associated with this registrar */ - AST_LIST_TRAVERSE(&hints, hint, list) { + 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 (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) { length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this); - if (!(this = ast_calloc(1, length))) + if (!(this = ast_calloc(1, length))) { continue; + } + ao2_lock(hint); this->callbacks = hint->callbacks; hint->callbacks = NULL; this->laststate = hint->laststate; + ao2_unlock(hint); this->context = this->data; strcpy(this->data, hint->exten->parent->name); this->exten = this->data + strlen(this->context) + 1; @@ -4041,11 +4089,7 @@ while ((this = 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); - /* Find the hint in the list of hints */ - AST_LIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == exten) - break; - } + hint = ao2_find(hints, exten, 0); if (!exten || !hint) { /* this hint has been removed, notify the watchers */ prevcb = NULL; @@ -4058,16 +4102,22 @@ } } else { thiscb = this->callbacks; - while (thiscb->next) + while (thiscb->next) { thiscb = thiscb->next; + } + ao2_lock(hint); thiscb->next = hint->callbacks; hint->callbacks = this->callbacks; hint->laststate = this->laststate; + ao2_unlock(hint); } + if (hint) { + ao2_ref(hint, -1); + } free(this); } - AST_LIST_UNLOCK(&hints); + ao2_unlock(hints); ast_unlock_contexts(); return; @@ -6477,3 +6527,24 @@ return 0; } + +static int hint_hash(const void *hint, const int flags) +{ + /* Only 1 bucket, not important. */ + return 0; +} + +static int hint_cmp(void *obj, void *arg, int flags) +{ + const struct ast_hint *hint = obj; + const struct ast_exten *exten = arg; + + return (hint->exten == exten) ? CMP_MATCH | CMP_STOP : 0; +} + +int ast_pbx_init(void) +{ + hints = ao2_container_alloc(1, hint_hash, hint_cmp); + + return hints ? 0 : -1; +}