]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_queue crashes randomly, often during call-transfers
authorDavid Vossel <dvossel@digium.com>
Mon, 30 Nov 2009 16:32:58 +0000 (16:32 +0000)
committerDavid Vossel <dvossel@digium.com>
Mon, 30 Nov 2009 16:32:58 +0000 (16:32 +0000)
In app_queue, it is possible for a call_queue to be destroyed
while another object still holds a pointer to it.  This patch
converts call_queue objects to ao2 objects allowing them to be
ref counted.  This makes it safe for the queue_ent object in
queue_exec() to reference it's parent call_queue even after it
has left the queue.

(closes issue #15686)
Reported by: Hatrix
Patches:
      v2_queue_ao2.diff uploaded by dvossel (license 671)
Tested by: dvossel, aragon

Review: https://reviewboard.asterisk.org/r/427/

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@231437 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_queue.c

index 47f59d53269e2b2a7a63191f8a140c9fc02ed8bd..11b4a9b9bc8c8c3633d81c8e88ed5922cfef926d 100644 (file)
@@ -385,7 +385,6 @@ static AST_LIST_HEAD_STATIC(interfaces, member_interface);
 #define QUEUE_EVENT_VARIABLES 3
 
 struct call_queue {
-       ast_mutex_t lock;       
        char name[80];                      /*!< Name */
        char moh[80];                       /*!< Music On Hold class to be used */
        char announce[80];                  /*!< Announcement to play when call is answered */
@@ -454,8 +453,8 @@ struct call_queue {
 static AST_LIST_HEAD_STATIC(queues, call_queue);
 
 static int set_member_paused(const char *queuename, const char *interface, int paused);
-
 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); 
+static void free_members(struct call_queue *q, int all);
 
 static void rr_dep_warning(void)
 {
@@ -512,6 +511,27 @@ static int strat2int(const char *strategy)
        return -1;
 }
 
+/*!
+ * \brief removes a call_queue from the list of call_queues
+ */
+static void remove_queue(struct call_queue *q)
+{
+       AST_LIST_LOCK(&queues);
+       if (AST_LIST_REMOVE(&queues, q, list)) {
+               ao2_ref(q, -1);
+       }
+       AST_LIST_UNLOCK(&queues);
+}
+
+static void destroy_queue(void *obj)
+{
+       struct call_queue *q = obj;
+       if (q->members) {
+               free_members(q, 1);
+               ao2_ref(q->members, -1);
+       }
+}
+
 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 {
@@ -527,6 +547,11 @@ static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, st
                q->head = new;
        }
        new->next = cur;
+
+       /* 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;
        new->pos = ++(*pos);
        new->opos = *pos;
@@ -550,7 +575,7 @@ static enum queue_member_status get_member_status(struct call_queue *q, int max_
        struct ao2_iterator mem_iter;
        enum queue_member_status result = QUEUE_NO_MEMBERS;
 
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
        mem_iter = ao2_iterator_init(q->members, 0);
        while ((member = ao2_iterator_next(&mem_iter))) {
                if (max_penalty && (member->penalty > max_penalty)) {
@@ -573,13 +598,13 @@ static enum queue_member_status get_member_status(struct call_queue *q, int max_
                        ao2_ref(member, -1);
                        break;
                default:
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                        ao2_ref(member, -1);
                        return QUEUE_NORMAL;
                }
        }
        ao2_iterator_destroy(&mem_iter);
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
        return result;
 }
 
@@ -598,7 +623,7 @@ static int update_status(const char *interface, const int status)
 
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                mem_iter = ao2_iterator_init(q->members, 0);
                while ((cur = ao2_iterator_next(&mem_iter))) {
                        char *slash_pos;
@@ -635,7 +660,7 @@ static int update_status(const char *interface, const int status)
                        ao2_ref(cur, -1);
                }
                ao2_iterator_destroy(&mem_iter);
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        }
        AST_LIST_UNLOCK(&queues);
 
@@ -785,8 +810,7 @@ static struct call_queue *alloc_queue(const char *queuename)
 {
        struct call_queue *q;
 
-       if ((q = ast_calloc(1, sizeof(*q)))) {
-               ast_mutex_init(&q->lock);
+       if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
                ast_copy_string(q->name, queuename, sizeof(q->name));
        }
        return q;
@@ -915,7 +939,7 @@ static int interface_exists_global(const char *interface)
 
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                mem_iter = ao2_iterator_init(q->members, 0);
                while ((mem = ao2_iterator_next(&mem_iter))) {
                        if (!strcasecmp(mem->state_interface, interface)) {
@@ -926,7 +950,7 @@ static int interface_exists_global(const char *interface)
                        ao2_ref(mem, -1);
                }
                ao2_iterator_destroy(&mem_iter);
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
                if (ret)
                        break;
        }
@@ -1187,14 +1211,6 @@ static void free_members(struct call_queue *q, int all)
        ao2_iterator_destroy(&mem_iter);
 }
 
-static void destroy_queue(struct call_queue *q)
-{
-       free_members(q, 1);
-       ast_mutex_destroy(&q->lock);
-       ao2_ref(q->members, -1);
-       free(q);
-}
-
 /*!\brief Reload a single queue via realtime.
    \return Return the queue, or NULL if it doesn't exist.
    \note Should be called with the global qlock locked. */
@@ -1216,14 +1232,14 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
 
        /* Static queues override realtime. */
        if (q) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                if (!q->realtime) {
                        if (q->dead) {
-                               ast_mutex_unlock(&q->lock);
+                               ao2_unlock(q);
                                return NULL;
                        } else {
                                ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
-                               ast_mutex_unlock(&q->lock);
+                               ao2_unlock(q);
                                return q;
                        }
                }
@@ -1244,11 +1260,10 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
                        /* Delete if unused (else will be deleted when last caller leaves). */
                        if (!q->count) {
                                /* Delete. */
-                               AST_LIST_REMOVE(&queues, q, list);
-                               ast_mutex_unlock(&q->lock);
-                               destroy_queue(q);
+                               ao2_unlock(q);
+                               remove_queue(q);
                        } else
-                               ast_mutex_unlock(&q->lock);
+                               ao2_unlock(q);
                }
                return NULL;
        }
