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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Nov 4 07:50:17 CDT 2011


Author: irroot
Date: Fri Nov  4 07:50:13 2011
New Revision: 343443

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=343443
Log:
Some cleanups moving code back
add device unref function to unref/unlink if needed
move rules into a container these are atomic as they never get rewriten
the rules are all unlinked when config is re read and when the last ref [queue_ent]
releases there memory is freeed they rules are still a linked list for now

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=343443&r1=343442&r2=343443
==============================================================================
--- team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c (original)
+++ team/irroot/asterisk-trunk-quack-queue/apps/app_queue.c Fri Nov  4 07:50:13 2011
@@ -123,8 +123,8 @@
  * Please be extra careful to always lock in the following order
  *
  * 1) queues container lock (ao2 functions that lock the container)
- * 2) queue lock 
- * 3) queue member lock 
+ * 2) queue lock
+ * 3) queue member lock
  * 4) member lock
  * 5) devices container lock
  * 6) member device lock
@@ -1084,8 +1084,8 @@
 	time_t 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 ast_channel *chan;              /*!< Our channel */
-	AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
 	struct penalty_rule *pr;               /*!< Pointer to the next penalty rule to implement */
+	struct rule_list *rules;               /*!< Pointer holding the ref for the queue penalty rules */
 	AST_LIST_ENTRY(queue_ent) next;        /*!< The next queue entry */
 };
 
@@ -1236,23 +1236,18 @@
 	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 ao2_container *members;             /*!< Head of the list of members */
-	AST_LIST_HEAD(,queue_ent)  *head; /*!< Head of the list of callers */
-	AST_LIST_ENTRY(call_queue) list;    /*!< Next call queue */
-	AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
+	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;
-	AST_LIST_ENTRY(rule_list) list;
+	AST_LIST_HEAD_NOLOCK(, penalty_rule) *rules;
 };
-
-static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
 
 static struct ao2_container *queues;
 static struct ao2_container *devices;
+static struct ao2_container *rules;
 
 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);
@@ -1319,6 +1314,30 @@
 
 	/*This 'double check' that default value is OFF */
 	return QUEUE_AUTOPAUSE_OFF;
+}
+
+/*!
+ * \brief ao2 callback to calculate hash of a queue by name
+ */
+static int queue_hash_cb(const void *obj, const int flags)
+{
+	const struct call_queue *q = obj;
+	const char *name = (flags & OBJ_KEY) ? obj : q->name;
+
+	return ast_str_case_hash(name);
+}
+
+
+/*!
+ * \brief ao2 callback to find queue by name
+ * \note this is the default function used by ao2_find
+ */
+static int queue_cmp_cb(void *obj, void *arg, int flags)
+{
+	const struct call_queue *q = obj, *q2 = arg;
+	const char *name = (flags & OBJ_POINTER) ? q2->name : arg;
+
+	return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /*! \brief Set channel variables of queue */
@@ -1349,10 +1368,6 @@
 /*! \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)
 {
-	if (!q || !new) {
-		return;
-	}
-
 	/* 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. */
@@ -1465,6 +1480,21 @@
 	return -1;
 }
 
+/*! \brief Un ref a device if im the last consumer unlink it*/
+static void unref_device(struct mem_state *s) {
+	if (!s) {
+		return;
+	}
+
+	ao2_lock(devices);
+	/* remove our ref*/
+	if (ao2_ref(s, -1) == 2) {
+		/* we were the only consumer unlink*/
+		ao2_find(devices, s, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA | OBJ_NOLOCK);
+	}
+	ao2_unlock(devices);
+}
+
 /*! \brief send a QueueMemberStatus manager_event when device state changes*/
 static int update_status(void *data)
 {
@@ -1515,9 +1545,7 @@
 	}
 	ao2_iterator_destroy(&qiter);
 
-	if (ao2_ref(s, -1) == 2) {
-		ao2_unlink(devices, s);
-	}
+	unref_device(s);
 
 	return 0;
 }
@@ -1538,14 +1566,35 @@
 		s->status = status;
 		ao2_unlock(s);
 		if (ast_taskprocessor_push(devicestate_tps, update_status, s)) {
-			ao2_ref(s, -1);
+			unref_device(s);
 		}
 	} else {
 		ao2_unlock(s);
-		ao2_ref(s, -1);
+		unref_device(s);
 	}
 
 	return 0;
