Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 378071) +++ apps/app_queue.c (working copy) @@ -1046,6 +1046,7 @@ struct call_queue *lastqueue; /*!< Last queue we received a call */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int delme:1; /*!< Flag to delete entry on reload */ + unsigned int call_pending:1; /*!< TRUE if the Q is attempting to place a call to the member. */ char rt_uniqueid[80]; /*!< Unique id of realtime member entry */ }; @@ -3079,6 +3080,83 @@ return vars; } +/*! + * \internal + * \brief Check if the member status is available. + * + * \param status Member status to check if available. + * + * \retval non-zero if the member status is available. + */ +static int member_status_available(int status) +{ + return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN; +} + +/*! + * \internal + * \brief Determine if can ring a queue entry. + * + * \param qe Queue entry to check. + * \param call Member call attempt. + * + * \retval non-zero if an entry can be called. + */ +static int can_ring_entry(struct queue_ent *qe, struct callattempt *call) +{ + if (call->member->paused) { + ast_debug(1, "%s paused, can't receive call\n", call->interface); + return 0; + } + + if (!qe->parent->ringinuse && !member_status_available(call->member->status)) { + ast_debug(1, "%s not available, can't receive call\n", call->interface); + return 0; + } + + if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime)) + || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) { + ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", + (call->lastqueue ? call->lastqueue->name : qe->parent->name), + call->interface); + return 0; + } + + if (use_weight && compare_weight(qe->parent, call->member)) { + ast_debug(1, "Priority queue delaying call to %s:%s\n", + qe->parent->name, call->interface); + return 0; + } + + if (!qe->parent->ringinuse) { + ao2_lock(call->member); + if (call->member->call_pending) { + ao2_unlock(call->member); + ast_debug(1, "%s has another call pending, can't receive call\n", + call->interface); + return 0; + } + call->member->call_pending = 1; + ao2_unlock(call->member); + + /* + * The queue member is available. Get current status to be sure + * because the device state and extension state callbacks may + * not have updated the status yet. + */ + if (!member_status_available(get_queue_member_status(call->member))) { + ast_debug(1, "%s actually not available, can't receive call\n", + call->interface); + ao2_lock(call->member); + call->member->call_pending = 0; + ao2_unlock(call->member); + return 0; + } + } + + return 1; +} + /*! * \brief Part 2 of ring_one * @@ -3102,43 +3180,16 @@ const char *macrocontext, *macroexten; /* on entry here, we know that tmp->chan == NULL */ - if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || - (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { - ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", - (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); - if (qe->chan->cdr) + if (!can_ring_entry(qe, tmp)) { + if (qe->chan->cdr) { ast_cdr_busy(qe->chan->cdr); + } tmp->stillgoing = 0; - (*busies)++; + ++*busies; return 0; } + ast_assert(qe->parent->ringinuse || tmp->member->call_pending); - if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { - ast_debug(1, "%s in use, can't receive call\n", tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - (*busies)++; - return 0; - } - - if (tmp->member->paused) { - ast_debug(1, "%s paused, can't receive call\n", tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - (*busies)++; - return 0; - } - if (use_weight && compare_weight(qe->parent,tmp->member)) { - ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - (*busies)++; - return 0; - } - ast_copy_string(tech, tmp->interface, sizeof(tech)); if ((location = strchr(tech, '/'))) *location++ = '\0'; @@ -3148,17 +3199,20 @@ /* Request the peer */ tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); if (!tmp->chan) { /* If we can't, just go on to the next call */ - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - ao2_lock(qe->parent); - update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); qe->parent->rrpos++; qe->linpos++; ao2_unlock(qe->parent); - (*busies)++; + ao2_lock(tmp->member); + tmp->member->call_pending = 0; + ao2_unlock(tmp->member); + + if (qe->chan->cdr) { + ast_cdr_busy(qe->chan->cdr); + } + tmp->stillgoing = 0; + ++*busies; return 0; } @@ -3233,10 +3287,14 @@ /* Again, keep going even if there's an error */ ast_verb(3, "Couldn't call %s\n", tmp->interface); do_hang(tmp); - (*busies)++; - update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); + ao2_lock(tmp->member); + tmp->member->call_pending = 0; + ao2_unlock(tmp->member); + ++*busies; return 0; - } else if (qe->parent->eventwhencalled) { + } + + if (qe->parent->eventwhencalled) { char vars[2048]; ast_channel_lock_both(tmp->chan, qe->chan); @@ -3270,7 +3328,9 @@ ast_verb(3, "Called %s\n", tmp->interface); } - update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); + ao2_lock(tmp->member); + tmp->member->call_pending = 0; + ao2_unlock(tmp->member); return 1; } @@ -7472,7 +7532,7 @@ while ((mem = ao2_iterator_next(&mem_iter))) { if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { ++qmemcount; - if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) { + if (member_status_available(mem->status) && !mem->paused) { ++qmemavail; } }