@@ -1257,7 +1272,7 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
        if (!q) {
                if (!(q = alloc_queue(queuename)))
                        return NULL;
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                clear_queue(q);
                q->realtime = 1;
                AST_LIST_INSERT_HEAD(&queues, q, list);
@@ -1309,16 +1324,16 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
        while ((m = ao2_iterator_next(&mem_iter))) {
                if (m->dead) {
                        ao2_unlink(q->members, m);
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                        remove_from_interfaces(m->state_interface);
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        q->membercount--;
                }
                ao2_ref(m, -1);
        }
        ao2_iterator_destroy(&mem_iter);
 
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
 
        return q;
 }
@@ -1358,7 +1373,7 @@ static void update_realtime_members(struct call_queue *q)
                return;
        }
 
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
        
        /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
        mem_iter = ao2_iterator_init(q->members, 0);
@@ -1382,15 +1397,15 @@ static void update_realtime_members(struct call_queue *q)
        while ((m = ao2_iterator_next(&mem_iter))) {
                if (m->dead) {
                        ao2_unlink(q->members, m);
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                        remove_from_interfaces(m->state_interface);
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        q->membercount--;
                }
                ao2_ref(m, -1);
        }
        ao2_iterator_destroy(&mem_iter);
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
        ast_config_destroy(member_config);
 }
 
@@ -1457,7 +1472,7 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
                return res;
 
        AST_LIST_LOCK(&queues);
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
 
        /* This is our one */
        stat = get_member_status(q, qe->max_penalty);