+}
+
+/*! \brief callback used when device state changes*/
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+	enum ast_device_state state;
+	const char *device;
+
+	state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+	device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+
+	if (ast_strlen_zero(device)) {
+		ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+		return;
+	}
+
+	if (set_device_status(device, state)) {
+		ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", device, state, ast_devstate2str(state));
+	} else {
+		ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", device, state, ast_devstate2str(state));
+	}
 }
 
 /*! \brief Helper function which converts from extension state to device state values */
@@ -1580,27 +1629,6 @@
 	return state;
 }
 
-/*! \brief callback used when device state changes*/
-static void device_state_cb(const struct ast_event *event, void *unused)
-{
-	enum ast_device_state state;
-	const char *device;
-
-	state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
-	device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
-
-	if (ast_strlen_zero(device)) {
-		ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
-		return;
-	}
-
-	if (set_device_status(device, state)) {
-		ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", device, state, ast_devstate2str(state));
-	} else {
-		ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", device, state, ast_devstate2str(state));
-	}
-}
-
 /*! \brief callback called when a extension hint is notified of change*/
 static int extension_state_cb(const char *context, const char *exten, enum ast_extension_states state, void *data)
 {
@@ -1680,14 +1708,12 @@
 	ao2_lock(s);
 	if (s->status != status) {
 		s->status = status;
-		ao2_unlock(s);
 		/* we must pass a ref to the task processor*/
 		if (!ast_taskprocessor_push(devicestate_tps, update_status, s)) {
 			ao2_ref(s, 1);
 		}
-	} else {
-		ao2_unlock(s);
-	}
+	}
+	ao2_unlock(s);
 	ao2_unlock(m);
 }
 
@@ -1706,70 +1732,6 @@
 }
 
 /*!
- * \brief ao2 callback to calculate hash of a queue by name
- */
-static int queue_hash_cb(const void *obj, const int flags)
-{
-	const struct call_queue *q = obj;
-	const char *name = (flags & OBJ_KEY) ? obj : q->name;
-
-	return ast_str_case_hash(name);
-}
-
-
-/*!
- * \brief ao2 callback to find queue by name
- * \note this is the default function used by ao2_find
- */
-static int queue_cmp_cb(void *obj, void *arg, int flags)
-{
-	struct call_queue *q = obj;
-	const struct call_queue *q2 = arg;
-	const char *name = (flags & OBJ_POINTER) ? q2->name : arg;
-
-	return !strcasecmp(q->name, name) ? CMP_MATCH | CMP_STOP : 0;
-}
-
-/*!
- * \brief ao2 callback to mark static queues dead
- */
-static int mark_queues_dead(void *obj, void *arg, int flags)
-{
-	struct call_queue *q = obj;
-	const struct call_queue *q2 = arg;
-	const char *queuename = (flags & OBJ_POINTER) ? q2->name : arg;
-
-	ao2_lock(q);
-	if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
-		q->dead = 1;
-		ao2_unlock(q);
-		return CMP_MATCH;
-	}
-	ao2_unlock(q);
-
-	return 0;
-}
-
-/*!
- * \brief ao2 callback to delete queues marked dead
- */
-static int kill_dead_queues(void *obj, void *arg, int flags)
-{
-	struct call_queue *q = obj;
-	const struct call_queue *q2 = arg;
-	const char *queuename = (flags & OBJ_POINTER) ? q2->name : arg;
-
-	ao2_lock(q);
-	if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
-		ao2_unlock(q);
-		return CMP_MATCH;
-	}
-	ao2_unlock(q);
-
-	return 0;
-}
-
-/*!
  * \brief ao2 callback to calculate hash of a member by interface
  */
 static int member_hash_fn(const void *obj, const int flags)
