Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 81644) +++ apps/app_queue.c (working copy) @@ -92,6 +92,7 @@ #include "asterisk/astdb.h" #include "asterisk/devicestate.h" #include "asterisk/stringfields.h" +#include "asterisk/astobj2.h" enum { QUEUE_STRATEGY_RINGALL = 0, @@ -324,7 +325,6 @@ time_t lastcall; /*!< When last successful call was hungup */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */ unsigned int delme:1; /*!< Flag to delete entry on reload */ - struct member *next; /*!< Next member */ }; struct member_interface { @@ -396,7 +396,7 @@ int memberdelay; /*!< Seconds to delay connecting member to caller */ int autofill; /*!< Ignore the head call status and ring an available agent */ - struct member *members; /*!< Head of the list of members */ + struct ao2_container *members; /*!< Head of the list of members */ int membercount; /*!< Number of members in queue */ struct queue_ent *head; /*!< Head of the list of callers */ AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */ @@ -481,28 +481,38 @@ static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty) { struct member *member; + struct ao2_iterator i; enum queue_member_status result = QUEUE_NO_MEMBERS; ast_mutex_lock(&q->lock); - for (member = q->members; member; member = member->next) { - if (max_penalty && (member->penalty > max_penalty)) + i = ao2_iterator_init(q->members, 0); + while ((member = ao2_iterator_next(&i))) { + if (max_penalty && (member->penalty > max_penalty)) { + ao2_ref(member, -1); continue; + } - if (member->paused) continue; + if (member->paused) { + ao2_ref(member, -1); + continue; + } switch (member->status) { case AST_DEVICE_INVALID: /* nothing to do */ + ao2_ref(member, -1); break; case AST_DEVICE_UNAVAILABLE: result = QUEUE_NO_REACHABLE_MEMBERS; + ao2_ref(member, -1); break; default: ast_mutex_unlock(&q->lock); + ao2_ref(member, -1); return QUEUE_NORMAL; } } - + ast_mutex_unlock(&q->lock); return result; } @@ -517,6 +527,7 @@ struct call_queue *q; struct statechange *sc = data; struct member *cur; + struct ao2_iterator i; struct member_interface *curint; char *loc; char *technology; @@ -556,7 +567,8 @@ AST_LIST_LOCK(&queues); AST_LIST_TRAVERSE(&queues, q, list) { ast_mutex_lock(&q->lock); - for (cur = q->members; cur; cur = cur->next) { + i = ao2_iterator_init(q->members, 0); + while ((cur = ao2_iterator_next(&i))) { char *interface; char *slash_pos; interface = ast_strdupa(cur->interface); @@ -564,13 +576,17 @@ if ((slash_pos = strchr(slash_pos + 1, '/'))) *slash_pos = '\0'; - if (strcasecmp(sc->dev, interface)) + if (strcasecmp(sc->dev, interface)) { + ao2_ref(cur, -1); continue; + } if (cur->status != sc->state) { cur->status = sc->state; - if (q->maskmemberstatus) + if (q->maskmemberstatus) { + ao2_ref(cur, -1); continue; + } manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", "Queue: %s\r\n" @@ -585,6 +601,7 @@ q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); } + ao2_ref(cur, -1); } ast_mutex_unlock(&q->lock); } @@ -623,7 +640,7 @@ { struct member *cur; - if ((cur = ast_calloc(1, sizeof(*cur)))) { + if ((cur = ao2_alloc(sizeof(*cur), NULL))) { cur->penalty = penalty; cur->paused = paused; ast_copy_string(cur->interface, interface, sizeof(cur->interface)); @@ -647,6 +664,34 @@ return q; } +static int compress_char(const char c) +{ + if (c < 32) + return 0; + else if (c > 96) + return c - 64; + else + return c - 32; +} + +static int member_hash_fn(const void *obj, const int flags) +{ + const struct member *mem = obj; + const char *chname = strchr(mem->interface, '/'); + int ret = 0, i; + if (!chname) + chname = mem->interface; + for (i = 0; i < 5 && chname[i]; i++) + ret += compress_char(chname[i]) << (i * 6); + return ret; +} + +static int member_cmp_fn(void *obj1, void *obj2, int flags) +{ + struct member *mem1 = obj1, *mem2 = obj2; + return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH; +} + static void init_queue(struct call_queue *q) { int i; @@ -668,6 +713,7 @@ q->context[0] = '\0'; q->monfmt[0] = '\0'; q->periodicannouncefrequency = 0; + q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); q->membercount = 0; ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); @@ -729,9 +775,9 @@ AST_LIST_LOCK(&queues); AST_LIST_TRAVERSE(&queues, q, list) { ast_mutex_lock(&q->lock); - for (mem = q->members; mem && !ret; mem = mem->next) { - if (!strcasecmp(interface, mem->interface)) - ret = 1; + if ((mem = ao2_find(q->members, (char *)interface, 0))) { + ao2_ref(mem, -1); + ret = 1; } ast_mutex_unlock(&q->lock); if (ret) @@ -930,7 +976,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str) { - struct member *m, *prev_m; + struct member *m; int penalty = 0; int paused = 0; @@ -947,9 +993,7 @@ } /* Find the member, or the place to put a new one. */ - for (m = q->members, prev_m = NULL; - m && strcmp(m->interface, interface); - prev_m = m, m = m->next); + m = ao2_find(q->members, interface, 0); /* Create a new one if not found, else update penalty */ if (!m) { @@ -957,11 +1001,7 @@ m->dead = 0; m->realtime = 1; add_to_interfaces(interface); - if (prev_m) { - prev_m->next = m; - } else { - q->members = m; - } + ao2_link(q->members, m); q->membercount++; } } else { @@ -969,26 +1009,23 @@ if (paused_str) m->paused = paused; m->penalty = penalty; + ao2_ref(m, -1); } } static void free_members(struct call_queue *q, int all) { /* Free non-dynamic members */ - struct member *curm, *next, *prev = NULL; + struct member *cur; + struct ao2_iterator i = ao2_iterator_init(q->members, 0); - for (curm = q->members; curm; curm = next) { - next = curm->next; - if (all || !curm->dynamic) { - if (prev) - prev->next = next; - else - q->members = next; - remove_from_interfaces(curm->interface); + while ((cur = ao2_iterator_next(&i))) { + if (all || !cur->dynamic) { + ao2_unlink(q->members, cur); + remove_from_interfaces(cur->interface); q->membercount--; - free(curm); - } else - prev = curm; + } + ao2_ref(cur, -1); } } @@ -996,6 +1033,7 @@ { free_members(q, 1); ast_mutex_destroy(&q->lock); + ao2_ref(q->members, -1); free(q); } @@ -1006,7 +1044,8 @@ { struct ast_variable *v; struct call_queue *q; - struct member *m, *prev_m, *next_m; + struct member *m; + struct ao2_iterator i; char *interface = NULL; char *tmp, *tmp_name; char tmpbuf[64]; /* Must be longer than the longest queue param name. */ @@ -1085,9 +1124,12 @@ /* Temporarily set realtime members dead so we can detect deleted ones. * Also set the membercount correctly for realtime*/ - for (m = q->members; m; m = m->next, q->membercount++) { + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { + q->membercount++; if (m->realtime) m->dead = 1; + ao2_ref(m, -1); } while ((interface = ast_category_browse(member_config, interface))) { @@ -1098,25 +1140,16 @@ } /* Delete all realtime members that have been deleted in DB. */ - m = q->members; - prev_m = NULL; - while (m) { - next_m = m->next; + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { if (m->dead) { - if (prev_m) { - prev_m->next = next_m; - } else { - q->members = next_m; - } + ao2_unlink(q->members, m); ast_mutex_unlock(&q->lock); remove_from_interfaces(m->interface); ast_mutex_lock(&q->lock); q->membercount--; - free(m); - } else { - prev_m = m; } - m = next_m; + ao2_ref(m, -1); } ast_mutex_unlock(&q->lock); @@ -1146,8 +1179,9 @@ static void update_realtime_members(struct call_queue *q) { struct ast_config *member_config = NULL; - struct member *m, *prev_m, *next_m; + struct member *m; char *interface = NULL; + struct ao2_iterator i; member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL); if (!member_config) { @@ -1160,9 +1194,11 @@ ast_mutex_lock(&q->lock); /* Temporarily set realtime members dead so we can detect deleted ones.*/ - for (m = q->members; m; m = m->next) { + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { if (m->realtime) m->dead = 1; + ao2_ref(m, -1); } while ((interface = ast_category_browse(member_config, interface))) { @@ -1173,25 +1209,16 @@ } /* Delete all realtime members that have been deleted in DB. */ - m = q->members; - prev_m = NULL; - while (m) { - next_m = m->next; + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { if (m->dead) { - if (prev_m) { - prev_m->next = next_m; - } else { - q->members = next_m; - } + ao2_unlink(q->members, m); ast_mutex_unlock(&q->lock); remove_from_interfaces(m->interface); ast_mutex_lock(&q->lock); q->membercount--; - free(m); - } else { - prev_m = m; } - m = next_m; + ao2_ref(m, -1); } ast_mutex_unlock(&q->lock); } @@ -1541,13 +1568,17 @@ static int update_status(struct call_queue *q, struct member *member, int status) { struct member *cur; + struct ao2_iterator i; /* Since a reload could have taken place, we have to traverse the list to be sure it's still valid */ ast_mutex_lock(&q->lock); - for (cur = q->members; cur; cur = cur->next) { - if (member != cur) + i = ao2_iterator_init(q->members, 0); + while ((cur = ao2_iterator_next(&i))) { + if (member != cur) { + ao2_ref(cur, -1); continue; + } cur->status = status; if (!q->maskmemberstatus) { @@ -1564,6 +1595,7 @@ q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime": "static", cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); } + ao2_ref(cur, -1); } ast_mutex_unlock(&q->lock); return 0; @@ -1597,16 +1629,13 @@ continue; ast_mutex_lock(&q->lock); if (q->count && q->members) { - for (mem = q->members; mem; mem = mem->next) { - if (strcmp(mem->interface, member->interface)) - continue; - + if ((mem = ao2_find(q->members, member->interface, 0))) { ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); if (q->weight > rq->weight) { ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count); found = 1; - break; } + ao2_ref(mem, -1); } } ast_mutex_unlock(&q->lock); @@ -2199,7 +2228,8 @@ ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl); avl = 1; } else { - for (cur = qe->parent->members; cur; cur = cur->next) { + struct ao2_iterator i = ao2_iterator_init(qe->parent->members, 0); + while ((cur = ao2_iterator_next(&i))) { switch (cur->status) { case AST_DEVICE_NOT_INUSE: case AST_DEVICE_UNKNOWN: @@ -2207,6 +2237,7 @@ avl++; break; } + ao2_ref(cur, -1); } } @@ -2294,20 +2325,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl) { - struct member *cur; - - /* Since a reload could have taken place, we have to traverse the list to - be sure it's still valid */ ast_mutex_lock(&q->lock); - cur = q->members; - while (cur) { - if (member == cur) { - time(&cur->lastcall); - cur->calls++; - break; - } - cur = cur->next; - } + time(&member->lastcall); + member->calls++; q->callscompleted++; if (callcompletedinsl) q->callscompletedinsl++; @@ -2405,6 +2425,7 @@ char vars[2048]; int forwardsallowed = 1; int callcompletedinsl; + struct ao2_iterator memi; memset(&bridge_config, 0, sizeof(bridge_config)); time(&now); @@ -2452,13 +2473,13 @@ ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", qe->chan->name); ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); - cur = qe->parent->members; if (!ast_strlen_zero(qe->announce)) announce = qe->announce; if (!ast_strlen_zero(announceoverride)) announce = announceoverride; - for (; cur; cur = cur->next) { + memi = ao2_iterator_init(qe->parent->members, 0); + while ((cur = ao2_iterator_next(&memi))) { struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); if (!tmp) { @@ -2468,7 +2489,7 @@ goto out; } tmp->stillgoing = -1; - tmp->member = cur; /* Never directly dereference! Could change on reload */ + tmp->member = cur; tmp->oldstatus = cur->status; tmp->lastcall = cur->lastcall; ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); @@ -2484,6 +2505,7 @@ if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) break; } else { + ao2_ref(cur, -1); free(tmp); } } @@ -2797,13 +2819,16 @@ static struct member *interface_exists(struct call_queue *q, const char *interface) { struct member *mem; + struct ao2_iterator i; if (!q) return NULL; - for (mem = q->members; mem; mem = mem->next) { + i = ao2_iterator_init(q->members, 0); + while ((mem = ao2_iterator_next(&i))) { if (!strcasecmp(interface, mem->interface)) return mem; + ao2_ref(mem, -1); } return NULL; @@ -2821,19 +2846,25 @@ char value[PM_MAX_LEN]; int value_len = 0; int res; + struct ao2_iterator i; memset(value, 0, sizeof(value)); if (!pm_queue) return; - for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) { - if (!cur_member->dynamic) + i = ao2_iterator_init(pm_queue->members, 0); + while ((cur_member = ao2_iterator_next(&i))) { + if (!cur_member->dynamic) { + ao2_ref(cur_member, -1); continue; + } - res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s", - cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, - cur_member->next ? "|" : ""); + res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s", + value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername); + + ao2_ref(cur_member, -1); + if (res != strlen(value + value_len)) { ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); break; @@ -2852,7 +2883,7 @@ static int remove_from_queue(const char *queuename, const char *interface) { struct call_queue *q; - struct member *last_member, *look; + struct member *mem; int res = RES_NOSUCHQUEUE; AST_LIST_LOCK(&queues); @@ -2863,28 +2894,15 @@ continue; } - if ((last_member = interface_exists(q, interface))) { - if ((look = q->members) == last_member) { - q->members = last_member->next; - q->membercount--; - } else { - while (look != NULL) { - if (look->next == last_member) { - look->next = last_member->next; - q->membercount--; - break; - } else { - look = look->next; - } - } - } + if ((mem = ao2_find(q->members, (char *)interface, OBJ_UNLINK))) { + q->membercount--; manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", "Queue: %s\r\n" "Location: %s\r\n" "MemberName: %s\r\n", - q->name, last_member->interface, last_member->membername); - free(last_member); - + q->name, mem->interface, mem->membername); + ao2_ref(mem, -1); + if (queue_persistent_members) dump_queue_members(q); @@ -2908,7 +2926,7 @@ static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump) { struct call_queue *q; - struct member *new_member; + struct member *new_member, *old_member; int res = RES_NOSUCHQUEUE; /* \note Ensure the appropriate realtime queue is loaded. Note that this @@ -2919,12 +2937,11 @@ AST_LIST_LOCK(&queues); ast_mutex_lock(&q->lock); - if (interface_exists(q, interface) == NULL) { + if ((old_member = interface_exists(q, interface)) == NULL) { add_to_interfaces(interface); if ((new_member = create_queue_member(interface, membername, penalty, paused))) { new_member->dynamic = 1; - new_member->next = q->members; - q->members = new_member; + ao2_link(q->members, new_member); q->membercount++; manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", "Queue: %s\r\n" @@ -2949,6 +2966,7 @@ res = RES_OUTOFMEMORY; } } else { + ao2_ref(old_member, -1); res = RES_EXISTS; } ast_mutex_unlock(&q->lock); @@ -2992,6 +3010,7 @@ "MemberName: %s\r\n" "Paused: %d\r\n", q->name, mem->interface, mem->membername, paused); + ao2_ref(mem, -1); } } ast_mutex_unlock(&q->lock); @@ -3634,6 +3653,7 @@ struct call_queue *q; struct ast_module_user *lu; struct member *m; + struct ao2_iterator i; buf[0] = '\0'; @@ -3654,11 +3674,13 @@ AST_LIST_UNLOCK(&queues); if (q) { - for (m = q->members; m; m = m->next) { + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { /* Count the agents who are logged in and presently answering calls */ if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { count++; } + ao2_ref(m, -1); } ast_mutex_unlock(&q->lock); } else @@ -3732,8 +3754,9 @@ if (q) { int buflen = 0, count = 0; + struct ao2_iterator i = ao2_iterator_init(q->members, 0); - for (m = q->members; m; m = m->next) { + while ((m = ao2_iterator_next(&i))) { /* strcat() is always faster than printf() */ if (count++) { strncat(buf + buflen, ",", len - buflen - 1); @@ -3746,6 +3769,7 @@ ast_log(LOG_WARNING, "Truncating list\n"); break; } + ao2_ref(m, -1); } ast_mutex_unlock(&q->lock); } else @@ -3801,7 +3825,8 @@ struct ast_config *cfg; char *cat, *tmp; struct ast_variable *var; - struct member *prev, *cur, *newm, *next; + struct member *cur, *newm; + struct ao2_iterator i; int new; const char *general_val = NULL; char parse[80]; @@ -3861,10 +3886,12 @@ /* Re-initialize the queue, and clear statistics */ init_queue(q); clear_queue(q); - for (cur = q->members; cur; cur = cur->next) { + i = ao2_iterator_init(q->members, 0); + while ((cur = ao2_iterator_next(&i))) { if (!cur->dynamic) { cur->delme = 1; } + ao2_ref(cur, -1); } for (var = ast_variable_browse(cfg, cat); var; var = var->next) { if (!strcasecmp(var->name, "member")) { @@ -3891,54 +3918,35 @@ membername = interface; /* Find the old position in the list */ - for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) { - if (!strcmp(cur->interface, interface)) { - break; - } - } + cur = ao2_find(q->members, interface, OBJ_UNLINK); newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); + ao2_link(q->members, newm); - if (cur) { - /* Delete it now */ - newm->next = cur->next; - if (prev) { - prev->next = newm; - } else { - q->members = newm; - } - free(cur); - } else { + if (cur) + ao2_ref(cur, -1); + else { /* Add them to the master int list if necessary */ add_to_interfaces(interface); - newm->next = q->members; - q->members = newm; + q->membercount++; } - q->membercount++; } else { queue_set_param(q, var->name, var->value, var->lineno, 1); } } /* Free remaining members marked as delme */ - for (prev = NULL, cur = q->members; - cur; - cur = next) { - next = cur->next; - - if (!cur->delme) { - prev = cur; + i = ao2_iterator_init(q->members, 0); + while ((cur = ao2_iterator_next(&i))) { + if (! cur->delme) { + ao2_ref(cur, -1); continue; } - if (prev) - prev->next = next; - else - q->members = next; - remove_from_interfaces(cur->interface); q->membercount--; - free(cur); + ao2_unlink(q->members, cur); + ao2_ref(cur, -1); } if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) @@ -3961,10 +3969,12 @@ ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); } else { ast_mutex_lock(&q->lock); - for (cur = q->members; cur; cur = cur->next) { - if(cur->dynamic) + i = ao2_iterator_init(q->members, 0); + while ((cur = ao2_iterator_next(&i))) { + if (cur->dynamic) q->membercount++; cur->status = ast_device_state(cur->interface); + ao2_ref(cur, -1); } ast_mutex_unlock(&q->lock); } @@ -3986,6 +3996,7 @@ size_t max_left; float sl = 0; char *term = manager ? "\r\n" : "\n"; + struct ao2_iterator i; time(&now); if (argc == 2) @@ -4044,12 +4055,13 @@ else ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); - if (q->members) { + if (ao2_container_count(q->members)) { if (s) astman_append(s, " Members: %s", term); else ast_cli(fd, " Members: %s", term); - for (mem = q->members; mem; mem = mem->next) { + i = ao2_iterator_init(q->members, 0); + while ((mem = ao2_iterator_next(&i))) { max_buf[0] = '\0'; max = max_buf; max_left = sizeof(max_buf); @@ -4071,6 +4083,7 @@ astman_append(s, " %s%s%s", mem->interface, max_buf, term); else ast_cli(fd, " %s%s%s", mem->interface, max_buf, term); + ao2_ref(mem, -1); } } else if (s) astman_append(s, " No Members%s", term); @@ -4165,6 +4178,7 @@ struct queue_ent *qe; float sl = 0; struct member *mem; + struct ao2_iterator i; astman_send_ack(s, m, "Queue status will follow"); time(&now); @@ -4193,7 +4207,8 @@ q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl, q->weight, idText); /* List Queue Members */ - for (mem = q->members; mem; mem = mem->next) { + i = ao2_iterator_init(q->members, 0); + while ((mem = ao2_iterator_next(&i))) { if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { astman_append(s, "Event: QueueMember\r\n" "Queue: %s\r\n" @@ -4209,6 +4224,7 @@ q->name, mem->interface, mem->dynamic ? "dynamic" : "static", mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); } + ao2_ref(mem, -1); } /* List Queue Entries */ pos = 1; @@ -4473,6 +4489,7 @@ int which = 0; struct call_queue *q; struct member *m; + struct ao2_iterator i; /* 0 - queue; 1 - remove; 2 - member; 3 - ; 4 - from; 5 - */ if (pos > 5 || pos < 3) @@ -4487,10 +4504,14 @@ if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ AST_LIST_TRAVERSE(&queues, q, list) { ast_mutex_lock(&q->lock); - for (m = q->members ; m ; m = m->next) { + i = ao2_iterator_init(q->members, 0); + while ((m = ao2_iterator_next(&i))) { if (++which > state) { + char *tmp; ast_mutex_unlock(&q->lock); - return ast_strdup(m->interface); + tmp = ast_strdup(m->interface); + ao2_ref(m, -1); + return tmp; } } ast_mutex_unlock(&q->lock);