All pastes #25437 Raw Edit

Miscellany

public text v1 · immutable
#25437 ·published 2005-10-14 00:25 UTC
rendered paste body
/*
 * Helper function to find channels. It supports these modes:
 *
 * prev != NULL : get channel next in list after prev
 * name != NULL : get channel with matching name
 * name != NULL && namelen != 0 : get channel whose name starts with prefix
 * exten != NULL : get channel whose exten or macroexten matches
 * context != NULL && exten != NULL : get channel whose context or macrocontext
 *      
 * It returns with the channel's lock held. If getting the individual lock fails,
 * unlock and retry quickly up to 10 times, then give up.
 *
 * 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 opbx_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 opbx_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 opbx_channel *channel_find_locked(const struct opbx_channel *prev,
                                               const char *name, const int namelen,
                                               const char *context, const char *exten)
{
        const char *msg = prev ? "deadlock" : "initial deadlock";
        int retries, done;
        struct opbx_channel *c;

        for (retries = 0; retries < 10; retries++) {
                opbx_mutex_lock(&chlock);
                for (c = channels; c; c = c->next) {
                        if (!prev) {
                                /* want head of list */
                                if (!name && !exten)
                                        break;
                                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))
                                                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;
                        }
                }
                /* exit if chan not found or mutex acquired successfully */
                done = (c == NULL) || (opbx_mutex_trylock(&c->lock) == 0);
                /* this is slightly unsafe, as we _should_ hold the lock to access c->name */
                if (!done && c)
                        opbx_log(LOG_DEBUG, "Avoiding %s for '%s'\n", msg, c->name);
                opbx_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
         */
        opbx_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n",
                msg, c, retries);

        return NULL;
}