@@ -1794,7 +1756,7 @@
  */
 static int member_cmp_fn(void *obj1, void *obj2, int flags)
 {
-	struct member *mem1 = obj1;
+	const struct member *mem1 = obj1;
 	const struct member *mem2 = obj2;
 	const char *arg = (flags & OBJ_POINTER) ? mem2->interface : obj2;
 
@@ -1806,7 +1768,7 @@
  */
 static int member_cmp_uniqueid_fn(void *obj1, void *arg, int flags)
 {
-	struct member *mem1 = obj1;
+	const struct member *mem1 = obj1;
 	const struct member *mem2 = arg;
 	const char *uniqueid = (flags & OBJ_POINTER) ? mem2->rt_uniqueid : arg;
 
@@ -1814,34 +1776,6 @@
 		return CMP_MATCH | CMP_STOP;
 	}
 
-	return 0;
-}
-
-/*!
- * \brief ao2 callback to mark static members dead
- */
-static int mark_static_member_dead(void *obj, void *arg, int flags)
-{
-	struct member *member = obj;
-
-	if (!(member->dynamic | member->realtime)) {
-		member->dead = 1;
-		return CMP_MATCH;
-	}
-
-	return 0;
-}
-
-/*!
- * \brief ao2 callback to delete static members marked dead
- */
-static int kill_static_dead_members(void *obj, void *arg, int flags)
-{
-	struct member *member = obj;
-
-	if (!(member->dynamic | member->realtime) && member->dead) {
-		return CMP_MATCH;
-	}
 	return 0;
 }
 
@@ -1864,8 +1798,8 @@
  */
 static int kill_realtime_dead_members(void *obj, void *arg, void *data, int flags)
 {
-	struct member *m = obj;
-	struct call_queue *q = data;
+	const struct member *m = obj;
+	const struct call_queue *q = data;
 
 	if (m->dead && m->realtime) {
 		if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
@@ -1889,7 +1823,6 @@
 	return 0;
 }
 
-
 /*!
  * \brief ao2 callback to calculate hash of a device by state_interface
  */
@@ -1907,63 +1840,35 @@
  */
 static int device_cmp_cb(void *obj, void *arg, int flags)
 {
-	struct mem_state *d = obj;
+	const struct mem_state *d = obj;
 	const struct mem_state *d2 = arg;
 	const char *iface = (flags & OBJ_POINTER) ? d2->state_interface : arg;
 
 	return !strcasecmp(d->state_interface, iface) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-/*! \brief Free queue's member list then its string fields */
-static void destroy_queue(void *obj)
-{
-	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);
-	}
-}
-
-/*! \brief create a new call_queue structure */
-static struct call_queue *alloc_queue(const char *queuename, int rt)
-{
-	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);
-		ao2_ref(q, -1);
-		return NULL;
-	}
-	AST_LIST_HEAD_INIT(q->head);
-	q->realtime = rt;
-	q->weight = 0;
-	q->found = 0;
-	ao2_link(queues, q);
-
-	return q;
+/*!
+ * \brief ao2 callback to calculate hash of a rule by name
+ */
+static int rules_hash_cb(const void *obj, const int flags)
+{
+	const struct rule_list *rl = obj;
+	const char *name = (flags & OBJ_KEY) ? obj : rl->name;
+
+	return ast_str_case_hash(name);
+}
+
+/*!
+ * \brief ao2 callback to find rule by name
+ * \note this is the default function used by ao2_find
+ */
+static int rules_cmp_cb(void *obj, void *arg, int flags)
+{
+	const struct rule_list *rl = obj;
+	const struct rule_list *rl2 = arg;
+	const char *name = (flags & OBJ_POINTER) ? rl2->name : arg;
+
+	return !strcasecmp(rl->name, name) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /*!
@@ -1973,7 +1878,6 @@
 static void init_queue(struct call_queue *q)
 {
 	int i;
-	struct penalty_rule *pr_iter;
 
 	q->dead = 0;
 	q->retry = DEFAULT_RETRY;
@@ -2035,9 +1939,6 @@
 		if (q->sound_periodicannounce[i])
 			ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
 	}
-
-	while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
-		ast_free(pr_iter);
 }
 
 /*!
@@ -2098,11 +1999,8 @@
 		rule->min_relative = 1;
 
 	/*We have the rule made, now we need to insert it where it belongs*/
-	AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
-		if (strcasecmp(rl_iter->name, list_name))
-			continue;
-
-		AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
+	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;
@@ -2110,10 +2008,12 @@
 			}
 		}
 		AST_LIST_TRAVERSE_SAFE_END;
-	
+
 		if (!inserted) {
-			AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
-		}
+			AST_LIST_INSERT_TAIL(rl_iter->rules, rule, list);
+		}
+
+		ao2_ref(rl_iter, -1);
 	}
 
 	return 0;
