[svn-commits] jpeeler: branch 1.6.2 r294639 - in /branches/1.6.2: ./ include/ main/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Nov 11 13:31:18 CST 2010


Author: jpeeler
Date: Thu Nov 11 13:31:00 2010
New Revision: 294639

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=294639
Log:
Merged revisions 294384 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r294384 | jpeeler | 2010-11-09 11:37:59 -0600 (Tue, 09 Nov 2010) | 47 lines
  
  Fix a deadlock in device state change processing.
  
  Copied from some notes from the original author (Russell):
  
  Deadlock scenario:
  Thread 1: device state change thread
    Holds - rdlock on contexts
    Holds - hints lock
    Waiting on channels container lock
  
  Thread 2: SIP monitor thread
    Holds the "iflock"
    Holds a sip_pvt lock
    Holds channel container lock
    Waiting for a channel lock
  
  Thread 3: A channel thread (chan_local in this case)
    Holds 2 channel locks acquired within app_dial
    Holds a 3rd channel lock it got inside of chan_local
    Holds a local_pvt lock
    Waiting on a rdlock of the contexts lock
  
  A bunch of other threads waiting on a wrlock of the contexts lock
  
  
  To address this deadlock, some locking order rules must be put in place and
  enforced. Existing relevant rules:
  
  1) channel lock before a pvt lock
  2) contexts lock before hints lock
  3) channels container before a channel
  
  What's missing is some enforcement of the order when you involve more than any
  two. To fix this problem, I put in some code that ensures that (at least in the
  code paths involved in this bug) the locks in (3) come before the locks in (2).
  To change the operation of thread 1 to comply, I converted the storage of hints
  to an astobj2 container. This allows processing of hints without holding the
  hints container lock. So, in the code path that led to thread 1's state, it no
  longer holds either the contexts or hints lock while it attempts to lock the
  channels container.
  
  (closes issue #18165)
  Reported by: antonio
  
  ABE-2583
........

Modified:
    branches/1.6.2/   (props changed)
    branches/1.6.2/include/asterisk.h
    branches/1.6.2/main/asterisk.c
    branches/1.6.2/main/pbx.c

Propchange: branches/1.6.2/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: branches/1.6.2/include/asterisk.h
URL: http://svnview.digium.com/svn/asterisk/branches/1.6.2/include/asterisk.h?view=diff&rev=294639&r1=294638&r2=294639
==============================================================================
--- branches/1.6.2/include/asterisk.h (original)
+++ branches/1.6.2/include/asterisk.h Thu Nov 11 13:31:00 2010
@@ -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.

Modified: branches/1.6.2/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.6.2/main/asterisk.c?view=diff&rev=294639&r1=294638&r2=294639
==============================================================================
--- branches/1.6.2/main/asterisk.c (original)
+++ branches/1.6.2/main/asterisk.c Thu Nov 11 13:31:00 2010
@@ -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"));

Modified: branches/1.6.2/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.6.2/main/pbx.c?view=diff&rev=294639&r1=294638&r2=294639
==============================================================================
--- branches/1.6.2/main/pbx.c (original)
+++ branches/1.6.2/main/pbx.c Thu Nov 11 13:31:00 2010
@@ -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.
  */
 
@@ -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 :
@@ -886,8 +887,14 @@
 	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 */
 };
+
+/* --- Hash tables of various objects --------*/
+#ifdef LOW_MEMORY
+static const int HASH_EXTENHINT_SIZE = 17;
+#else
+static const int HASH_EXTENHINT_SIZE = 563;
+#endif
 
 static const struct cfextension_states {
 	int extension_state;
@@ -1131,13 +1138,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 +3873,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 +3908,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 +3931,11 @@
 		}
 
 		hint->laststate = state;	/* record we saw the change */
-	}
-	AST_RWLIST_UNLOCK(&hints);
-	ast_unlock_contexts();
+		ao2_unlock(hint);
+		ao2_unlock(hints);
+		ast_unlock_contexts();
+	}
+	ao2_iterator_destroy(&i);
 	ast_free(str);
 	ast_free(sc);
 	return 0;
