[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