diff --git a/apps/app_queue.c b/apps/app_queue.c index 260745d65f40e1b2cca99038df0837cb72c7688b..1acf5bb430a6bf589d71ca8290421876775d56d8 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -2268,11 +2268,56 @@ static void hangupcalls(struct callattempt *outgoing, struct ast_channel *except } } -/*! - * \brief traverse all defined queues which have calls waiting and contain this member - * \retval 0 if no other queue has precedence (higher weight) - * \retval 1 if found -*/ +/*! + * \brief Get the number of members available to accept a call. + * + * \note The queue passed in should be locked prior to this function call + * + * \param[in] q The queue for which we are couting the number of available members + * \return Return the number of available members in queue q + */ +static int num_available_members(struct call_queue *q) +{ + struct member *mem; + int avl = 0; + struct ao2_iterator mem_iter; + + mem_iter = ao2_iterator_init(q->members, 0); + while ((mem = ao2_iterator_next(&mem_iter))) { + switch (mem->status) { + case AST_DEVICE_INUSE: + if (!q->ringinuse) + break; + /* else fall through */ + case AST_DEVICE_NOT_INUSE: + case AST_DEVICE_UNKNOWN: + if (!mem->paused) { + avl++; + } + break; + } + ao2_ref(mem, -1); + + /* If autofill is not enabled or if the queue's strategy is ringall, then + * we really don't care about the number of available members so much as we + * do that there is at least one available. + * + * In fact, we purposely will return from this function stating that only + * one member is available if either of those conditions hold. That way, + * functions which determine what action to take based on the number of available + * members will operate properly. The reasoning is that even if multiple + * members are available, only the head caller can actually be serviced. + */ + if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { + break; + } + } + + return avl; +} + +/* traverse all defined queues which have calls waiting and contain this member + return 0 if no other queue has precedence (higher weight) or 1 if found */ static int compare_weight(struct call_queue *rq, struct member *member) { struct call_queue *q; @@ -2292,7 +2337,7 @@ static int compare_weight(struct call_queue *rq, struct member *member) if (q->count && q->members) { if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); - if (q->weight > rq->weight) { + if (q->weight > rq->weight && q->count >= num_available_members(q)) { ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count); found = 1; } @@ -2981,78 +3026,44 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte /*! * \brief Check if we should start attempting to call queue members. * - * The behavior of this function is dependent first on whether autofill is enabled - * and second on whether the ring strategy is ringall. If autofill is not enabled, - * then return true if we're the head of the queue. If autofill is enabled, then - * we count the available members and see if the number of available members is enough - * that given our position in the queue, we would theoretically be able to connect to - * one of those available members + * A simple process, really. Count the number of members who are available + * to take our call and then see if we are in a position in the queue at + * which a member could accept our call. + * + * \param[in] qe The caller who wants to know if it is his turn + * \retval 0 It is not our turn + * \retval 1 It is our turn */ static int is_our_turn(struct queue_ent *qe) { struct queue_ent *ch; - struct member *cur; - int avl = 0; - int idx = 0; int res; + int avl; + int idx = 0; + /* This needs a lock. How many members are available to be served? */ + ao2_lock(qe->parent); - if (!qe->parent->autofill) { - /* Atomically read the parent head -- does not need a lock */ - ch = qe->parent->head; - /* If we are now at the top of the head, break out */ - if (ch == qe) { - ast_debug(1, "It's our turn (%s).\n", qe->chan->name); - res = 1; - } else { - ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); - res = 0; - } + avl = num_available_members(qe->parent); - } else { - /* This needs a lock. How many members are available to be served? */ - ao2_lock(qe->parent); - - ch = qe->parent->head; - - if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { - ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n"); - avl = 1; - } else { - struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0); - while ((cur = ao2_iterator_next(&mem_iter))) { - switch (cur->status) { - case AST_DEVICE_INUSE: - if (!qe->parent->ringinuse) - break; - /* else fall through */ - case AST_DEVICE_NOT_INUSE: - case AST_DEVICE_UNKNOWN: - if (!cur->paused) - avl++; - break; - } - ao2_ref(cur, -1); - } - } + ch = qe->parent->head; - ast_debug(1, "There are %d available members.\n", avl); - - while ((idx < avl) && (ch) && (ch != qe)) { - if (!ch->pending) - idx++; - ch = ch->next; - } - - /* If the queue entry is within avl [the number of available members] calls from the top ... */ - if (ch && idx < avl) { - ast_debug(1, "It's our turn (%s).\n", qe->chan->name); - res = 1; - } else { - ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); - res = 0; - } - - ao2_unlock(qe->parent); + ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); + + while ((idx < avl) && (ch) && (ch != qe)) { + if (!ch->pending) + idx++; + ch = ch->next; + } + + ao2_unlock(qe->parent); + + /* If the queue entry is within avl [the number of available members] calls from the top ... */ + if (ch && idx < avl) { + ast_debug(1, "It's our turn (%s).\n", qe->chan->name); + res = 1; + } else { + ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); + res = 0; } return res;