@@ -2160,7 +2060,7 @@
 }
 
 /*! \brief Configure a queue parameter.
- * 
+ *
  * The failunknown flag is set for config files (and static realtime) to show
  * errors for unknown parameters. It is cleared for dynamic realtime to allow
  *  extra fields in the tables.
@@ -2371,16 +2271,8 @@
 /*! \brief callback that is called when a member is released*/
 static void remove_queue_member(void *data) {
 	struct member *mem = data;
-	struct mem_state *s = mem->device;
-
-	if (s) {
-		/* we have a device lets unlink and unref it*/
-		/* remove our ref*/
-		if (ao2_ref(s, -1) == 2) {
-			/* we were the only consumer unlink*/
-			ao2_unlink(devices, s);
-		}
-	}
+
+	unref_device(mem->device);
 }
 
 /*!
@@ -2419,37 +2311,37 @@
 	} else {
 		ao2_lock(q->members);
 		ao2_lock(m);
-	}
-
-	if (!link && (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_ref(m, -1);
-		return res;
-	} else if (!link && (m->realtime || m->dynamic) && (memtype & MEMBER_STATIC)) {
-		/*static members take precedence over all others*/
-		m->dynamic = 0;
-		m->realtime = 0;
-		if (!ast_strlen_zero(m->rt_uniqueid)) {
-			m->rt_uniqueid[0] = '\0';
-		}
-	} else if (!link && (memtype & MEMBER_REALTIME)) {
-		/* realtime takes precedence over dynamic but not static*/
-		if (m->dynamic) {
-			m->dynamic = 0;
-			m->realtime = 1;
-		} else if (!m->realtime) {
+
+		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_ref(m, -1);
-			return RES_EXISTS;
+			return res;
+		} else if ((m->realtime || m->dynamic) && (memtype & MEMBER_STATIC)) {
+			/*static members take precedence over all others*/
+			m->dynamic = 0;
+			m->realtime = 0;
+			if (!ast_strlen_zero(m->rt_uniqueid)) {
+				m->rt_uniqueid[0] = '\0';
+			}
+		} else if (memtype & MEMBER_REALTIME) {
+			/* realtime takes precedence over dynamic but not static*/
+			if (m->dynamic) {
+				m->dynamic = 0;
+				m->realtime = 1;
+			} else if (!m->realtime) {
+				ao2_unlock(m);
+				ao2_unlock(q->members);
+				ao2_ref(m, -1);
+				return RES_EXISTS;
+			}
 		}
 	}
 
@@ -2483,18 +2375,11 @@
 	}
 
 	if (!m->dead && (s = ao2_find(devices, st_dev, 0))) {
-		if (s && (m->device != s)) {
-			if (m->device && (ao2_ref(m->device, -1) == 2)) {
-				ao2_unlink(devices, m->device);
-			}
-			m->device = s;
+		if ((s && (m->device != s)) || (!s && m->device)) {
+			unref_device(m->device);
+			m->device = (s) ? s : NULL;
 		} else if (s) {
-			ao2_ref(s, -1);
-		} else if (m->device) {
-			if (ao2_ref(m->device, -1) == 2) {
-				ao2_unlink(devices, m->device);
-			}
-			m->device = NULL;
+			unref_device(s);
 		}
 	}
 
@@ -2602,6 +2487,58 @@
 	ao2_callback_data(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_realtime_dead_members, NULL, q);
 }
 
+/*! \brief Free queue's member list then its string fields */
+static void destroy_queue(void *obj)
+{
+	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);
+	}
+}
+
+/*! \brief create a new call_queue structure */
+static struct call_queue *alloc_queue(const char *queuename, int rt)
+{
+	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);
+		ao2_ref(q, -1);
+		return NULL;
+	}
+	AST_LIST_HEAD_INIT(q->head);
+	q->realtime = rt;
+	q->weight = 0;
+	q->found = 0;
+	ao2_link(queues, q);
+
+	return q;
+}
+
 /*!
  * \brief Reload a single queue via realtime.
  *
@@ -2778,13 +2715,13 @@
 	 * the queue.
 	 * 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) {
 		/* 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->prio > cur->prio)) {
+		if ((!inserted) && qe && (qe->prio > cur->prio)) {
 			AST_LIST_INSERT_BEFORE_CURRENT(qe, next);
 			insert_entry(q, qe, &pos);
 			inserted = 1;
@@ -2792,7 +2729,7 @@
 		/* <= is necessary for the position comparison because it may not be possible to enter
 		 * at our desired position since higher-priority callers may have taken the position we want
 		 */