@@ -1503,7 +1518,7 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
                if (option_debug)
                        ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
        }
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
        AST_LIST_UNLOCK(&queues);
 
        return res;
@@ -1678,10 +1693,10 @@ static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
        /* Thanks to SRT for this contribution */
        /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
 
-       ast_mutex_lock(&qe->parent->lock);
+       ao2_lock(qe->parent);
        oldvalue = qe->parent->holdtime;
        qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
-       ast_mutex_unlock(&qe->parent->lock);
+       ao2_unlock(qe->parent);
 }
 
 
@@ -1693,7 +1708,7 @@ static void leave_queue(struct queue_ent *qe)
 
        if (!(q = qe->parent))
                return;
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
 
        prev = NULL;
        for (cur = q->head; cur; cur = cur->next) {
@@ -1717,14 +1732,11 @@ static void leave_queue(struct queue_ent *qe)
                        prev = cur;
                }
        }
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
 
        if (q->dead && !q->count) {     
                /* It's dead and nobody is in it, so kill it */
-               AST_LIST_LOCK(&queues);
-               AST_LIST_REMOVE(&queues, q, list);
-               AST_LIST_UNLOCK(&queues);
-               destroy_queue(q);
+               remove_queue(q);
        }
 }
 
@@ -1807,7 +1819,7 @@ static int compare_weight(struct call_queue *rq, struct member *member)
        AST_LIST_TRAVERSE(&queues, q, list) {
                if (q == rq) /* don't check myself, could deadlock */
                        continue;
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                if (q->count && q->members) {
                        if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
                                ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
@@ -1818,7 +1830,7 @@ static int compare_weight(struct call_queue *rq, struct member *member)
                                ao2_ref(mem, -1);
                        }
                }
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
                if (found)
                        break;
        }
@@ -1933,9 +1945,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
 
                update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
 
-               ast_mutex_lock(&qe->parent->lock);
+               ao2_lock(qe->parent);
                qe->parent->rrpos++;
-               ast_mutex_unlock(&qe->parent->lock);
+               ao2_unlock(&qe->parent);
 
                (*busies)++;
                return 0;
@@ -2151,7 +2163,7 @@ static int say_periodic_announcement(struct queue_ent *qe)
 
 static void record_abandoned(struct queue_ent *qe)
 {
-       ast_mutex_lock(&qe->parent->lock);
+       ao2_lock(qe->parent);
        manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
                "Queue: %s\r\n"
                "Uniqueid: %s\r\n"
@@ -2161,7 +2173,7 @@ static void record_abandoned(struct queue_ent *qe)
                qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
 
        qe->parent->callsabandoned++;
-       ast_mutex_unlock(&qe->parent->lock);
+       ao2_unlock(&qe->parent);
 }
 
 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
@@ -2463,7 +2475,7 @@ static int is_our_turn(struct queue_ent *qe)
        int avl;
        int idx = 0;
        /* This needs a lock. How many members are available to be served? */
-       ast_mutex_lock(&qe->parent->lock);
+       ao2_lock(qe->parent);
 
        avl = num_available_members(qe->parent);
 
@@ -2479,7 +2491,7 @@ static int is_our_turn(struct queue_ent *qe)
                ch = ch->next;                  
        }
 
-       ast_mutex_unlock(&qe->parent->lock);
+       ao2_unlock(qe->parent);
 
        /* If the queue entry is within avl [the number of available members] calls from the top ... */
        if (ch && idx < avl) {
@@ -2581,13 +2593,13 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
 
 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
 {
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
        time(&member->lastcall);
        member->calls++;
        q->callscompleted++;
        if (callcompletedinsl)
                q->callscompletedinsl++;
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
        return 0;
 }
 
@@ -2865,7 +2877,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
        /* Hold the lock while we setup the outgoing calls */
        if (use_weight)
                AST_LIST_LOCK(&queues);
-       ast_mutex_lock(&qe->parent->lock);
+       ao2_lock(qe->parent);
        if (option_debug)
                ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
                                                        qe->chan->name);
