/*
 * Helper function to return the channel after prev, or the one matching name,
 * with the individual lock held. If getting the individual lock fails,
 * unlock and retry quickly up to 10 times, then give up.
 * 
 * This is only an internal interface, applications should use
 * ast_channel_walk_locked(prev) or ast_get_channel_by_name_locked(name).
 *
 * XXX Note that this code has cost O(N) because of the need to verify
 * that the object is still on the global list.
 *
 * XXX also note that accessing fields (e.g. c->name in ast_log())
 * can only be done with the lock held or someone could delete the
 * object while we work on it. This causes some ugliness in the code.
 * Note that removing the first ast_log() may be harmful, as it would
 * shorten the retry period and possibly cause failures.
 * We should definitely go for a better scheme that is deadlock-free.
 */
static struct ast_channel *ast_channel_find_locked(struct ast_channel *prev,
	const char *name)
{
	/* The original code had a variant msg depending on 'prev' */
	char *msg = prev ? "initial deadlock" : "deadlock";
	int retries, done;
	struct ast_channel *c;

	for (retries =0; retries < 10; retries++) {
		ast_mutex_lock(&chlock);
		for (c = channels; c; c = c->next) {
			if (prev == NULL) {
				/* want either head of list or match by name */
				if (name == NULL || !strcasecmp(name, c->name))
					break;
			} else if (c == prev) { /* found, return c->next */
				c = c->next;
				break;
			}
		}
		/* exit if chan not found or mutex acquired successfully */
		done = (c == NULL) || (ast_mutex_trylock(&c->lock) == 0);
		/* XXX we need to hold the lock to access c->name */
		if (!done)
			ast_log(LOG_DEBUG, "Avoiding %s for '%s'\n", msg, c->name);
		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
	 */
	ast_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n",
			msg, c, retries);
	return NULL;
}

struct ast_channel *ast_channel_walk_locked(struct ast_channel *prev)
{
    return ast_channel_find_locked(prev, NULL);
}

struct ast_channel *ast_get_channel_by_name_locked(char *channame)
{
    return ast_channel_find_locked(NULL, channame);
}