-		if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
+		if (!inserted && qe && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
 			AST_LIST_INSERT_BEFORE_CURRENT(qe, next);
 			insert_entry(q, qe, &pos);
 			/*pos is incremented inside insert_entry, so don't need to add 1 here*/
@@ -2806,13 +2743,19 @@
 	AST_LIST_TRAVERSE_SAFE_END;
 
 	/* No luck, join at the end of the queue */
-	if (!inserted) {
+	if (!inserted && qe) {
 		AST_LIST_INSERT_TAIL(q->head, qe, next);
 		insert_entry(q, qe, &pos);
 	}
 	AST_LIST_UNLOCK(q->head);
 
 	ao2_lock(q);
+	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));
@@ -3090,8 +3033,8 @@
 {
 	struct call_queue *q;
 	struct queue_ent *cur;
-	struct penalty_rule *pr_iter;
 	int pos = 0;
+	char posstr[20];
 
 	if (!(q = qe->parent)) {
 		return;
@@ -3113,8 +3056,6 @@
 			ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
 			/* Take us out of the queue */
 			AST_LIST_REMOVE_CURRENT(next);
-			while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
-				ast_free(pr_iter);
 			snprintf(posstr, sizeof(posstr), "%d", qe->pos);
 			pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
 		} else {
@@ -3628,7 +3569,7 @@
 	return ret;
 }
 
-/*! \brief Search for best metric and add to Round Robbin queue 
+/*! \brief Search for best metric and add to Round Robbin queue
  * \note the members container is locked here for new_attempt
  */
 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
@@ -3701,10 +3642,10 @@
 		ast_moh_stop(qe->chan);
 
 	ast_verb(3, "Playing periodic announcement\n");
-	
+
 	if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
 		qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
-	} else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
+	} else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
 		ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
 		qe->last_periodic_announce_sound = 0;
 	}
@@ -3757,6 +3698,8 @@
 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
 static void rna(int rnatime, struct queue_ent *qe, struct callattempt *call, int pause)
 {
+	int autopause, autopausedelay;
+
 	ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
 
 	/* Stop ringing, and resume MOH if specified */
@@ -3787,28 +3730,29 @@
 						qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
 	}
 	ast_queue_log(qe->parent->name, qe->chan->uniqueid, call->member->membername, "RINGNOANSWER", "%d", rnatime);