@@ -3929,19 +3951,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 +3972,7 @@
 
 		AST_LIST_INSERT_HEAD(&statecbs, cblist, entry);
 
-		AST_RWLIST_UNLOCK(&hints);
-
+		ao2_unlock(hints);
 		return 0;
 	}
 
@@ -3979,22 +4000,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;
 	}
 
@@ -4002,25 +4016,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);
-
-	AST_RWLIST_UNLOCK(&hints);
+	ao2_unlock(hint);
+
+	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,19 +4060,25 @@
 			}
 		}
 		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);
 		}
 	}
 
@@ -4048,70 +4086,61 @@
 		ast_free(p_cur);
 	}
 
-	AST_RWLIST_UNLOCK(&hints);
-
 	return ret;
 }
 
 
-/*! \brief Add hint to hint list, check initial extension state; the hints had better be WRLOCKED already! */
-static int ast_add_hint_nolock(struct ast_exten *e)
+/*! \brief 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;
-
-	/* 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;
-}
-
-/*! \brief Add hint to hint list, check initial extension state */
-static int ast_add_hint(struct ast_exten *e)
-{
-	int ret;
-
-	AST_RWLIST_WRLOCK(&hints);
-	ret = ast_add_hint_nolock(e);
-	AST_RWLIST_UNLOCK(&hints);
-
-	return ret;
 }
 
 /*! \brief Change hint for an extension */
 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;
-		}
-	}
-	AST_RWLIST_UNLOCK(&hints);
-
-	return res;
+
+	hint = ao2_find(hints, oe, 0);
+
+	if (!hint) {
+		return -1;
+	}
+
+	ao2_lock(hint);
+	hint->exten = ne;
+	ao2_unlock(hint);
+	ao2_ref(hint, -1);
+
+	return 0;
 }
 
 /*! \brief Remove hint from extension */
@@ -4120,32 +4149,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;
-
-		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;
-
-	return res;
+	}
+
+	hint = ao2_find(hints, e, 0);
+
+	if (!hint) {
+		return -1;
+	}
+	ao2_lock(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);
+	}
+
+	hint->exten = NULL;
+	ao2_unlink(hints, hint);
+	ao2_unlock(hint);
+	ao2_ref(hint, -1);
+
+	return 0;
 }
 
 
@@ -5356,6 +5384,7 @@
 	int num = 0;
 	int watchers;
 	struct ast_state_cb *watcher;
+	struct ao2_iterator i;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -5368,15 +5397,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 +5418,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 +5432,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 +5461,7 @@
 	int watchers;
 	int num = 0, extenlen;
 	struct ast_state_cb *watcher;
+	struct ao2_iterator i;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -5442,15 +5477,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 +5499,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 +6695,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 +6716,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 +6741,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 +6772,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))) {
@@ -6736,13 +6780,18 @@
 				ast_free(thiscb);
 			}
 		} else {
+			ao2_lock(hint);
 			AST_LIST_APPEND_LIST(&hint->callbacks, &this->callbacks, entry);
 			hint->laststate = this->laststate;
+			ao2_unlock(hint);
 		}
 		ast_free(this);
-	}
-
-	AST_RWLIST_UNLOCK(&hints);
+		if (hint) {
+			ao2_ref(hint, -1);
+		}
+	}
+
+	ao2_unlock(hints);
 	ast_unlock_contexts();
 	endlocktime = ast_tvnow();
 
@@ -7606,7 +7655,7 @@
 			if (lockhints) {
 				ast_add_hint(tmp);
 			} else {
-				ast_add_hint_nolock(tmp);
+				ast_add_hint(tmp);
 			}
 		}
 	}
@@ -7831,7 +7880,7 @@
 			if (lockhints) {
 				ast_add_hint(tmp);
 			} else {
-				ast_add_hint_nolock(tmp);
+				ast_add_hint(tmp);
 			}
 		}
 	}
@@ -9654,3 +9703,31 @@
 {
 	return pbx_parseable_goto(chan, goto_string, 1);
 }
+
+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));
+	}
+
+	return res;
+}
+
+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(HASH_EXTENHINT_SIZE, hint_hash, hint_cmp);
+
+	return hints ? 0 : -1;
+}




More information about the svn-commits mailing list