--- apps/app_queue.c 2009-02-06 12:10:44.000000000 +0100 +++ apps/app_queue.c 2009-02-06 12:14:49.000000000 +0100 @@ -119,7 +119,8 @@ QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, - QUEUE_STRATEGY_RRMEMORY + QUEUE_STRATEGY_RRMEMORY, + QUEUE_STRATEGY_LINEAR }; static struct strategy { @@ -132,6 +133,7 @@ { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" }, { QUEUE_STRATEGY_RANDOM, "random" }, { QUEUE_STRATEGY_RRMEMORY, "rrmemory" }, + { QUEUE_STRATEGY_LINEAR, "linear" } }; #define DEFAULT_RETRY 5 @@ -346,6 +348,8 @@ int handled; /*!< Whether our call was handled */ int pending; /*!< Non-zero if we are attempting to call a member */ int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */ + int linpos; /*!< If using linear strategy, position in queue */ + int linwrapped; /*!< Is the linpos wrapped? */ time_t start; /*!< When we started holding */ time_t expire; /*!< When this entry should expire (time out of queue) */ struct ast_channel *chan; /*!< Our channel */ @@ -841,8 +845,16 @@ q->eventwhencalled = 0; q->weight = 0; q->timeoutrestart = 0; - if (!q->members) - q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); + if(!q->members) { + if(q->strategy == QUEUE_STRATEGY_LINEAR) { + /*linear strategy depnds on order, so only use one hash bucket */ + q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); + ast_log(LOG_NOTICE, "Using linear strategy.\n"); + } else { + q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); + ast_log(LOG_NOTICE, "Strategy is NOT linear.\n"); + } + } q->membercount = 0; q->found = 1; ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); @@ -1239,6 +1251,30 @@ ast_mutex_lock(&q->lock); clear_queue(q); q->realtime = 1; + + /* manwe & saghul FTW!!*/ + + /*Before we initialize the queue, we need to set the strategy, so that linear strategy + * will allocate the members properly + */ + struct ast_variable *tmpvar = NULL; + for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { + if (!strcasecmp(tmpvar->name, "strategy")) { + q->strategy = strat2int(tmpvar->value); + if (q->strategy < 0) { + ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", + tmpvar->value, q->name); + q->strategy = QUEUE_STRATEGY_RINGALL; + } + break; + } + } + /* We traversed all variables and didn't find a strategy */ + if (!tmpvar) + q->strategy = QUEUE_STRATEGY_RINGALL; + + /* /manwe & saghul FTW!! */ + AST_LIST_INSERT_HEAD(&queues, q, list); } init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ @@ -1857,6 +1893,8 @@ qe->parent->rrpos++; ast_mutex_unlock(&qe->parent->lock); + qe->linpos++; + (*busies)++; return 0; } @@ -1987,7 +2025,7 @@ return ret; } -static int store_next(struct queue_ent *qe, struct callattempt *outgoing) +static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing) { struct callattempt *best = find_best(outgoing); @@ -2011,6 +2049,30 @@ return 0; } +static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing) +{ + struct callattempt *best = find_best(outgoing); + + if (best) { + /* Ring just the best channel */ + if (option_debug) + ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); + qe->linpos = best->metric % 1000; + } else { + /* Just increment rrpos */ + if (qe->linwrapped) { + /* No more channels, start over */ + qe->linpos = 0; + } else { + /* Prioritize next entry */ + qe->linpos++; + } + } + qe->linwrapped = 0; + + return 0; +} + static int say_periodic_announcement(struct queue_ent *qe) { int res = 0; @@ -2553,6 +2615,17 @@ } tmp->metric += mem->penalty * 1000000; break; + case QUEUE_STRATEGY_LINEAR: + if (pos < qe->linpos) { + tmp->metric = 1000 + pos; + } else { + if (pos > qe->linpos) + /* Indicate there is another priority */ + qe->linwrapped = 1; + tmp->metric = pos; + } + tmp->metric += mem->penalty * 1000000; + break; case QUEUE_STRATEGY_RANDOM: tmp->metric = ast_random() % 1000; tmp->metric += mem->penalty * 1000000; @@ -2774,7 +2847,7 @@ ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); break; case 'n': - if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) + if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR) (*tries)++; else *tries = qe->parent->membercount; @@ -2916,7 +2989,10 @@ ast_channel_unlock(qe->chan); ast_mutex_lock(&qe->parent->lock); if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { - store_next(qe, outgoing); + store_next_rr(qe, outgoing); + } + if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { + store_next_lin(qe, outgoing); } ast_mutex_unlock(&qe->parent->lock); peer = lpeer ? lpeer->chan : NULL; @@ -4351,6 +4427,23 @@ ast_mutex_unlock(&q->lock); continue; } + /* manwe & saghul FTW!! */ + + /* Due to the fact that the "linear" strategy will have a different allocation + * scheme for queue members, we must devise the queue's strategy before other initializations + */ + const char *tmpvar = NULL; + if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) { + q->strategy = strat2int(tmpvar); + if (q->strategy < 0) { + ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", + tmpvar, q->name); + q->strategy = QUEUE_STRATEGY_RINGALL; + } + } else + q->strategy = QUEUE_STRATEGY_RINGALL; + /* /manwe & saghul FTW!! */ + /* Re-initialize the queue, and clear statistics */ init_queue(q); clear_queue(q);