-	if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
-		if (qe->parent->autopausedelay > 0) {
+
+	autopause = qe->parent->autopause;
+	autopausedelay = qe->parent->autopausedelay;
+
+	ao2_unlock(call->member);
+	ao2_unlock(qe->parent);
+
+	if (autopause != QUEUE_AUTOPAUSE_OFF && pause) {
+		if (autopausedelay > 0) {
 			time_t idletime = time(&idletime) - call->member->lastcall;
-			if ((call->member->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
-				ao2_unlock(call->member);
-				ao2_unlock(qe->parent);
+			if ((call->member->lastcall != 0) && (autopausedelay > idletime)) {
 				return;
 			}
 		}
-		if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
+		if (autopause == QUEUE_AUTOPAUSE_ON) {
 			if (!set_member_paused(qe->parent->name, call->interface, "Auto-Pause", 1)) {
 				ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
 							call->interface, qe->parent->name);
 			} else {
 				ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", call->interface, qe->parent->name);
 			}
-			ao2_unlock(call->member);
-			ao2_unlock(qe->parent);
 			return;
 		} else {
-			ao2_unlock(call->member);
-			ao2_unlock(qe->parent);
 			/* If queue autopause is mode all, just don't send any queue to stop.
 			* the function will stop in all queues */
 			if (!set_member_paused("", call->interface, "Auto-Pause", 1)) {
@@ -3820,8 +3764,6 @@
 			return;
 		}
 	}
-	ao2_unlock(call->member);
-	ao2_unlock(qe->parent);
 
 	return;
 }
@@ -4827,7 +4769,7 @@
 	usepenalty = (ao2_container_count(qe->parent->members) <= qe->parent->penaltymemberslimit) ? 0 : 1;
 	strategy = qe->parent->strategy;
 	ao2_unlock(qe->parent);
-		
+
 	for (; options && *options; options++)
 		switch (*options) {
 		case 't':
@@ -4906,7 +4848,7 @@
 	datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
 	ast_channel_unlock(qe->chan);
 
-	if (!datastore && 
+	if (!datastore &&
 	    (datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
 		datastore->inheritance = DATASTORE_INHERIT_FOREVER;
 		if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
@@ -4938,7 +4880,7 @@
 		AST_LIST_LOCK(dialed_interfaces);
 		AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
 			if (!strcasecmp(cur->interface, di->interface)) {
-				ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
+				ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
 					di->interface);
 				break;
 			}
@@ -5674,7 +5616,7 @@
 	}
 	ao2_unlock(pm_queue->members);
 	ao2_iterator_destroy(&mem_iter);
-	
+
 	if (value_len) {
 		if (ast_db_put(pm_family, pm_queue->name, value)) {
 			ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
@@ -5685,7 +5627,7 @@
 	}
 }
 
-/*! \brief Remove member from queue 
+/*! \brief Remove member from queue
  * \retval RES_NOT_DYNAMIC when they aren't a RT member
  * \retval RES_NOSUCHQUEUE queue does not exist
  * \retval RES_OKAY removed member from queue
@@ -6199,40 +6141,6 @@
 		"%s", args.params ? args.params : "");
 
 	return 0;
-}
-
-/*! \brief Copy rule from global list into specified queue */
-static void copy_rules(struct queue_ent *qe, const char *rulename)
-{
-	struct penalty_rule *pr_iter;
-	struct rule_list *rl_iter;
-	const char *tmp;
-
-	ao2_lock(qe->parent);
-	tmp = ast_strdupa((ast_strlen_zero(rulename)) ? qe->parent->defaultrule : rulename);
-	ao2_unlock(qe->parent);
-
-	AST_LIST_LOCK(&rule_lists);
-	AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
-		if (!strcasecmp(rl_iter->name, tmp))
-			break;
-	}
-	if (rl_iter) {
-		AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
-			struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
-			if (!new_pr) {
-				ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
-				break;
-			}
-			new_pr->time = pr_iter->time;
-			new_pr->max_value = pr_iter->max_value;
-			new_pr->min_value = pr_iter->min_value;
-			new_pr->max_relative = pr_iter->max_relative;
-			new_pr->min_relative = pr_iter->min_relative;
-			AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
-		}
-	}
-	AST_LIST_UNLOCK(&rule_lists);
 }
 
 /*!\brief The starting point for all queue calls
@@ -6378,8 +6286,6 @@
 		S_OR(args.url, ""),
 		S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
 		qe.opos);
-	copy_rules(&qe, args.rule);
-	qe.pr = AST_LIST_FIRST(&qe.qe_rules);
 check_turns:
 	if (ringing) {
 		ast_indicate(chan, AST_CONTROL_RINGING);
@@ -6536,6 +6442,10 @@
 		 * the queue_ent is about to be returned on the stack */
 		ao2_ref(qe.parent, -1);
 		qe.parent = NULL;
+	}
+
+	if (qe.rules) {
+		ao2_ref(qe.rules, -1);
 	}
 
 	return res;
@@ -7048,6 +6958,20 @@
 	.write = queue_function_memberpenalty_write,
 };
 
