Index: asterisk-1.6.2.13/include/asterisk.h =================================================================== --- asterisk-1.6.2.13.orig/include/asterisk.h 2010-11-08 14:57:47.000000000 +0000 +++ asterisk-1.6.2.13/include/asterisk.h 2010-11-08 15:03:30.000000000 +0000 @@ -79,6 +79,7 @@ int ast_set_priority(int); /*!< Provided by asterisk.c */ int ast_fd_init(void); /*!< Provided by astfd.c */ +int ast_pbx_init(void); /*!< Provided by pbx.c */ /*! * \brief Register a function to be executed before Asterisk exits. Index: asterisk-1.6.2.13/main/asterisk.c =================================================================== --- asterisk-1.6.2.13.orig/main/asterisk.c 2010-11-08 14:57:48.000000000 +0000 +++ asterisk-1.6.2.13/main/asterisk.c 2010-11-08 15:04:57.000000000 +0000 @@ -3173,6 +3173,7 @@ tdd_init(); ast_tps_init(); ast_fd_init(); + ast_pbx_init(); if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); Index: asterisk-1.6.2.13/main/pbx.c =================================================================== --- asterisk-1.6.2.13.orig/main/pbx.c 2010-11-08 14:57:48.000000000 +0000 +++ asterisk-1.6.2.13/main/pbx.c 2010-11-08 18:01:37.000000000 +0000 @@ -12,7 +12,7 @@ * channels for your use. * * This program is free software, distributed under the terms of -* the GNU General Public License Version 2. See the LICENSE file + * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ @@ -57,6 +57,7 @@ #include "asterisk/causes.h" #include "asterisk/musiconhold.h" #include "asterisk/app.h" +#include "asterisk/astobj2.h" #include "asterisk/devicestate.h" #include "asterisk/event.h" #include "asterisk/hashtab.h" @@ -886,7 +887,6 @@ struct ast_exten *exten; /*!< Extension */ int laststate; /*!< Last known state */ AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks; /*!< Callback list for this extension */ - AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */ }; static const struct cfextension_states { @@ -1130,14 +1130,16 @@ static AST_RWLIST_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_RWLIST_HEAD_STATIC(hints, ast_hint); +static struct ao2_container *hints; +/* XXX TODO Convert this to an astobj2 container, too. */ static AST_LIST_HEAD_NOLOCK_STATIC(statecbs, ast_state_cb); #ifdef CONTEXT_DEBUG @@ -3860,11 +3862,10 @@ { struct ast_hint *hint; struct statechange *sc = datap; + struct ao2_iterator i; - ast_rdlock_contexts(); - AST_RWLIST_RDLOCK(&hints); - - AST_RWLIST_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 buf[AST_MAX_EXTENSION]; char *parse = buf; @@ -3890,6 +3891,8 @@ /* Device state changed since last check - notify the watchers */ + ast_rdlock_contexts(); + ao2_lock(hints); /* For general callbacks */ AST_LIST_TRAVERSE(&statecbs, cblist, entry) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); @@ -3901,9 +3904,12 @@ } hint->laststate = state; /* record we saw the change */ + ao2_unlock(hint); + + ao2_unlock(hints); + ast_unlock_contexts(); } - AST_RWLIST_UNLOCK(&hints); - ast_unlock_contexts(); + ao2_iterator_destroy(&i); ast_free(sc); return 0; } @@ -3918,19 +3924,19 @@ /* If there's no context and extension: add callback to statecbs list */ if (!context && !exten) { - AST_RWLIST_WRLOCK(&hints); + ao2_lock(hints); AST_LIST_TRAVERSE(&statecbs, cblist, entry) { if (cblist->callback == callback) { cblist->data = data; - AST_RWLIST_UNLOCK(&hints); + ao2_unlock(hints); return 0; } } /* Now insert the callback */ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { - AST_RWLIST_UNLOCK(&hints); + ao2_unlock(hints); return -1; } cblist->id = 0; @@ -3938,9 +3944,7 @@ cblist->data = data; AST_LIST_INSERT_HEAD(&statecbs, cblist, entry); - - AST_RWLIST_UNLOCK(&hints); - + ao2_unlock(hints); return 0; } @@ -3967,23 +3971,15 @@ } } - /* Find the hint in the list of hints */ - AST_RWLIST_WRLOCK(&hints); - - AST_RWLIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == e) - break; - } + hint = ao2_find(hints, e, 0); if (!hint) { - /* We have no hint, sorry */ - AST_RWLIST_UNLOCK(&hints); return -1; } /* Now insert the callback in the callback list */ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) { - AST_RWLIST_UNLOCK(&hints); + ao2_ref(hint, -1); return -1; } @@ -3991,55 +3987,78 @@ cblist->callback = callback; /* Pointer to callback routine */ cblist->data = data; /* Data for the callback */ + ao2_lock(hint); AST_LIST_INSERT_HEAD(&hint->callbacks, cblist, entry); + ao2_unlock(hint); - AST_RWLIST_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; + + AST_LIST_TRAVERSE(&hint->callbacks, cb, entry) { + if (cb->id == *id) { + return CMP_MATCH | CMP_STOP; + } + } + + return 0; +} + /*! \brief 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; int ret = -1; - if (!id && !callback) + if (!id && !callback) { return -1; - - AST_RWLIST_WRLOCK(&hints); + } if (!id) { /* id == 0 is a callback without extension */ + ao2_lock(hints); AST_LIST_TRAVERSE_SAFE_BEGIN(&statecbs, p_cur, entry) { if (p_cur->callback == callback) { AST_LIST_REMOVE_CURRENT(entry); + ret = 0; break; } } AST_LIST_TRAVERSE_SAFE_END; + ao2_unlock(hints); } else { /* callback with extension, find the callback based on ID */ struct ast_hint *hint; - AST_RWLIST_TRAVERSE(&hints, hint, list) { + + hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id); + + if (hint) { + ao2_lock(hint); AST_LIST_TRAVERSE_SAFE_BEGIN(&hint->callbacks, p_cur, entry) { if (p_cur->id == id) { AST_LIST_REMOVE_CURRENT(entry); + ret = 0; break; } } AST_LIST_TRAVERSE_SAFE_END; - - if (p_cur) - break; + ao2_unlock(hint); + ao2_ref(hint, -1); } } - if (p_cur) { - ast_free(p_cur); - } + return ret; +} - AST_RWLIST_UNLOCK(&hints); - return ret; +static void ast_hint_destroy(void *obj) +{ + /* ast_remove_hint() takes care of most things before object destruction */ } @@ -4048,26 +4067,30 @@ { struct ast_hint *hint; - if (!e) + if (!e) { return -1; + } - /* Search if hint exists, do nothing */ - AST_RWLIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == e) { - ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); - return -1; - } + 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); + return -1; } ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); - if (!(hint = ast_calloc(1, sizeof(*hint)))) { + 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_RWLIST_INSERT_HEAD(&hints, hint, list); + + ao2_link(hints, hint); + + ao2_ref(hint, -1); return 0; } @@ -4077,9 +4100,8 @@ { int ret; - AST_RWLIST_WRLOCK(&hints); + /* This is a NOOP now that ao2 is being used. */ ret = ast_add_hint_nolock(e); - AST_RWLIST_UNLOCK(&hints); return ret; } @@ -4088,19 +4110,19 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne) { struct ast_hint *hint; - int res = -1; - AST_RWLIST_WRLOCK(&hints); - AST_RWLIST_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_RWLIST_UNLOCK(&hints); - return res; + ao2_lock(hint); + hint->exten = ne; + ao2_unlock(hint); + ao2_ref(hint, -1); + + return 0; } /*! \brief Remove hint from extension */ @@ -4109,32 +4131,27 @@ /* Cleanup the Notifys if hint is removed */ struct ast_hint *hint; struct ast_state_cb *cblist; - int res = -1; - if (!e) + if (!e) { return -1; + } - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) { - if (hint->exten != e) - continue; + hint = ao2_find(hints, e, 0); + + if (!hint) { + return -1; + } - while ((cblist = AST_LIST_REMOVE_HEAD(&hint->callbacks, entry))) { - /* Notify with -1 and remove all callbacks */ + while ((cblist = AST_LIST_REMOVE_HEAD(&hint->callbacks, entry))) { + /* Notify with -1 and remove all callbacks */ cblist->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cblist->data); ast_free(cblist); - } - - AST_RWLIST_REMOVE_CURRENT(list); - ast_free(hint); - - res = 0; - - break; } - AST_RWLIST_TRAVERSE_SAFE_END; + ao2_unlink(hints, hint); + ao2_ref(hint, -1); - return res; + return 0; } @@ -5345,6 +5362,7 @@ int num = 0; int watchers; struct ast_state_cb *watcher; + struct ao2_iterator i; switch (cmd) { case CLI_INIT: @@ -5357,15 +5375,14 @@ return NULL; } - AST_RWLIST_RDLOCK(&hints); - if (AST_RWLIST_EMPTY(&hints)) { + if (ao2_container_count(hints) == 0) { ast_cli(a->fd, "There are no registered dialplan hints\n"); - AST_RWLIST_UNLOCK(&hints); return CLI_SUCCESS; } - /* ... we have hints ... */ + ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n"); - AST_RWLIST_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; AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) { watchers++; @@ -5377,9 +5394,11 @@ ast_extension_state2str(hint->laststate), watchers); num++; } + ao2_iterator_destroy(&i); + ast_cli(a->fd, "----------------\n"); ast_cli(a->fd, "- %d hints registered\n", num); - AST_RWLIST_UNLOCK(&hints); + return CLI_SUCCESS; } @@ -5390,21 +5409,22 @@ char *ret = NULL; int which = 0; int wordlen; + struct ao2_iterator i; if (pos != 3) return NULL; wordlen = strlen(word); - AST_RWLIST_RDLOCK(&hints); - /* walk through all hints */ - AST_RWLIST_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)) { if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) { ret = ast_strdup(ast_get_extension_name(hint->exten)); + ao2_ref(hint, -1); break; } } - AST_RWLIST_UNLOCK(&hints); + ao2_iterator_destroy(&i); return ret; } @@ -5416,6 +5436,7 @@ int watchers; int num = 0, extenlen; struct ast_state_cb *watcher; + struct ao2_iterator i; switch (cmd) { case CLI_INIT: @@ -5431,14 +5452,14 @@ if (a->argc < 4) return CLI_SHOWUSAGE; - AST_RWLIST_RDLOCK(&hints); - if (AST_RWLIST_EMPTY(&hints)) { - ast_cli(a->fd, "There are no registered dialplan hints\n"); - AST_RWLIST_UNLOCK(&hints); - return CLI_SUCCESS; - } + if (ao2_container_count(hints) == 0) { + ast_cli(a->fd, "There are no registered dialplan hints\n"); + return CLI_SUCCESS; + } + extenlen = strlen(a->argv[3]); - AST_RWLIST_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)) { if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) { watchers = 0; AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) { @@ -5452,7 +5473,8 @@ num++; } } - AST_RWLIST_UNLOCK(&hints); + ao2_iterator_destroy(&i); + if (!num) ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]); else @@ -6648,6 +6670,7 @@ int length; struct ast_state_cb *thiscb; struct ast_hashtab_iter *iter; + 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 @@ -6668,19 +6691,23 @@ } ast_hashtab_end_traversal(iter); - AST_RWLIST_WRLOCK(&hints); + ao2_lock(hints); writelocktime = ast_tvnow(); /* preserve all watchers for hints */ - AST_RWLIST_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 (!AST_LIST_EMPTY(&hint->callbacks)) { 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; + } /* this removes all the callbacks from the hint into this. */ + ao2_lock(hint); AST_LIST_APPEND_LIST(&this->callbacks, &hint->callbacks, entry); this->laststate = hint->laststate; this->context = this->data; + ao2_unlock(hint); strcpy(this->data, hint->exten->parent->name); this->exten = this->data + strlen(this->context) + 1; strcpy(this->exten, hint->exten->exten); @@ -6713,11 +6740,7 @@ exten = ast_hint_extension_nolock(NULL, this->context, this->exten); } - /* Find the hint in the list of hints */ - AST_RWLIST_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 */ while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) { @@ -6725,13 +6748,18 @@ ast_free(thiscb); } } else { + ao2_lock(hint); AST_LIST_APPEND_LIST(&hint->callbacks, &this->callbacks, entry); hint->laststate = this->laststate; + ao2_unlock(hint); + } + if (hint) { + ao2_ref(hint, -1); } ast_free(this); } - AST_RWLIST_UNLOCK(&hints); + ao2_unlock(hints); ast_unlock_contexts(); endlocktime = ast_tvnow(); @@ -9636,3 +9664,24 @@ { return pbx_parseable_goto(chan, goto_string, 1); } + +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; +}