@@ -2883,7 +2895,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                if (!tmp) {
                        ao2_iterator_destroy(&memi);
                        ao2_ref(cur, -1);
-                       ast_mutex_unlock(&qe->parent->lock);
+                       ao2_unlock(qe->parent);
                        if (use_weight)
                                AST_LIST_UNLOCK(&queues);
                        goto out;
@@ -2892,7 +2904,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                        if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
                                ao2_iterator_destroy(&memi);
                                ao2_ref(cur, -1);
-                               ast_mutex_unlock(&qe->parent->lock);
+                               ao2_unlock(qe->parent);
                                if (use_weight)
                                        AST_LIST_UNLOCK(&queues);
                                free(tmp);
@@ -2902,7 +2914,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                        if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
                                ao2_iterator_destroy(&memi);
                                ao2_ref(cur, -1);
-                               ast_mutex_unlock(&qe->parent->lock);
+                               ao2_unlock(qe->parent);
                                if (use_weight)
                                        AST_LIST_UNLOCK(&queues);
                                free(tmp);
@@ -2940,7 +2952,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                        if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
                                ao2_iterator_destroy(&memi);
                                ao2_ref(cur, -1);
-                               ast_mutex_unlock(&qe->parent->lock);
+                               ao2_unlock(qe->parent);
                                if (use_weight)
                                        AST_LIST_UNLOCK(&queues);
                                free(tmp);
@@ -2980,7 +2992,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
        else
                to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
        ++qe->pending;
-       ast_mutex_unlock(&qe->parent->lock);
+       ao2_unlock(qe->parent);
        ring_one(qe, outgoing, &numbusies);
        if (use_weight)
                AST_LIST_UNLOCK(&queues);
@@ -2996,11 +3008,11 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                ast_channel_datastore_free(datastore);
        }
        ast_channel_unlock(qe->chan);
-       ast_mutex_lock(&qe->parent->lock);
+       ao2_lock(qe->parent);
        if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
                store_next(qe, outgoing);
        }
-       ast_mutex_unlock(&qe->parent->lock);
+       ao2_unlock(qe->parent);
        peer = lpeer ? lpeer->chan : NULL;
        if (!peer) {
                qe->pending = 0;
@@ -3038,9 +3050,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                /* Update parameters for the queue */
                time(&now);
                recalc_holdtime(qe, (now - qe->start));
-               ast_mutex_lock(&qe->parent->lock);
+               ao2_lock(qe->parent);
                callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
-               ast_mutex_unlock(&qe->parent->lock);
+               ao2_unlock(qe->parent);
                member = lpeer->member;
                /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
                ao2_ref(member, 1);
@@ -3411,9 +3423,9 @@ static int remove_from_queue(const char *queuename, const char *interface)
 
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                if (strcmp(q->name, queuename)) {
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                        continue;
                }
 
@@ -3422,7 +3434,7 @@ static int remove_from_queue(const char *queuename, const char *interface)
                        if (!mem->dynamic) {
                                res = RES_NOT_DYNAMIC;
                                ao2_ref(mem, -1);
-                               ast_mutex_unlock(&q->lock);
+                               ao2_unlock(q);
                                break;
                        }
                        q->membercount--;
@@ -3442,7 +3454,7 @@ static int remove_from_queue(const char *queuename, const char *interface)
                } else {
                        res = RES_EXISTS;
                }
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
                break;
        }
 