+/*!
+ * \brief ao2 callback to delete priority rules
+ */
+static void delete_priority_rule(void *data)
+{
+	struct rule_list *rule = data;
+	struct penalty_rule *pr;
+
+	while ((pr = AST_LIST_REMOVE_HEAD(rule->rules, list))) {
+		ast_free(pr);
+	}
+	AST_LIST_HEAD_INIT_NOLOCK(rule->rules);
+}
+
 /*! \brief Reload the rules defined in queuerules.conf
  *
  * \param reload If 1, then only process queuerules.conf if the file
@@ -7057,8 +6981,7 @@
 static int reload_queue_rules(int reload)
 {
 	struct ast_config *cfg;
-	struct rule_list *rl_iter, *new_rl;
-	struct penalty_rule *pr_iter;
+	struct rule_list *new_rl;
 	char *rulecat = NULL;
 	struct ast_variable *rulevar = NULL;
 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
@@ -7074,28 +6997,31 @@
 		return AST_MODULE_LOAD_SUCCESS;
 	}
 
-	AST_LIST_LOCK(&rule_lists);
-	while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
-		while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
-			ast_free(pr_iter);
-		ast_free(rl_iter);
-	}
+	/* unlink all objects they will be deleted when all refference to them is droped*/
+	ao2_callback(rules, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+
 	while ((rulecat = ast_category_browse(cfg, rulecat))) {
-		if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
-			AST_LIST_UNLOCK(&rule_lists);
+		if (!(new_rl = ao2_alloc(sizeof(*new_rl), delete_priority_rule))) {
 			ast_config_destroy(cfg);
 			return AST_MODULE_LOAD_FAILURE;
 		} else {
+			if (!(new_rl->rules = ast_calloc(1, sizeof(*new_rl->rules)))) {
+				ast_log(AST_LOG_ERROR, "Failed to create rules list for %s\n", rulecat);
+				ao2_ref(new_rl, -1);
+				continue;
+			}
+			AST_LIST_HEAD_INIT_NOLOCK(new_rl->rules);
 			ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
-			AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
-			for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
-				if(!strcasecmp(rulevar->name, "penaltychange"))
+			ao2_link(rules, new_rl);
+			for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) {
+				if(!strcasecmp(rulevar->name, "penaltychange")) {
 					insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
-				else
+				} else {
 					ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
-		}
-	}
-	AST_LIST_UNLOCK(&rule_lists);
+				}
+			}
+		}
+	}
 
 	ast_config_destroy(cfg);
 
@@ -7181,6 +7107,34 @@
 	add_var_to_cat(mcat, "ignorebusy", args.ignorebusy);
 
 	return mcat;
+}
+
+/*!
+ * \brief ao2 callback to mark static members dead
+ */
+static int mark_static_member_dead(void *obj, void *arg, int flags)
+{
+	struct member *member = obj;
+
+	if (!(member->dynamic | member->realtime)) {
+		member->dead = 1;
+		return CMP_MATCH;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief ao2 callback to delete static members marked dead
+ */
+static int kill_static_dead_members(void *obj, void *arg, int flags)
+{
+	struct member *member = obj;
+
+	if (!(member->dynamic | member->realtime) && member->dead) {
+		return CMP_MATCH;
+	}
+	return 0;
 }
 
 /*! \brief Reload information pertaining to a particular queue
@@ -7278,6 +7232,45 @@
 		ast_config_destroy(mcfg);
 	}
 	ao2_t_ref(q, -1, "Expiring creation reference");
+}
+
+/*!
+ * \brief ao2 callback to mark static queues dead
+ */
+static int mark_queues_dead(void *obj, void *arg, int flags)
+{
+	struct call_queue *q = obj;
+	const struct call_queue *q2 = arg;
+	const char *queuename = (flags & OBJ_POINTER) ? q2->name : arg;
+
+	ao2_lock(q);
+	if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
+		q->dead = 1;
+		ao2_unlock(q);
+		return CMP_MATCH;
+	}
+	ao2_unlock(q);
+
+	return 0;
+}
+
+/*!
+ * \brief ao2 callback to delete queues marked dead
+ */
+static int kill_dead_queues(void *obj, void *arg, int flags)
+{
+	struct call_queue *q = obj;
+	const struct call_queue *q2 = arg;
+	const char *queuename = (flags & OBJ_POINTER) ? q2->name : arg;
+
+	ao2_lock(q);
+	if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
+		ao2_unlock(q);
+		return CMP_MATCH;
+	}
+	ao2_unlock(q);
+
+	return 0;
 }
 
 /*! \brief reload the queues.conf file
@@ -7597,24 +7590,29 @@
 	const char *id = astman_get_header(m, "ActionID");
 	struct rule_list *rl_iter;
 	struct penalty_rule *pr_iter;
+	struct ao2_iterator riter;
 
 	astman_append(s, "Response: Success\r\n");
 	if (!ast_strlen_zero(id)) {
 		astman_append(s, "ActionID: %s\r\n", id);
 	}
 
-	AST_LIST_LOCK(&rule_lists);
-	AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
-		if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
+	if (!ast_strlen_zero(rule) && (rl_iter = ao2_find(rules, rule, OBJ_KEY))) {
+		astman_append(s, "RuleList: %s\r\n", rl_iter->name);
+		AST_LIST_TRAVERSE(rl_iter->rules, pr_iter, list) {
+			astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
+		}
+		ao2_ref(rl_iter, -1);
+	} else if (ast_strlen_zero(rule)) {
+		riter = ao2_iterator_init(rules, 0);
+		while ((rl_iter = ao2_iterator_next(&riter))) {
 			astman_append(s, "RuleList: %s\r\n", rl_iter->name);
-			AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
+			AST_LIST_TRAVERSE(rl_iter->rules, pr_iter, list) {
 				astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
 			}
-			if (!ast_strlen_zero(rule))
-				break;
-		}
-	}
-	AST_LIST_UNLOCK(&rule_lists);
+		}
+		ao2_iterator_destroy(&riter);
+	}
 
 	/*
 	 * Two blank lines instead of one because the Response and
@@ -7694,7 +7692,7 @@
 			"Queue: %s\r\n"
 			"LoggedIn: %d\r\n"
 			"Available: %d\r\n"
-			"Callers: %d\r\n" 
+			"Callers: %d\r\n"
 			"HoldTime: %d\r\n"
 			"TalkTime: %d\r\n"
 			"LongestHoldTime: %d\r\n"
@@ -8524,18 +8522,20 @@
 	struct rule_list *rl_iter;
 	int wordlen = strlen(word);
 	char *ret = NULL;
+	struct ao2_iterator riter;
+
 	if (pos != 3) /* Wha? */ {
 		return NULL;
 	}
 
