diff --git a/apps/app_queue.c b/apps/app_queue.c index 0474eb8b5a..53f4225b9c 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -1618,6 +1618,9 @@ static int negative_penalty_invalid; /*! \brief queues.conf [general] option */ static int log_membername_as_agent; +/*! \brief queues.conf [general] option */ +static int force_longest_waiting_caller; + /*! \brief name of the ringinuse field in the realtime database */ static char *realtime_ringinuse_field; @@ -4575,6 +4578,56 @@ static int compare_weight(struct call_queue *rq, struct member *member) return found; } +static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member) +{ + struct call_queue *q; + struct member *mem; + int is_longest_waiting = 1; + struct ao2_iterator queue_iter; + struct queue_ent *ch; + + queue_iter = ao2_iterator_init(queues, 0); + while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { + if (q == caller->parent) { /* don't check myself, could deadlock */ + queue_t_unref(q, "Done with iterator"); + continue; + } + ao2_lock(q); + /* + * If the other queue has equal weight, see if we should let that handle + * their call first. If weights are not equal, compare_weights will step in. + */ + if (q->weight == caller->parent->weight && q->count && q->members) { + if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { + ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name); + + /* Does this queue have a caller that's been waiting longer? */ + ch = q->head; + while (ch) { + /* If ch->pending, the other call (which may be waiting for a longer period of time), + * is already ringing at another agent. Ignore such callers; otherwise, all agents + * will be unused until the first caller is picked up. + */ + if (ch->start < caller->start && !ch->pending) { + ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n", + q->name, ch->pos, ch->start, caller->start); + is_longest_waiting = 0; + break; + } + ch = ch->next; + } + } + } + ao2_unlock(q); + queue_t_unref(q, "Done with iterator"); + if (!is_longest_waiting) { + break; + } + } + ao2_iterator_destroy(&queue_iter); + return is_longest_waiting; +} + /*! \brief common hangup actions */ static void do_hang(struct callattempt *o) { @@ -4639,6 +4692,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call) return 0; } + if (force_longest_waiting_caller && !is_longest_waiting_caller(qe, memberp)) { + ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n", + qe->parent->name, call->interface); + return 0; + } + if (!memberp->ringinuse) { struct member *mem; @@ -9520,6 +9579,7 @@ static void queue_reset_global_params(void) shared_lastcall = 0; negative_penalty_invalid = 0; log_membername_as_agent = 0; + force_longest_waiting_caller = 0; } /*! Set the global queue parameters as defined in the "general" section of queues.conf */ @@ -9545,6 +9605,9 @@ static void queue_set_global_params(struct ast_config *cfg) if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) { log_membername_as_agent = ast_true(general_val); } + if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) { + force_longest_waiting_caller = ast_true(general_val); + } } /*! \brief reload information pertaining to a single member diff --git a/configs/samples/queues.conf.sample b/configs/samples/queues.conf.sample index fbb5653203..436cdf13ac 100644 --- a/configs/samples/queues.conf.sample +++ b/configs/samples/queues.conf.sample @@ -58,6 +58,14 @@ monitor-type = MixMonitor ; ;log_membername_as_agent = no ; +; force_longest_waiting_caller will cause app_queue to make sure callers are offered +; in order (longest waiting first), even for callers across multiple queues. +; Before a call is offered to an agent, an additional check is made to see if the agent +; is a member of another queue with a call that's been waiting longer. If so, the current +; call is not offered to the agent. The default value is 'no'. +; +;force_longest_waiting_caller = no +; ;[markq] ; ; A sample call queue