[svn-commits] irroot: branch irroot/asterisk-trunk-quack-queue r343535 - /team/irroot/aster...

SVN commits to the Digium repositories svn-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 svn-commits mailing list