@@ -3465,7 +3477,7 @@ static int add_to_queue(const char *queuename, const char *interface, const char
 
        AST_LIST_LOCK(&queues);
 
-       ast_mutex_lock(&q->lock);
+       ao2_lock(q);
        if ((old_member = interface_exists(q, interface)) == NULL) {
                if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
                        add_to_interfaces(new_member->state_interface);
@@ -3501,7 +3513,7 @@ static int add_to_queue(const char *queuename, const char *interface, const char
                ao2_ref(old_member, -1);
                res = RES_EXISTS;
        }
-       ast_mutex_unlock(&q->lock);
+       ao2_unlock(q);
        AST_LIST_UNLOCK(&queues);
 
        return res;
@@ -3520,7 +3532,7 @@ static int set_member_paused(const char *queuename, const char *interface, int p
 
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
                        if ((mem = interface_exists(q, interface))) {
                                found++;
@@ -3545,7 +3557,7 @@ static int set_member_paused(const char *queuename, const char *interface, int p
                                ao2_ref(mem, -1);
                        }
                }
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        }
        AST_LIST_UNLOCK(&queues);
 
@@ -3579,10 +3591,10 @@ static void reload_queue_members(void)
                queue_name = entry->key + strlen(pm_family) + 2;
 
                AST_LIST_TRAVERSE(&queues, cur_queue, list) {
-                       ast_mutex_lock(&cur_queue->lock);
+                       ao2_lock(cur_queue);
                        if (!strcmp(queue_name, cur_queue->name))
                                break;
-                       ast_mutex_unlock(&cur_queue->lock);
+                       ao2_unlock(cur_queue);
                }
                
                if (!cur_queue)
@@ -3595,7 +3607,7 @@ static void reload_queue_members(void)
                        ast_db_del(pm_family, queue_name);
                        continue;
                } else
-                       ast_mutex_unlock(&cur_queue->lock);
+                       ao2_unlock(cur_queue);
 
                if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
                        continue;
@@ -3972,7 +3984,7 @@ static int queue_exec(struct ast_channel *chan, void *data)
                AST_APP_ARG(agi);
        );
        /* Our queue entry */
-       struct queue_ent qe;
+       struct queue_ent qe = { 0 };
        
        if (ast_strlen_zero(data)) {
                ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
@@ -3985,7 +3997,6 @@ static int queue_exec(struct ast_channel *chan, void *data)
        lu = ast_module_user_add(chan);
 
        /* Setup our queue entry */
-       memset(&qe, 0, sizeof(qe));
        qe.start = time(NULL);
 
        /* set the expire time based on the supplied timeout; */
@@ -4213,6 +4224,12 @@ stop:
                set_queue_result(chan, reason);
                res = 0;
        }
+       if (qe.parent) {
+               /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
+                * This ref must be taken away right before the queue_ent is destroyed.  In this case
+                * the queue_ent is about to be returned on the stack */
+               ao2_ref(qe.parent, -1);
+       }
        ast_module_user_remove(lu);
 
        return res;
@@ -4236,7 +4253,7 @@ static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, c
        lu = ast_module_user_add(chan);
 
        if ((q = load_realtime_queue(data))) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                mem_iter = ao2_iterator_init(q->members, 0);
                while ((m = ao2_iterator_next(&mem_iter))) {
                        /* Count the agents who are logged in and presently answering calls */
@@ -4246,7 +4263,7 @@ static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, c
                        ao2_ref(m, -1);
                }
                ao2_iterator_destroy(&mem_iter);
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        } else
                ast_log(LOG_WARNING, "queue %s was not found\n", data);
 
@@ -4275,7 +4292,7 @@ static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd,
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
                if (!strcasecmp(q->name, data)) {
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        break;
                }
        }
