Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 92813) +++ channels/chan_sip.c (working copy) @@ -7127,7 +7127,7 @@ subscriptiontype = find_subscription_type(p->subscribed); /* Check which device/devices we are watching and if they are registered */ - if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { + if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) == -1) { char *hint2 = hint, *individual_hint = NULL; int hint_count = 0, unavailable_count = 0; @@ -8767,7 +8767,7 @@ /* If this is a subscription we actually just need to see if a hint exists for the extension */ if (req->method == SIP_SUBSCRIBE) { char hint[AST_MAX_EXTENSION]; - return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) ? 0 : -1); + return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) == -1 ? 0 : -1); } else { /* Check the dialplan for the username part of the request URI, the domain will be stored in the SIPDOMAIN variable Index: apps/app_dial.c =================================================================== --- apps/app_dial.c (revision 92813) +++ apps/app_dial.c (working copy) @@ -374,7 +374,7 @@ const char *context = S_OR(chan->macrocontext, chan->context); const char *exten = S_OR(chan->macroexten, chan->exten); - return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : ""; + return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) == -1 ? name : ""; } static void senddialevent(struct ast_channel *src, struct ast_channel *dst) Index: include/asterisk/pbx.h =================================================================== --- include/asterisk/pbx.h (revision 92813) +++ include/asterisk/pbx.h (working copy) @@ -313,7 +313,10 @@ * \param context which context to look in * \param exten which extension to get state * - * \return extension state as defined in the ast_extension_states enum + * \retval extension state as defined in the ast_extension_states enum + * \retval -EAGAIN if a deadlock would have resulted. You may unlock and + * relock all mutexes and try again. + * \retval -1 if the extension does not have an associated hint */ int ast_extension_state(struct ast_channel *c, const char *context, const char *exten); @@ -338,6 +341,8 @@ * * \retval -1 on failure * \retval ID on success + * \retval -EGAIN if a deadlock would have resulted. You may choose to unlock + * and lock all mutexes held before trying again. */ int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type callback, void *data); @@ -366,9 +371,11 @@ * \param context which context to look in * \param exten which extension to search for * - * \return If an extension within the given context with the priority PRIORITY_HINT - * is found a non zero value will be returned. - * Otherwise, 0 is returned. + * \retval -1, if an extension within the given context with the priority + * PRIORITY_HINT is found. + * \retval 0, if the hint is not found + * \retval -EGAIN if a deadlock would have resulted. You may choose to unlock + * and relock all mutexes held before trying again. */ int ast_get_hint(char *hint, int maxlen, char *name, int maxnamelen, struct ast_channel *c, const char *context, const char *exten); Index: main/pbx.c =================================================================== --- main/pbx.c (revision 92813) +++ main/pbx.c (working copy) @@ -1189,9 +1189,21 @@ } } } else if (!strcmp(var, "HINT")) { - s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL; + int i = 0, ret; + while (i++ < 10 && (ret = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten)) == -EAGAIN) { + ast_channel_unlock(c); + usleep(1); + ast_channel_lock(c); + } + s = ret == -1 ? workspace : NULL; } else if (!strcmp(var, "HINTNAME")) { - s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL; + int i = 0, ret; + while (i++ < 10 && (ret = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten)) == -EAGAIN) { + ast_channel_unlock(c); + usleep(1); + ast_channel_lock(c); + } + s = ret == -1 ? workspace : NULL; } else if (!strcmp(var, "EXTEN")) { s = c->exten; } else if (!strcmp(var, "CONTEXT")) { @@ -1886,14 +1898,17 @@ } /*! \brief ast_hint_extension: Find hint for given extension in context */ -static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten) +static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten, int *err) { - struct ast_exten *e; + struct ast_exten *e = NULL; struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */ - ast_mutex_lock(&conlock); - e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH); - ast_mutex_unlock(&conlock); + if (!ast_mutex_trylock(&conlock)) { + e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH); + ast_mutex_unlock(&conlock); + *err = 0; + } else + *err = EAGAIN; return e; } @@ -1999,10 +2014,15 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char *exten) { struct ast_exten *e; + int err = 0; - e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */ - if (!e) - return -1; /* No hint, return -1 */ + e = ast_hint_extension(c, context, exten, &err); /* Do we have a hint for this extension ? */ + if (!e) { + if (err == EAGAIN) + return -EAGAIN; + else + return -1; /* No hint, return -1 */ + } return ast_extension_state2(e); /* Check all devices in the hint */ } @@ -2059,6 +2079,7 @@ struct ast_hint *hint; struct ast_state_cb *cblist; struct ast_exten *e; + int err = 0; /* If there's no context and extension: add callback to statecbs list */ if (!context && !exten) { @@ -2092,9 +2113,12 @@ return -1; /* This callback type is for only one hint, so get the hint */ - e = ast_hint_extension(NULL, context, exten); + e = ast_hint_extension(NULL, context, exten, &err); if (!e) { - return -1; + if (err == EAGAIN) + return -EAGAIN; + else + return -1; } /* Find the hint in the list of hints */ @@ -2259,7 +2283,8 @@ /*! \brief ast_get_hint: Get hint for channel */ int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten) { - struct ast_exten *e = ast_hint_extension(c, context, exten); + int err = 0; + struct ast_exten *e = ast_hint_extension(c, context, exten, &err); if (e) { if (hint) @@ -2270,7 +2295,8 @@ ast_copy_string(name, tmp, namesize); } return -1; - } + } else if (err == EAGAIN) + return -EAGAIN; return 0; } @@ -3928,7 +3954,7 @@ struct store_hint *this; struct ast_hint *hint; struct ast_exten *exten; - int length; + int length, err = 0; struct ast_state_cb *thiscb, *prevcb; /* it is very important that this function hold the hint list lock _and_ the conlock @@ -3989,7 +4015,17 @@ cannot be restored */ while ((this = AST_LIST_REMOVE_HEAD(&store, list))) { - exten = ast_hint_extension(NULL, this->context, this->exten); +retry_hint: + exten = ast_hint_extension(NULL, this->context, this->exten, &err); + if (err) { + AST_LIST_UNLOCK(&hints); + ast_mutex_unlock(&conlock); + usleep(1); + ast_mutex_lock(&conlock); + AST_LIST_LOCK(&hints); + goto retry_hint; + } + /* Find the hint in the list of hints */ AST_LIST_TRAVERSE(&hints, hint, list) { if (hint->exten == exten)