-	AST_LIST_LOCK(&rule_lists);
-	AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+	riter = ao2_iterator_init(rules, 0);
+	while ((rl_iter = ao2_iterator_next(&riter))) {
 		if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
 			ret = ast_strdup(rl_iter->name);
 			break;
 		}
 	}
-	AST_LIST_UNLOCK(&rule_lists);
+	ao2_iterator_destroy(&riter);
 
 	return ret;
 }
@@ -8545,6 +8545,8 @@
 	const char *rule;
 	struct rule_list *rl_iter;
 	struct penalty_rule *pr_iter;
+	struct ao2_iterator riter;
+
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "queue show rules";
@@ -8561,16 +8563,18 @@
 		return CLI_SHOWUSAGE;
 
 	rule = a->argc == 4 ? a->argv[3] : "";
-	AST_LIST_LOCK(&rule_lists);
-	AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
+
+	riter = ao2_iterator_init(rules, 0);
+	while ((rl_iter = ao2_iterator_next(&riter))) {
 		if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
 			ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
-			AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
+			AST_LIST_TRAVERSE(rl_iter->rules, pr_iter, list) {
 				ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
 			}
 		}
 	}
-	AST_LIST_UNLOCK(&rule_lists);
+	ao2_iterator_destroy(&riter);
+
 	return CLI_SUCCESS; 
 }
 
@@ -8944,7 +8948,9 @@
 	struct ao2_iterator q_iter;
 	struct call_queue *q = NULL;
 	struct ao2_iterator d_iter;
+	struct ao2_iterator r_iter;
 	struct interface *d = NULL;
+	struct rule_list *r = NULL;
 
 	ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
 	res = ast_manager_unregister("QueueStatus");
@@ -8997,6 +9003,13 @@
 	ao2_iterator_destroy(&d_iter);
 	ao2_ref(devices, -1);
 
+	r_iter = ao2_iterator_init(rules, AO2_ITERATOR_UNLINK);
+	while ((r = ao2_iterator_next(&r_iter))) {
+		ao2_ref(r, -1);
+	}
+	ao2_iterator_destroy(&r_iter);
+	ao2_ref(rules, -1);
+
 	ast_unload_realtime("queue_members");
 	devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
 	return res;
@@ -9010,6 +9023,7 @@
 
 	queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
 	devices = ao2_container_alloc(MAX_QUEUE_BUCKETS, device_hash_cb, device_cmp_cb);
+	rules = ao2_container_alloc(MAX_QUEUE_BUCKETS, rules_hash_cb, rules_cmp_cb);
 
 	use_weight = 0;
 




More information about the asterisk-commits mailing list