@@ -4283,7 +4300,7 @@ static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd,
 
        if (q) {
                count = q->count;
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
                /* if the queue is realtime but was not found in memory, this
                 * means that the queue had been deleted from memory since it was 
@@ -4318,7 +4335,7 @@ static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, c
        AST_LIST_LOCK(&queues);
        AST_LIST_TRAVERSE(&queues, q, list) {
                if (!strcasecmp(q->name, data)) {
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        break;
                }
        }
@@ -4345,7 +4362,7 @@ static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, c
                        ao2_ref(m, -1);
                }
                ao2_iterator_destroy(&mem_iter);
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        } else
                ast_log(LOG_WARNING, "queue %s was not found\n", data);
 
@@ -4459,12 +4476,12 @@ static int reload_queues(void)
                                new = 0;
                        if (q) {
                                if (!new)
-                                       ast_mutex_lock(&q->lock);
+                                       ao2_lock(q);
                                /* Check if a queue with this name already exists */
                                if (q->found) {
                                        ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
                                        if (!new)
-                                               ast_mutex_unlock(&q->lock);
+                                               ao2_unlock(q);
                                        continue;
                                }
                                /* Re-initialize the queue, and clear statistics */
@@ -4564,7 +4581,7 @@ static int reload_queues(void)
                                if (new) {
                                        AST_LIST_INSERT_HEAD(&queues, q, list);
                                } else
-                                       ast_mutex_unlock(&q->lock);
+                                       ao2_unlock(q);
                        }
                }
        }
@@ -4572,12 +4589,9 @@ static int reload_queues(void)
        AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
                if (q->dead) {
                        AST_LIST_REMOVE_CURRENT(&queues, list);
-                       if (!q->count)
-                               destroy_queue(q);
-                       else
-                               ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
+                       ao2_ref(q, -1);
                } else {
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        mem_iter = ao2_iterator_init(q->members, 0);
                        while ((cur = ao2_iterator_next(&mem_iter))) {
                                if (cur->dynamic)
@@ -4586,7 +4600,7 @@ static int reload_queues(void)
                                ao2_ref(cur, -1);
                        }
                        ao2_iterator_destroy(&mem_iter);
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                }
        }
        AST_LIST_TRAVERSE_SAFE_END;
@@ -4647,10 +4661,10 @@ static int __queues_show(struct mansession *s, int manager, int fd, int argc, ch
                return RESULT_SUCCESS;
        }
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
                if (queue_show) {
                        if (strcasecmp(q->name, argv[2]) != 0) {
-                               ast_mutex_unlock(&q->lock);
+                               ao2_unlock(q);
                                if (!AST_LIST_NEXT(q, list)) {
                                        ast_cli(fd, "No such queue: %s.%s",argv[2], term);
                                        break;
@@ -4737,7 +4751,7 @@ static int __queues_show(struct mansession *s, int manager, int fd, int argc, ch
                        astman_append(s, "%s", term);
                else
                        ast_cli(fd, "%s", term);
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
                if (queue_show)
                        break;
        }
@@ -4811,7 +4825,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
                snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 
        AST_LIST_TRAVERSE(&queues, q, list) {
-               ast_mutex_lock(&q->lock);
+               ao2_lock(q);
 
                /* List queue properties */
                if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
@@ -4870,7 +4884,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
                                        (long) (now - qe->start), idText);
                        }
                }
-               ast_mutex_unlock(&q->lock);
+               ao2_unlock(q);
        }
 
        astman_append(s,
@@ -5139,13 +5153,13 @@ static char *complete_queue_remove_member(const char *line, const char *word, in
        /* here is the case for 3, <member> */
        if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
                AST_LIST_TRAVERSE(&queues, q, list) {
-                       ast_mutex_lock(&q->lock);
+                       ao2_lock(q);
                        mem_iter = ao2_iterator_init(q->members, 0);
                        while ((m = ao2_iterator_next(&mem_iter))) {
                                if (++which > state) {
                                        char *tmp;
                                        ao2_iterator_destroy(&mem_iter);
-                                       ast_mutex_unlock(&q->lock);
+                                       ao2_unlock(q);
                                        tmp = ast_strdup(m->interface);
                                        ao2_ref(m, -1);
                                        return tmp;
@@ -5153,7 +5167,7 @@ static char *complete_queue_remove_member(const char *line, const char *word, in
                                ao2_ref(m, -1);
                        }
                        ao2_iterator_destroy(&mem_iter);
-                       ast_mutex_unlock(&q->lock);
+                       ao2_unlock(q);
                }
        }