[asterisk-commits] irroot: branch irroot/asterisk-trunk-quack-queue r343535 - /team/irroot/aster...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Nov 7 13:13:59 CST 2011
Author: irroot
Date: Mon Nov 7 13:13:55 2011
New Revision: 343535
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=343535
Log:
Remove rules linked lists
move time_t to timeval to use ast_time API
split call_queue into mutable and non mutable bits
additional ao2 work
reconfigure of queue no longer changes it for active calls
refactoring and styling of code
Modified:
team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c
Modified: team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c?view=diff&rev=343535&r1=343534&r2=343535
==============================================================================
--- team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c (original)
+++ team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c Mon Nov 7 13:13:55 2011
@@ -111,6 +111,7 @@
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"
+#include "asterisk/time.h"
/*!
* \par Please read before modifying this file.
@@ -1030,23 +1031,12 @@
* use it not only for keeping track of what is in use but
* also for keeping track of who we're dialing.
*
- * There are two "links" defined in this structure, q_next and call_next.
- * q_next links ALL defined callattempt structures into a linked list. call_next is
- * a link which allows for a subset of the callattempts to be traversed. This subset
- * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
- * also is helpful so that queue logs are always accurate in the case where a call to
- * a member times out, especially if using the ringall strategy.
*/
struct callattempt {
- struct callattempt *q_next;
- struct callattempt *call_next;
struct ast_channel *chan;
- char interface[256];
int stillgoing;
int metric;
- time_t lastcall;
- int lastwrapup;
struct member *member;
/*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
struct ast_party_connected_line connected;
@@ -1058,21 +1048,17 @@
struct ast_aoc_decoded *aoc_s_rate_list;
};
-
struct queue_ent {
struct call_queue *parent; /*!< What queue is our parent */
- char moh[80]; /*!< Name of musiconhold to be used */
- char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
- char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
int pos; /*!< Where we are in the queue */
int prio; /*!< Our priority */
int last_pos_said; /*!< Last position we told the user */
int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
- time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
+ struct timeval last_periodic_announce_time; /*!< The last time we played a periodic announcement */
int last_periodic_announce_sound; /*!< The last periodic announcement we made */
- time_t last_pos; /*!< Last time we told the user their position */
+ struct timeval last_pos; /*!< Last time we told the user their position */
int opos; /*!< Where we started in the queue */
int handled; /*!< Whether our call was handled */
int pending; /*!< Non-zero if we are attempting to call a member */
@@ -1080,12 +1066,13 @@
int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
int linpos; /*!< If using linear strategy, what position are we at? */
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 timeval start; /*!< When we started holding */
+ struct timeval expire; /*!< When this entry should expire (time out of queue) */
int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
+ struct ao2_container *attempts; /*!< Container holding all call attempts*/
struct ast_channel *chan; /*!< Our channel */
struct rule_list *rules; /*!< Pointer holding the ref for the queue penalty rules */
- struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
+ struct penalty_rule *pr; /*!< Active penalty rule */
AST_LIST_ENTRY(queue_ent) next; /*!< The next queue entry */
};
@@ -1097,19 +1084,21 @@
};
struct member {
- char interface[80]; /*!< Technology/Location to dial to reach this member*/
- char membername[80]; /*!< Member name to use in queue logs */
- int penalty; /*!< Are we a last resort? */
- int calls; /*!< Number of calls serviced by this member */
- time_t lastcall; /*!< When last successful call was hungup */
- int lastwrapup; /*!< Last wrapuptime */
- unsigned int realtime:1; /*!< Is this member realtime? */
- unsigned int paused:1; /*!< Are we paused (not accepting calls)? */
- unsigned int dead:1; /*!< Used to detect members deleted in realtime */
- unsigned int dynamic:1; /*!< Are we dynamically added? */
- unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
- char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
- struct mem_state *device; /*!< Device information */
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(interface); /*!< Technology/Location to dial to reach this member*/
+ AST_STRING_FIELD(membername); /*!< Member name to use in queue logs */
+ AST_STRING_FIELD(rt_uniqueid); /*!< Unique id of realtime member entry */
+ );
+ int penalty; /*!< Are we a last resort? */
+ int calls; /*!< Number of calls serviced by this member */
+ struct timeval lastcall; /*!< When last successful call was hungup */
+ int lastwrapup; /*!< Last wrapuptime */
+ unsigned int realtime:1; /*!< Is this member realtime? */
+ unsigned int paused:1; /*!< Are we paused (not accepting calls)? */
+ unsigned int dead:1; /*!< Used to detect members deleted in realtime */
+ unsigned int dynamic:1; /*!< Are we dynamically added? */
+ unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
+ struct mem_state *device; /*!< Device information */
};
enum empty_conditions {
@@ -1192,7 +1181,6 @@
unsigned int setqueuevar:1;
unsigned int setqueueentryvar:1;
unsigned int reportholdtime:1;
- unsigned int wrapped:1;
unsigned int timeoutrestart:1;
unsigned int announceholdtime:2;
unsigned int announceposition:3;
@@ -1211,16 +1199,10 @@
int periodicannouncefrequency; /*!< How often to play periodic announcement */
int numperiodicannounce; /*!< The number of periodic announcements configured */
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
+ int servicelevel; /*!< seconds setting for servicelevel*/
int roundingseconds; /*!< How many seconds do we round to? */
- int holdtime; /*!< Current avg holdtime, based on an exponential average */
- int talktime; /*!< Current avg talktime, based on the same exponential average */
- int callscompleted; /*!< Number of queue calls completed */
- int callsabandoned; /*!< Number of queue calls abandoned */
- int servicelevel; /*!< seconds setting for servicelevel*/
- int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
char monfmt[8]; /*!< Format to use when recording calls */
int montype; /*!< Monitor type Monitor vs. MixMonitor */
- int count; /*!< How many entries */
int maxlen; /*!< Max number of entries */
int wrapuptime; /*!< Wrapup Time */
int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
@@ -1233,21 +1215,34 @@
int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
/* Queue strategy things */
- int rrpos; /*!< Round Robin - position */
int memberdelay; /*!< Seconds to delay connecting member to caller */
int autofill; /*!< Ignore the head call status and ring an available agent */
+ struct queue_data *data; /*!< Queue statistics */
+};
+
+struct queue_data {
+ int qhash; /*!< Hash for queue */
+ unsigned int wrapped:1;
+ int count; /*!< How many entries */
+ int holdtime; /*!< Current avg holdtime, based on an exponential average */
+ int talktime; /*!< Current avg talktime, based on the same exponential average */
+ int callscompleted; /*!< Number of queue calls completed */
+ int callsabandoned; /*!< Number of queue calls abandoned */
+ int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
+ int rrpos; /*!< Round Robin - position */
+ AST_LIST_HEAD(, queue_ent) *head; /*!< Head of the list of callers */
struct ao2_container *members; /*!< Head of the list of members */
- AST_LIST_HEAD(, queue_ent) *head; /*!< Head of the list of callers */
};
struct rule_list {
char name[80];
- AST_LIST_HEAD_NOLOCK(, penalty_rule) *rules;
+ struct ao2_container *rules;
};
static struct ao2_container *queues;
static struct ao2_container *devices;
static struct ao2_container *rules;
+static struct ao2_container *qdata;
static int do_set_member_penalty_paused(struct call_queue *q, struct member *mem, int pause, int value, const char *reason);
static void pm_load_member_config(struct call_queue *q);
@@ -1340,41 +1335,64 @@
return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
}
+/*!
+ * \brief ao2 callback to calculate hash of a queue by name
+ */
+static int qdata_hash_cb(const void *obj, const int flags)
+{
+ const struct queue_data *d = obj;
+ const char *qname = obj;
+ int qhash = (flags & OBJ_KEY) ? ast_str_case_hash(qname) : d->qhash;
+
+ return qhash;
+}
+
+
+/*!
+ * \brief ao2 callback to find queue by name
+ * \note this is the default function used by ao2_find
+ */
+static int qdata_cmp_cb(void *obj, void *arg, int flags)
+{
+ const struct queue_data *d = obj, *d2 = arg;
+ const char *name = arg;
+ int qhash = (flags & OBJ_POINTER) ? d2->qhash : ast_str_case_hash(name);
+
+ return (d->qhash == qhash) ? CMP_MATCH | CMP_STOP : 0;
+}
+
/*! \brief Set channel variables of queue */
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
{
char interfacevar[256]="";
float sl = 0;
-
- ao2_lock(q);
+ struct queue_data *data = q->data;
if (q->setqueuevar) {
sl = 0;
- if (q->callscompleted > 0)
- sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
+ ao2_lock(data);
+ if (data->callscompleted > 0) {
+ sl = 100 * ((float) data->callscompletedinsl / (float) data->callscompleted);
+ }
snprintf(interfacevar, sizeof(interfacevar),
"QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
- q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
-
- ao2_unlock(q);
-
- pbx_builtin_setvar_multiple(chan, interfacevar);
- } else {
- ao2_unlock(q);
+ q->name, q->maxlen, int2strat(q->strategy), data->count, data->holdtime, data->talktime,
+ data->callscompleted, data->callsabandoned, q->servicelevel, sl);
+
+ pbx_builtin_setvar_multiple(chan, interfacevar);
+ ao2_unlock(data);
}
}
/*! \brief Insert the 'new' callattempt entry after the 'prev' entry of queue 'q' */
-static inline void insert_entry(struct call_queue *q, struct queue_ent *new, int *pos)
-{
- /* every queue_ent must have a reference to it's parent call_queue, this
- * reference does not go away until the end of the queue_ent's life, meaning
- * that even when the queue_ent leaves the call_queue this ref must remain. */
- ao2_ref(q, 1);
- new->parent = q;
+static inline void insert_entry(struct queue_ent *new, int *pos)
+{
new->pos = ++(*pos);
new->opos = *pos;
+ ao2_lock(new->parent->data);
+ new->parent->data->count++;
+ ao2_unlock(new->parent->data);
}
/*! \brief return the device state for a member*/
@@ -1400,26 +1418,28 @@
* is available, the function immediately returns 0. If no members are available,
* then -1 is returned.
*/
-static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int join)
+static int get_member_status(const struct queue_ent *qe, int join)
{
struct member *member;
struct ao2_iterator mem_iter;
+ struct call_queue *q = qe->parent;
+ int max_penalty = qe->max_penalty;
+ int min_penalty = qe->min_penalty;
enum empty_conditions conditions;
- ao2_lock(q);
conditions = (join) ? q->joinempty : q->leavewhenempty;
- ao2_unlock(q);
if (!conditions) {
return 0;
}
- mem_iter = ao2_iterator_init(q->members, AO2_ITERATOR_DONTLOCK);
- ao2_lock(q->members);
+ mem_iter = ao2_iterator_init(q->data->members, 0);
while((member = ao2_iterator_next(&mem_iter))) {
+ ao2_lock(member);
if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
if (conditions & QUEUE_EMPTY_PENALTY) {
ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
+ ao2_unlock(member);
ao2_ref(member, -1);
continue;
}
@@ -1461,21 +1481,21 @@
if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
break;
- } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && member->lastwrapup && (time(NULL) - member->lastwrapup < member->lastcall)) {
- ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), member->lastwrapup);
+ } else if ((conditions & QUEUE_EMPTY_WRAPUP) && !ast_tvzero(member->lastcall) && member->lastwrapup && (ast_tvdiff_sec(ast_tvnow(), member->lastcall) <= member->lastwrapup)) {
+ ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int)ast_tvdiff_sec(ast_tvnow(), member->lastcall), member->lastwrapup);
break;
} else {
ast_debug(4, "%s is available.\n", member->membername);
+ ao2_unlock(member);
ao2_ref(member, -1);
- ao2_unlock(q->members);
ao2_iterator_destroy(&mem_iter);
return 0;
}
break;
}
+ ao2_unlock(member);
ao2_ref(member, -1);
}
- ao2_unlock(q->members);
ao2_iterator_destroy(&mem_iter);
return -1;
}
@@ -1506,14 +1526,11 @@
qiter = ao2_iterator_init(queues, 0);
while ((q = ao2_iterator_next(&qiter))) {
- ao2_lock(q);
if (q->maskmemberstatus) {
- ao2_unlock(q);
ao2_ref(q, -1);
continue;
}
- ao2_unlock(q);
- miter = ao2_iterator_init(q->members, 0);
+ miter = ao2_iterator_init(q->data->members, 0);
while((m = ao2_iterator_next(&miter))) {
ao2_lock(m);
if (m->device != s) {
@@ -1534,7 +1551,7 @@
"Status: %d\r\n"
"Paused: %d\r\n",
q->name, m->interface, m->membername, s->state_interface, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
- m->penalty, m->calls, (int)m->lastcall, s->status, m->paused
+ m->penalty, m->calls, (int)m->lastcall.tv_sec, s->status, m->paused
);
ao2_unlock(s);
ao2_unlock(m);
@@ -1772,7 +1789,7 @@
const struct member *mem2 = arg;
const char *uniqueid = (flags & OBJ_POINTER) ? mem2->rt_uniqueid : arg;
- if (mem1->realtime && !strcasecmp(mem1->rt_uniqueid, uniqueid)) {
+ if (mem1->realtime && !mem1->dead && !strcasecmp(mem1->rt_uniqueid, uniqueid)) {
return CMP_MATCH | CMP_STOP;
}
@@ -1798,15 +1815,17 @@
*/
static int kill_realtime_dead_members(void *obj, void *arg, void *data, int flags)
{
- const struct member *m = obj;
+ struct member *m = obj;
const struct call_queue *q = data;
if (m->dead && m->realtime) {
+ ao2_lock(m);
if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
} else {
ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
}
+ ao2_unlock(m);
return CMP_MATCH;
}
return 0;
@@ -1817,8 +1836,10 @@
{
struct member *mem = obj1;
+ ao2_lock(mem);
mem->calls = 0;
- mem->lastcall = 0;
+ mem->lastcall = ast_tv(0, 0);
+ ao2_unlock(mem);
return 0;
}
@@ -1859,6 +1880,36 @@
}
/*!
+ * \brief ao2 callback to calculate hash of a penalty rule by time
+ */
+static int penalty_hash_cb(const void *obj, const int flags)
+{
+ const struct penalty_rule *pr = obj;
+ const int *tin = obj;
+ int time = (flags & OBJ_KEY) ? *tin : pr->time;
+
+ return time;
+}
+
+/*! \brief find the best penalty rule for duration */
+static int get_best_rule_cb(void *obj, void *arg, void *data, int flags)
+{
+ int *time = arg;
+ struct penalty_rule *cur = obj;
+ struct penalty_rule **din = data, *best = *din;
+
+ if ((cur->time >= *time) && (!best || (best && (cur->time < best->time)))) {
+ if (best) {
+ ao2_ref(best, -1);
+ }
+ ao2_ref(cur, 1);
+ *din = cur;
+ return CMP_MATCH;
+ }
+ return 0;
+}
+
+/*!
* \brief ao2 callback to find rule by name
* \note this is the default function used by ao2_find
*/
@@ -1869,6 +1920,47 @@
const char *name = (flags & OBJ_POINTER) ? rl2->name : arg;
return !strcasecmp(rl->name, name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*!
+ * \brief ao2 callback to calculate hash of a callattempt by member interface
+ */
+static int callattempt_hash_fn(const void *obj, const int flags)
+{
+ const struct callattempt *c = obj;
+ const struct member *mem = c->member;
+ const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
+ const char *chname = strchr(interface, '/');
+ int ret = 0, i;
+
+ if (!chname) {
+ chname = interface;
+ }
+ for (i = 0; i < 5 && chname[i]; i++) {
+ ret += compress_char(chname[i]) << (i * 6);
+ }
+ return ret;
+}
+
+
+/*!
+ * \brief ao2 callback to obtain the callattempt with best metric
+ */
+static int get_best_metric_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct callattempt *cur = obj;
+ struct callattempt **din = data, *best = *din;
+
+ if (cur->stillgoing && !cur->chan &&
+ (!best || cur->metric < best->metric)) {
+ if (best) {
+ ao2_ref(best, -1);
+ }
+ ao2_ref(cur, 1);
+ *din = cur;
+ return CMP_MATCH;
+ }
+ return 0;
}
/*!
@@ -1950,14 +2042,13 @@
* \retval 0 on success
* \note Call this with the rule_lists locked
*/
-static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
+static int insert_penaltychange(struct ao2_container *rules, const char *content, const int linenum)
{
char *timestr, *maxstr, *minstr, *contentdup;
- struct penalty_rule *rule = NULL, *rule_iter;
- struct rule_list *rl_iter;
- int penaltychangetime, inserted = 0;
-
- if (!(rule = ast_calloc(1, sizeof(*rule)))) {
+ struct penalty_rule *rule = NULL;
+ int penaltychangetime;
+
+ if (!(rule = ao2_alloc(sizeof(*rule), NULL))) {
return -1;
}
@@ -1965,7 +2056,7 @@
if (!(maxstr = strchr(contentdup, ','))) {
ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
- ast_free(rule);
+ ao2_ref(rule, -1);
return -1;
}
@@ -1974,7 +2065,7 @@
if ((penaltychangetime = atoi(timestr)) < 0) {
ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
- ast_free(rule);
+ ao2_ref(rule, -1);
return -1;
}
@@ -1999,22 +2090,8 @@
rule->min_relative = 1;
/*We have the rule made, now we need to insert it where it belongs*/
- if ((rl_iter = ao2_find(rules, list_name, OBJ_KEY))) {
- AST_LIST_TRAVERSE_SAFE_BEGIN(rl_iter->rules, rule_iter, list) {
- if (rule->time < rule_iter->time) {
- AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
- inserted = 1;
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- if (!inserted) {
- AST_LIST_INSERT_TAIL(rl_iter->rules, rule, list);
- }
-
- ao2_ref(rl_iter, -1);
- }
+ ao2_link(rules, rule);
+ ao2_ref(rule, -1);
return 0;
}
@@ -2272,6 +2349,8 @@
static void remove_queue_member(void *data) {
struct member *mem = data;
+ ast_string_field_free_memory(mem);
+
unref_device(mem->device);
}
@@ -2295,65 +2374,76 @@
struct ast_variable *v;
int link = 0, res = RES_OKAY;
char *rt_uniqueid = NULL, *st_dev = NULL;
-
- if (!(m = ao2_find(q->members, interface, OBJ_KEY))) {
+ int dead = 0;
+
+ if (!(m = ao2_find(q->data->members, interface, OBJ_KEY))) {
if (!(m = ao2_alloc(sizeof(*m), remove_queue_member))) {
- ast_log(LOG_WARNING, "Unable to alocate member\n");
return RES_OUTOFMEMORY;
}
+ if (ast_string_field_init(m, 64)) {
+ ao2_ref(m, -1);
+ ast_log(LOG_WARNING, "Unable to alocate memory\n");
+ return RES_OUTOFMEMORY;
+ }
+
m->device = NULL;
m->penalty = 0;
m->paused = 0;
m->ignorebusy = 1;
m->realtime = (memtype & MEMBER_REALTIME) ? 1 : 0;
m->dynamic = (memtype & MEMBER_DYNAMIC) ? 1 : 0;
+ m->calls = 0;
+ m->lastcall = ast_tv(0, 0);
+ m->lastwrapup = 0;
link = 1;
+ ast_string_field_set(m, interface, interface);
} else {
- ao2_lock(q->members);
- ao2_lock(m);
+ ao2_lock(q->data->members);
if (memtype & MEMBER_DYNAMIC) {
/* dynamic members are the lowest priority and cannot overwrite settings from DB*/
if (m->dynamic) {
res = RES_EXISTS;
- m->dead = 0;
} else {
res = RES_NOT_DYNAMIC;
}
- ao2_unlock(m);
- ao2_unlock(q->members);
+ ao2_unlock(q->data->members);
ao2_ref(m, -1);
return res;
} else if ((m->realtime || m->dynamic) && (memtype & MEMBER_STATIC)) {
/*static members take precedence over all others*/
+ ao2_lock(m);
m->dynamic = 0;
m->realtime = 0;
if (!ast_strlen_zero(m->rt_uniqueid)) {
- m->rt_uniqueid[0] = '\0';
+ ast_string_field_set(m, rt_uniqueid, NULL);
}
+ ao2_unlock(q->data->members);
} else if (memtype & MEMBER_REALTIME) {
/* realtime takes precedence over dynamic but not static*/
+ ao2_lock(m);
if (m->dynamic) {
m->dynamic = 0;
m->realtime = 1;
} else if (!m->realtime) {
+ ao2_unlock(q->data->members);
ao2_unlock(m);
- ao2_unlock(q->members);
ao2_ref(m, -1);
return RES_EXISTS;
}
- }
- }
-
- m->dead = 0;
-
- ast_copy_string(m->interface, interface, sizeof(m->interface));
+ m->dead = 0;
+ ao2_unlock(q->data->members);
+ } else {
+ ao2_unlock(q->data->members);
+ ao2_lock(m);
+ }
+ }
for (v = ast_variable_browse(member_config, interface); v; v = v->next) {
if (!ast_strlen_zero(v->value) && !strcasecmp(v->name, "uniqueid")) {
rt_uniqueid = ast_strdupa(v->value);
} else if (!strcasecmp(v->name, "membername")) {
- ast_copy_string(m->membername, v->value, sizeof(m->membername));
+ ast_string_field_set(m, membername, v->value);
} else if (!strcasecmp(v->name, "state_interface")) {
st_dev = ast_strdupa(ast_strlen_zero(v->value) ? interface : v->value);
} else if (!strcasecmp(v->name, "penalty")) {
@@ -2361,7 +2451,7 @@
m->penalty = 0;
} else if (m->penalty < 0) {
/* negative_penalty_invalid is set and i have a invalid penalty ignoring this member */
- m->dead = 1;
+ dead = 1;
}
} else if (!strcasecmp(v->name, "paused")) {
m->paused = abs(ast_true(v->value));
@@ -2374,7 +2464,7 @@
st_dev = ast_strdupa(interface);
}
- if (!m->dead && (s = ao2_find(devices, st_dev, 0))) {
+ if (!dead && (s = ao2_find(devices, st_dev, 0))) {
if ((s && (m->device != s)) || (!s && m->device)) {
unref_device(m->device);
m->device = (s) ? s : NULL;
@@ -2383,35 +2473,31 @@
}
}
- if (!m->dead && !m->device && (!(m->device = create_member_state(st_dev)))) {
- m->dead = 1;
+ if (!dead && !m->device && (!(m->device = create_member_state(st_dev)))) {
+ dead = 1;
}
if (ast_strlen_zero(m->membername)) {
- ast_copy_string(m->membername, interface, sizeof(m->membername));
+ ast_string_field_set(m, membername, interface);
}
/*check the uniqueness of the RT uniqueid */
- if (m->realtime && !m->dead) {
+ if (link && (memtype & MEMBER_REALTIME) && !dead) {
if (ast_strlen_zero(rt_uniqueid)) {
ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(m->membername, interface));
- m->dead = 1;
- } else if (link && (rt_m = ao2_callback(q->members, 0, member_cmp_uniqueid_fn, rt_uniqueid))) {
+ dead = 1;
+ } else if ((rt_m = ao2_callback(q->data->members, 0, member_cmp_uniqueid_fn, rt_uniqueid))) {
/*make sure there no duplicates this should never happen am i changing interface perhaps ??*/
- ao2_lock(rt_m);
- if (!rt_m->dead) {
- m->dead = 1;
- }
+ dead = 1;
ast_log(AST_LOG_WARNING, "Duplicate uniqueid found while adding %s (%s) found %s (%s) on queue %s : Not adding\n",
m->interface, m->membername, rt_m->interface, rt_m->membername, q->name);
- ao2_unlock(rt_m);
ao2_ref(rt_m, -1);
} else {
- ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
- }
- }
-
- if (!m->dead && link) {
+ ast_string_field_set(m, rt_uniqueid, rt_uniqueid);
+ }
+ }
+
+ if (!dead && link) {
/* i have just been born */
if ((ast_strlen_zero(m->membername) || !log_membername_as_agent)) {
ast_queue_log(q->name, source, m->interface, "ADDMEMBER", "%s", "");
@@ -2433,11 +2519,11 @@
"Paused: %d\r\n"
"IgnoreBusy: %d\r\n",
q->name, m->interface, m->membername, m->device->state_interface,
- "dynamic",m->penalty, m->calls, (int)m->lastcall,
+ "dynamic",m->penalty, m->calls, (int)m->lastcall.tv_sec,
status, m->paused, m->ignorebusy);
}
- ao2_link(q->members, m);
- } else if (m->dead) {
+ ao2_link(q->data->members, m);
+ } else if (dead) {
/* ive failed penalty/uniqueid/devstate failure */
if (!m->device) {
res = RES_OUTOFMEMORY;
@@ -2452,13 +2538,11 @@
ast_queue_log(q->name, source, m->membername, "REMOVEMEMBER", "%s", "");
}
ao2_unlock(m);
- ao2_unlock(q->members);
- ao2_unlink(q->members, m);
+ ao2_unlink(q->data->members, m);
}
} else if (!link) {
/* ive been updated */
ao2_unlock(m);
- ao2_unlock(q->members);
}
ao2_ref(m, -1);
return res;
@@ -2476,7 +2560,7 @@
}
/* Temporarily set realtime members dead so we can detect deleted ones. */
- ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, mark_realtime_member_dead, NULL);
+ ao2_callback(q->data->members, OBJ_NODATA | OBJ_MULTIPLE, mark_realtime_member_dead, NULL);
while ((interface = ast_category_browse(member_config, interface))) {
handle_member_record(q, interface, member_config, MEMBER_REALTIME, "REALTIME");
@@ -2484,7 +2568,46 @@
ast_config_destroy(member_config);
/* Delete all realtime members that have been deleted in DB. */
- ao2_callback_data(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_realtime_dead_members, NULL, q);
+ ao2_callback_data(q->data->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_realtime_dead_members, NULL, q);
+}
+
+/*! \brief Free queue's data struct */
+static void destroy_queue_info(void *obj)
+{
+ struct queue_data *data = obj;
+
+ ao2_callback(data->members, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+ ao2_ref(data->members, -1);
+
+ if (data->head) {
+ AST_LIST_HEAD_DESTROY(data->head);
+ ast_free(data->head);
+ }
+}
+
+/*! \brief find or create a queue data structure*/
+static struct queue_data *get_queue_data(const char *name)
+{
+ struct queue_data *data;
+
+ /* ref will be held for each queue and one ref for container */
+ if ((data = ao2_find(qdata, name, OBJ_KEY))) {
+ return data;
+ } else if (!(data = ao2_alloc(sizeof(*data), destroy_queue_info))) {
+ return NULL;
+ }
+
+ if (!(data->head = ast_calloc(1, sizeof(*data->head)))) {
+ ao2_ref(data, -1);
+ return NULL;
+ }
+
+ data->qhash = ast_str_case_hash(name);
+
+ AST_LIST_HEAD_INIT(data->head);
+
+ ao2_link(qdata, data);
+ return data;
}
/*! \brief Free queue's member list then its string fields */
@@ -2492,25 +2615,14 @@
{
struct call_queue *q = obj;
int i;
- struct member *cur;
- struct ao2_iterator mem_iter;
-
- mem_iter = ao2_iterator_init(q->members, AO2_ITERATOR_UNLINK);
- while ((cur = ao2_iterator_next(&mem_iter))) {
- ao2_ref(cur, -1);
- }
- ao2_iterator_destroy(&mem_iter);
ast_string_field_free_memory(q);
for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
if (q->sound_periodicannounce[i])
free(q->sound_periodicannounce[i]);
}
- ao2_ref(q->members, -1);
- if (q->head) {
- AST_LIST_HEAD_DESTROY(q->head);
- ast_free(q->head);
- }
+ ao2_ref(q->data, -1);
+ ast_log(AST_LOG_WARNING, "Releasing Queue %s\n", q->name);
}
/*! \brief create a new call_queue structure */
@@ -2518,23 +2630,28 @@
{
struct call_queue *q;
- if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
- if (ast_string_field_init(q, 64)) {
- ao2_t_ref(q, -1, "String field allocation failed");
- return NULL;
- }
- ast_string_field_set(q, name, queuename);
- }
- if (!(q->head = ast_calloc(1, sizeof(*q->head)))) {
- ast_string_field_free_memory(q);
+ if (!(q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
+ ast_log(AST_LOG_ERROR, "Memory allocation error\n");
+ return NULL;
+ }
+
+ if (!(q->data = get_queue_data(queuename))) {
+ ast_log(AST_LOG_ERROR, "Memory allocation error\n");
ao2_ref(q, -1);
return NULL;
}
- AST_LIST_HEAD_INIT(q->head);
+
+ if (ast_string_field_init(q, 64)) {
+ ast_log(AST_LOG_ERROR, "Memory allocation error\n");
+ ao2_ref(q->data, -1);
+ ao2_ref(q, -1);
+ return NULL;
+ }
+
+ ast_string_field_set(q, name, queuename);
q->realtime = rt;
q->weight = 0;
q->found = 0;
- ao2_link(queues, q);
return q;
}
@@ -2556,7 +2673,7 @@
char tmpbuf[64]; /* Must be longer than the longest queue param name. */
int prev_weight = 0;
struct ast_variable *queue_vars;
- struct call_queue *q;
+ struct call_queue *q, *oldq;
int found;
if ((q = ao2_t_find(queues, queuename, OBJ_KEY, "Look for queue in memory first")) &&
@@ -2591,11 +2708,18 @@
if (!q && (!(q = alloc_queue(queuename, 1)))) {
ast_variables_destroy(queue_vars);
return NULL;
- }
-
- ao2_lock(q);
- found = q->found;
- prev_weight = q->weight ? 1 : 0;
+ } else {
+ found = q->found;
+ prev_weight = q->weight ? 1 : 0;
+
+ oldq = q;
+ if (!(q = alloc_queue(queuename, 1))) {
+ ast_variables_destroy(queue_vars);
+ return NULL;
+ }
+ ast_log(AST_LOG_WARNING, "Reconfiguring Queue\n");
+ }
+
init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
memset(tmpbuf, 0, sizeof(tmpbuf));
@@ -2618,35 +2742,40 @@
ast_variables_destroy(queue_vars);
/* its important that this is never altered in the life of the queue*/
- if (!q->members && (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)) {
+ if (!q->data->members && (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)) {
/* linear strategy depends on order, so we have to place all members in a single bucket */
- q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
- } else if (!q->members) {
- q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
- }
-
- /* add persistent members to new queue*/
- if (!found && queue_persistent_members) {
- pm_load_member_config(q);
+ q->data->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
+ } else if (!q->data->members) {
+ q->data->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
}
/* update the use_weight value if the queue's has gained or lost a weight */
/* Other cases will end up with the proper value for use_weight */
if (!q->weight && prev_weight) {
ast_atomic_fetchadd_int(&use_weight, -1);
- }
-
- if (q->weight && !prev_weight) {
+ } else if (q->weight && !prev_weight) {
ast_atomic_fetchadd_int(&use_weight, +1);
}
- ao2_unlock(q);
+ /* add persistent members to new queue*/
+ if (!found && queue_persistent_members) {
+ pm_load_member_config(q);
+ }
/* Load realtime members*/
if (rmask & QUEUE_RELOAD_MEMBER) {
rt_load_member_config(q);
}
+ if (oldq) {
+ ao2_lock(queues);
+ ao2_find(queues, oldq, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA | OBJ_NOLOCK);
+ ao2_link(queues, q);
+ ao2_unlock(queues);
+ ao2_ref(oldq, -1);
+ } else {
+ ao2_link(queues, q);
+ }
return q;
}
@@ -2695,20 +2824,25 @@
return res;
}
+ qe->parent = q;
+
/* This is our one */
- if ((get_member_status(q, qe->max_penalty, qe->min_penalty, 1))) {
+ if ((get_member_status(qe, 1))) {
*reason = QUEUE_JOINEMPTY;
ao2_t_ref(q, -1, "Done with realtime queue");
return res;
}
-
- if ((*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) ||
+
+ ao2_lock(q->data);
+ if ((*reason == QUEUE_UNKNOWN && q->maxlen && (q->data->count >= q->maxlen)) ||
(*reason != QUEUE_UNKNOWN)) {
+ ao2_unlock(q->data);
*reason = QUEUE_FULL;
ao2_t_ref(q, -1, "Done with realtime queue");
return res;
}
+ ao2_unlock(q->data);
/* There's space for us, put us at the right position inside
@@ -2716,14 +2850,14 @@
* Take into account the priority of the calling user */
inserted = 0;
- AST_LIST_LOCK(q->head);
- AST_LIST_TRAVERSE_SAFE_BEGIN(q->head, cur, next) {
+ AST_LIST_LOCK(q->data->head);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(q->data->head, cur, next) {
/* We have higher priority than the current user, enter
* before him, after all the other users with priority
* higher or equal to our priority. */
if ((!inserted) && qe && (qe->prio > cur->prio)) {
AST_LIST_INSERT_BEFORE_CURRENT(qe, next);
- insert_entry(q, qe, &pos);
+ insert_entry(qe, &pos);
inserted = 1;
}
/* <= is necessary for the position comparison because it may not be possible to enter
@@ -2731,7 +2865,7 @@
*/
if (!inserted && qe && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
AST_LIST_INSERT_BEFORE_CURRENT(qe, next);
- insert_entry(q, qe, &pos);
+ insert_entry(qe, &pos);
/*pos is incremented inside insert_entry, so don't need to add 1 here*/
if (position < pos) {
ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
@@ -2744,23 +2878,20 @@
/* No luck, join at the end of the queue */
if (!inserted && qe) {
- AST_LIST_INSERT_TAIL(q->head, qe, next);
- insert_entry(q, qe, &pos);
- }
- AST_LIST_UNLOCK(q->head);
-
- ao2_lock(q);
+ AST_LIST_INSERT_TAIL(q->data->head, qe, next);
+ insert_entry(qe, &pos);
+ }
+ AST_LIST_UNLOCK(q->data->head);
+
+ /* pass a ref to the queue rules or this queue*/
+ qe->pr = NULL;
if ((qe->rules = ao2_find(rules, q->defaultrule, 0))) {
- qe->pr = AST_LIST_FIRST(qe->rules->rules);
- } else {
- qe->rules = NULL;
- qe->pr = NULL;
- }
- ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
- ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
- ast_copy_string(qe->context, q->context, sizeof(qe->context));
- q->count++;
+ int time = 0;
+ ao2_callback_data(qe->rules->rules, OBJ_NODATA | OBJ_MULTIPLE, get_best_rule_cb, &time, &qe->pr);
+ }
+
res = 0;
+ ao2_lock(q->data);
ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
"Channel: %s\r\n"
"CallerIDNum: %s\r\n"
@@ -2776,19 +2907,16 @@
S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
- q->name, qe->pos, q->count, qe->chan->uniqueid );
+ q->name, qe->pos, q->data->count, qe->chan->uniqueid );
+ ao2_unlock(q->data);
ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
- ao2_unlock(q);
- ao2_t_ref(q, -1, "Done with realtime queue");
-
return res;
}
-static int play_file(struct ast_channel *chan, struct call_queue *q, const char *filename)
+static int play_file(struct ast_channel *chan, const char *filename)
{
int res;
- const char *playfile;
if (ast_strlen_zero(filename)) {
return 0;
@@ -2798,23 +2926,13 @@
return 0;
}
- playfile = ast_strdupa(filename);
-
- if (q) {
- ao2_unlock(q);
- }
-
ast_stopstream(chan);
- res = ast_streamfile(chan, playfile, chan->language);
+ res = ast_streamfile(chan, filename, chan->language);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
ast_stopstream(chan);
-
- if (q) {
- ao2_lock(q);
- }
return res;
}
@@ -2838,18 +2956,18 @@
}
/* If there's no context to goto, short-circuit */
- if (ast_strlen_zero(qe->context))
+ if (ast_strlen_zero(qe->parent->context))
return 0;
/* If the extension is bad, then reset the digits to blank */
- if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
+ if (!ast_canmatch_extension(qe->chan, qe->parent->context, qe->digits, 1,
S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
qe->digits[0] = '\0';
return 0;
}
/* We have an exact match */
- if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
+ if (!ast_goto_if_exists(qe->chan, qe->parent->context, qe->digits, 1)) {
qe->valid_digits = 1;
/* Return 1 on a successful goto */
return 1;
@@ -2862,16 +2980,18 @@
{
int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
int say_thanks = 1;
- time_t now;
+ struct timeval now;
/* Let minannouncefrequency seconds pass between the start of each position announcement */
- time(&now);
- if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
+ now = ast_tvnow();
+ if (ast_tvdiff_sec(now, qe->last_pos) < qe->parent->minannouncefrequency) {
return 0;
+ }
/* If either our position has changed, or we are over the freq timer, say position */
- if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
+ if ((qe->last_pos_said == qe->pos) && (ast_tvdiff_sec(now, qe->last_pos) < qe->parent->announcefrequency)) {
return 0;
+ }
if (ringing) {
ast_indicate(qe->chan,-1);
@@ -2889,7 +3009,7 @@
if (announceposition == 1) {
/* Say we're next, if we are */
if (qe->pos == 1) {
- res = play_file(qe->chan, qe->parent, qe->parent->sound_next);
+ res = play_file(qe->chan, qe->parent->sound_next);
if (res)
goto playout;
else
@@ -2897,84 +3017,78 @@
} else {
[... 3245 lines stripped ...]
More information about the asterisk-commits
mailing list