Property changes on: . ___________________________________________________________________ Modified: branch-1.4-merged - /branches/1.4:1-279056,279206,279945,280088,280341,280448,280811,280982,281390,281566,281762,281819,281911,282129,282430,282729,282893,283048,283123,283380,283690,283880,283960,284316,284393,284478,284703,284777,284881,285088,285194,285266,285365,285566,285638,285742,285817,285889,286023,286059,286113-286114,286222,286267,286679,286756,286941,287114,287118,287197,287307,287386,287469,287555,287758,287933,288005,288112,288192,288265-288266,288339,288343,288416,288499,288636,288746,289094,289177,289338,289424,289500,289699,289703,289797,289873,289949,290100,290177,290323,290392,290750,290862,291109,291263,291392,291577,291643,291938,292222-292223,292411,292866,293004,293194,293339,293416,293639,293722,293805,293968 + /branches/1.4:1-279056,279206,279945,280088,280341,280448,280811,280982,281390,281566,281762,281819,281911,282129,282430,282729,282893,283048,283123,283380,283690,283880,283960,284316,284393,284478,284703,284777,284881,285088,285194,285266,285365,285566,285638,285742,285817,285889,286023,286059,286113-286114,286222,286267,286679,286756,286941,287114,287118,287197,287307,287386,287469,287555,287758,287933,288005,288112,288192,288265-288266,288339,288343,288416,288499,288636,288746,289094,289177,289338,289424,289500,289699,289703,289797,289873,289949,290100,290177,290323,290392,290750,290862,291109,291263,291392,291577,291643,291938,292222-292223,292411,292866,293004,293194,293339,293416,293639,293722,293805,293968,294384 Index: include/asterisk.h =================================================================== --- include/asterisk.h (revision 294384) +++ include/asterisk.h (working copy) @@ -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: main/asterisk.c =================================================================== --- main/asterisk.c (revision 294384) +++ main/asterisk.c (working copy) @@ -3178,6 +3178,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: main/pbx.c =================================================================== --- main/pbx.c (revision 294384) +++ main/pbx.c (working copy) @@ -64,6 +64,7 @@ #include "asterisk/indications.h" #include "asterisk/taskprocessor.h" #include "asterisk/xmldoc.h" +#include "asterisk/astobj2.h" /*! * \note I M P O R T A N T : @@ -1131,13 +1132,14 @@ 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 @@ -3865,15 +3867,15 @@ struct ast_hint *hint; struct ast_str *str; struct statechange *sc = datap; + struct ao2_iterator i; if (!(str = ast_str_create(1024))) { return -1; } - 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 *cur, *parse; int state; @@ -3900,6 +3902,18 @@ /* Device state changed since last check - notify the watchers */ + ast_rdlock_contexts(); + ao2_lock(hints); + ao2_lock(hint); + + if (hint->exten == NULL) { + /* the extension has been destroyed */ + ao2_unlock(hint); + ao2_unlock(hints); + ast_unlock_contexts(); + continue; + } + /* For general callbacks */ AST_LIST_TRAVERSE(&statecbs, cblist, entry) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); @@ -3911,9 +3925,11 @@ } 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(str); ast_free(sc); return 0; @@ -3929,19 +3945,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; @@ -3950,8 +3966,7 @@ AST_LIST_INSERT_HEAD(&statecbs, cblist, entry); - AST_RWLIST_UNLOCK(&hints); - + ao2_unlock(hints); return 0; } @@ -3979,22 +3994,15 @@ } /* Find the hint in the list of hints */ - AST_RWLIST_WRLOCK(&hints); + hint = ao2_find(hints, e, 0); - AST_RWLIST_TRAVERSE(&hints, hint, list) { - if (hint->exten == e) - break; - } - 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; } @@ -4002,25 +4010,43 @@ 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; } /*! \brief Remove a watcher from the callback list */ +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 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; 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); @@ -4028,9 +4054,14 @@ } } 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); @@ -4039,8 +4070,8 @@ } AST_LIST_TRAVERSE_SAFE_END; - if (p_cur) - break; + ao2_unlock(hint); + ao2_ref(hint, -1); } } @@ -4048,8 +4079,6 @@ ast_free(p_cur); } - AST_RWLIST_UNLOCK(&hints); - return ret; } @@ -4059,27 +4088,32 @@ { 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; - } + /* 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); + 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), NULL))) { 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; } @@ -4088,9 +4122,9 @@ { int ret; - AST_RWLIST_WRLOCK(&hints); + ao2_lock(hints); ret = ast_add_hint_nolock(e); - AST_RWLIST_UNLOCK(&hints); + ao2_unlock(hints); return ret; } @@ -4099,19 +4133,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 */ @@ -4120,32 +4154,31 @@ /* 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); - 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); - } + if (!hint) { + return -1; + } + ao2_lock(hint); - AST_RWLIST_REMOVE_CURRENT(list); - ast_free(hint); + 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); + } - res = 0; + hint->exten = NULL; + ao2_unlink(hints, hint); + ao2_unlock(hint); + ao2_ref(hint, -1); - break; - } - AST_RWLIST_TRAVERSE_SAFE_END; - - return res; + return 0; } @@ -5356,6 +5389,7 @@ int num = 0; int watchers; struct ast_state_cb *watcher; + struct ao2_iterator i; switch (cmd) { case CLI_INIT: @@ -5368,15 +5402,16 @@ 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++; @@ -5388,9 +5423,10 @@ 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; } @@ -5401,21 +5437,24 @@ 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) { - if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) { + 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)) { + ao2_lock(hint); + if (!strncasecmp(word, hint->exten ? ast_get_extension_name(hint->exten) : "", wordlen) && ++which > state) { ret = ast_strdup(ast_get_extension_name(hint->exten)); + ao2_unlock(hint); break; } } - AST_RWLIST_UNLOCK(&hints); + ao2_iterator_destroy(&i); return ret; } @@ -5427,6 +5466,7 @@ int watchers; int num = 0, extenlen; struct ast_state_cb *watcher; + struct ao2_iterator i; switch (cmd) { case CLI_INIT: @@ -5442,15 +5482,16 @@ if (a->argc < 4) return CLI_SHOWUSAGE; - 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; } + extenlen = strlen(a->argv[3]); - AST_RWLIST_TRAVERSE(&hints, hint, list) { - if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) { + 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)) { + ao2_lock(hint); + if (!strncasecmp(hint->exten ? ast_get_extension_name(hint->exten) : "", a->argv[3], extenlen)) { watchers = 0; AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) { watchers++; @@ -5463,7 +5504,7 @@ 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 @@ -6659,6 +6700,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 @@ -6679,15 +6721,24 @@ } 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; + } + ao2_lock(hint); + + if (hint->exten == NULL) { + ao2_unlock(hint); + continue; + } + /* this removes all the callbacks from the hint into this. */ AST_LIST_APPEND_LIST(&this->callbacks, &hint->callbacks, entry); this->laststate = hint->laststate; @@ -6695,6 +6746,7 @@ strcpy(this->data, hint->exten->parent->name); this->exten = this->data + strlen(this->context) + 1; strcpy(this->exten, hint->exten->exten); + ao2_unlock(hint); AST_LIST_INSERT_HEAD(&store, this, list); } } @@ -6725,10 +6777,7 @@ } /* 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))) { @@ -6740,9 +6789,12 @@ hint->laststate = this->laststate; } ast_free(this); + if (hint) { + ao2_ref(hint, -1); + } } - AST_RWLIST_UNLOCK(&hints); + ao2_unlock(hints); ast_unlock_contexts(); endlocktime = ast_tvnow(); @@ -9654,3 +9706,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; +}