--- channel.c~ 2006-10-24 12:58:48.000000000 +0100 +++ channel.c 2006-11-17 11:25:49.000000000 +0000 @@ -748,43 +748,58 @@ { const char *msg = prev ? "deadlock" : "initial deadlock"; int retries, done; - struct ast_channel *c; + struct ast_channel *c, *fromc; for (retries = 0; retries < 10; retries++) { ast_mutex_lock(&chlock); - for (c = channels; c; c = c->next) { - if (!prev) { - /* want head of list */ - if (!name && !exten && !uniqueid) + /* If prev is defined, then it specifies the start point of the search, so + * check it exists. If prev is NULL, start from the beginning. + */ + if ( prev ) { + for (c = channels; c; c = c->next) { + if( c == prev ) break; - if (uniqueid) { - if (!strcasecmp(c->uniqueid, uniqueid)) - break; - else + } + /* Could not find prev in the channel list, so stop. */ + if ( !c ) { + ast_mutex_unlock(&chlock); + return NULL; + } + fromc = c; + } else + fromc = channels; + for (c = fromc; c; c = c->next) { + /* Want head of list */ + if (!prev && !name && !exten && !uniqueid ) + break; + + if (uniqueid) { + if (!strcasecmp(c->uniqueid, uniqueid)) + break; + else continue; - } else if (name) { - /* want match by full name */ - if (!namelen) { - if (!strcasecmp(c->name, name)) - break; - else - continue; - } - /* want match by name prefix */ - if (!strncasecmp(c->name, name, namelen)) + } else if (name) { + /* want match by full name */ + if (!namelen) { + if (!strcasecmp(c->name, name)) break; - } else if (exten) { - /* want match by context and exten */ - if (context && (strcasecmp(c->context, context) && - strcasecmp(c->macrocontext, context))) - continue; - /* match by exten */ - if (strcasecmp(c->exten, exten) && - strcasecmp(c->macroexten, exten)) - continue; else - break; + continue; } + /* want match by name prefix */ + if (!strncasecmp(c->name, name, namelen)) + break; + } else if (exten) { + /* want match by context and exten */ + if (context && (strcasecmp(c->context, context) && + strcasecmp(c->macrocontext, context))) + continue; + /* match by exten */ + if (strcasecmp(c->exten, exten) && + strcasecmp(c->macroexten, exten)) + continue; + else + break; } else if (c == prev) { /* found, return c->next */ c = c->next; break; @@ -793,20 +808,33 @@ /* exit if chan not found or mutex acquired successfully */ done = (c == NULL) || (ast_mutex_trylock(&c->lock) == 0); /* this is slightly unsafe, as we _should_ hold the lock to access c->name */ - if (!done && c) + if (!done && c) { ast_log(LOG_DEBUG, "Avoiding %s for '%s'\n", msg, c->name); + /* Doing this here so we know we still have a channel-list lock. + * + * If we are about to give up locking... Are we at the end of the chan list yet? + * If not, then SKIP this channel rather than giving up completely, as this might + * allow us to eventually resolve the deadlock. + * + * NOTE: No point doing this for a get-list-head, uniqueid, or full-name search. + */ + if (retries == 10 && (prev || name || exten ) && !uniqueid && !(name && !namelen)) { + ast_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n", + msg, c, retries); + prev = c; + retries = -1; + } + } ast_mutex_unlock(&chlock); if (done) return c; usleep(1); + } /* - * c is surely not null, but we don't have the lock so cannot - * access c->name + * We don't have the lock so cannot access c->name, and + * we are at the end of the channels list. */ - ast_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n", - msg, c, retries); - return NULL; }