diff --git a/apps/app_queue.c b/apps/app_queue.c index aca8aaf24c..351e6b3319 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -670,11 +670,13 @@ static enum queue_member_status get_member_status(struct call_queue *q, int max_ } else { ao2_unlock(q); ao2_ref(member, -1); + ao2_iterator_destroy(&mem_iter); return QUEUE_NORMAL; } break; } } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); return result; @@ -756,7 +758,6 @@ static void *handle_statechange(struct statechange *sc) if ((slash_pos = strchr(interface, '/'))) if ((slash_pos = strchr(slash_pos + 1, '/'))) *slash_pos = '\0'; - if (!strcasecmp(interface, sc->dev)) break; } @@ -1025,7 +1026,7 @@ static int interface_exists_global(const char *interface, int lock_queue_contain int ret = 0; ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); - queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK); + queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : AO2_ITERATOR_DONTLOCK); while ((q = ao2_iterator_next(&queue_iter))) { ao2_lock(q); mem_iter = ao2_iterator_init(q->members, 0); @@ -1038,7 +1039,9 @@ static int interface_exists_global(const char *interface, int lock_queue_contain } ao2_unlock(q); queue_unref(q); + ao2_iterator_destroy(&mem_iter); } + ao2_iterator_destroy(&queue_iter); return ret; } @@ -1415,6 +1418,7 @@ static void free_members(struct call_queue *q, int all) } ao2_ref(cur, -1); } + ao2_iterator_destroy(&mem_iter); } /*! \brief Free queue's member list then its string fields */ @@ -1562,6 +1566,7 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as m->dead = 1; ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); while ((interface = ast_category_browse(member_config, interface))) { rt_handle_member_record(q, interface, @@ -1581,6 +1586,7 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); @@ -1680,6 +1686,7 @@ static void update_realtime_members(struct call_queue *q) m->dead = 1; ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); while ((interface = ast_category_browse(member_config, interface))) { rt_handle_member_record(q, interface, @@ -1699,6 +1706,7 @@ static void update_realtime_members(struct call_queue *q) } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); ao2_unlock(queues); ast_config_destroy(member_config); @@ -2095,6 +2103,7 @@ static int num_available_members(struct call_queue *q) break; } } + ao2_iterator_destroy(&mem_iter); return avl; } @@ -2133,6 +2142,7 @@ static int compare_weight(struct call_queue *rq, struct member *member) break; } } + ao2_iterator_destroy(&queue_iter); return found; } @@ -3021,6 +3031,7 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom ao2_unlock(qtmp); ao2_ref(qtmp, -1); } + ao2_iterator_destroy(&queue_iter); } else { ao2_lock(q); time(&member->lastcall); @@ -3425,6 +3436,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce if (!tmp) { ao2_ref(cur, -1); ao2_unlock(qe->parent); + ao2_iterator_destroy(&memi); if (use_weight) ao2_unlock(queues); goto out; @@ -3433,6 +3445,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_ref(cur, -1); ao2_unlock(qe->parent); + ao2_iterator_destroy(&memi); if (use_weight) ao2_unlock(queues); free(tmp); @@ -3442,6 +3455,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { ao2_ref(cur, -1); ao2_unlock(&qe->parent); + ao2_iterator_destroy(&memi); if (use_weight) ao2_unlock(queues); free(tmp); @@ -3479,6 +3493,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_ref(cur, -1); ao2_unlock(qe->parent); + ao2_iterator_destroy(&memi); if (use_weight) ao2_unlock(queues); free(tmp); @@ -4036,10 +4051,13 @@ static struct member *interface_exists(struct call_queue *q, const char *interfa mem_iter = ao2_iterator_init(q->members, 0); while ((mem = ao2_iterator_next(&mem_iter))) { - if (!strcasecmp(interface, mem->interface)) + if (!strcasecmp(interface, mem->interface)) { + ao2_iterator_destroy(&mem_iter); return mem; + } ao2_ref(mem, -1); } + ao2_iterator_destroy(&mem_iter); return NULL; } @@ -4080,6 +4098,7 @@ static void dump_queue_members(struct call_queue *pm_queue) } value_len += res; } + ao2_iterator_destroy(&mem_iter); if (value_len && !cur_member) { if (ast_db_put(pm_family, pm_queue->name, value)) @@ -4267,6 +4286,7 @@ static int set_member_paused(const char *queuename, const char *interface, const ao2_unlock(q); queue_unref(q); } + ao2_iterator_destroy(&queue_iter); return found ? RESULT_SUCCESS : RESULT_FAILURE; } @@ -4305,6 +4325,7 @@ static int set_member_penalty(char *queuename, char *interface, int penalty) ao2_unlock(q); queue_unref(q); } + ao2_iterator_destroy(&queue_iter); if (foundinterface) { return RESULT_SUCCESS; @@ -5097,6 +5118,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); } else if (!strcasecmp(option, "free")) { mem_iter = ao2_iterator_init(q->members, 0); while ((m = ao2_iterator_next(&mem_iter))) { @@ -5106,6 +5128,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); } else /* must be "count" */ count = q->membercount; ao2_unlock(q); @@ -5151,6 +5174,7 @@ static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, cha } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); queue_unref(q); } else @@ -5234,6 +5258,7 @@ static int queue_function_queuememberlist(struct ast_channel *chan, const char * } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); queue_unref(q); } else @@ -5463,7 +5488,7 @@ static int reload_queues(int reload) ao2_lock(queues); use_weight=0; /* Mark all queues as dead for the moment */ - queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK); + queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); while ((q = ao2_iterator_next(&queue_iter))) { if (!q->realtime) { q->dead = 1; @@ -5700,7 +5725,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv) } } - queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK); + queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); ao2_lock(queues); while ((q = ao2_iterator_next(&queue_iter))) { float sl; @@ -5764,6 +5789,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv) do_print(s, fd, out->str); ao2_ref(mem, -1); } + ao2_iterator_destroy(&mem_iter); } if (!q->head) do_print(s, fd, " No Callers"); @@ -5783,6 +5809,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, char **argv) ao2_unlock(q); queue_unref(q); /* Unref the iterator's reference */ } + ao2_iterator_destroy(&queue_iter); ao2_unlock(queues); if (!found) { if (argc == 3) @@ -5811,6 +5838,7 @@ static char *complete_queue(const char *line, const char *word, int pos, int sta } queue_unref(q); } + ao2_iterator_destroy(&queue_iter); return ret; } @@ -5919,6 +5947,7 @@ static int manager_queues_summary(struct mansession *s, const struct message *m) } ao2_ref(mem, -1); } + ao2_iterator_destroy(&mem_iter); for (qe = q->head; qe; qe = qe->next) { if ((now - qe->start) > qlongestholdtime) { qlongestholdtime = now - qe->start; @@ -5939,6 +5968,7 @@ static int manager_queues_summary(struct mansession *s, const struct message *m) ao2_unlock(q); queue_unref(q); } + ao2_iterator_destroy(&queue_iter); astman_append(s, "Event: QueueSummaryComplete\r\n" "%s" @@ -6011,6 +6041,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m) } ao2_ref(mem, -1); } + ao2_iterator_destroy(&mem_iter); /* List Queue Entries */ pos = 1; for (qe = q->head; qe; qe = qe->next) { @@ -6032,6 +6063,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m) ao2_unlock(q); queue_unref(q); } + ao2_iterator_destroy(&queue_iter); astman_append(s, "Event: QueueStatusComplete\r\n" @@ -6330,13 +6362,17 @@ static char *complete_queue_remove_member(const char *line, const char *word, in tmp = ast_strdup(m->interface); ao2_ref(m, -1); queue_unref(q); + ao2_iterator_destroy(&mem_iter); + ao2_iterator_destroy(&queue_iter); return tmp; } ao2_ref(m, -1); } + ao2_iterator_destroy(&mem_iter); ao2_unlock(q); queue_unref(q); } + ao2_iterator_destroy(&queue_iter); return NULL; } @@ -6661,6 +6697,7 @@ static int unload_module(void) ao2_unlink(queues, q); queue_unref(q); } + ao2_iterator_destroy(&q_iter); ao2_ref(queues, -1); return res; diff --git a/channels/chan_console.c b/channels/chan_console.c index b3a48a5c5e..ab91131c77 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -1017,6 +1017,7 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a console_pvt_unlock(pvt); unref_pvt(pvt); } + ao2_iterator_destroy(&i); ast_cli(a->fd, "=============================================================\n\n"); @@ -1168,9 +1169,12 @@ static char *cli_console_active(struct ast_cli_entry *e, int cmd, struct ast_cli if (++x > a->n && !strncasecmp(pvt->name, a->word, strlen(a->word))) res = ast_strdup(pvt->name); unref_pvt(pvt); - if (res) + if (res) { + ao2_iterator_destroy(&i); return res; + } } + ao2_iterator_destroy(&i); } return NULL; } @@ -1370,6 +1374,7 @@ static void destroy_pvts(void) } unref_pvt(pvt); } + ao2_iterator_destroy(&i); } /*! @@ -1440,6 +1445,7 @@ static void stop_streams(void) stop_stream(pvt); unref_pvt(pvt); } + ao2_iterator_destroy(&i); } static int unload_module(void) diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index aad43194bd..660303df34 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1451,6 +1451,7 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len) } peer_unref(peer); } + ao2_iterator_destroy(&i); if (!peer) { peer = realtime_peer(NULL, &sin); @@ -2191,6 +2192,7 @@ static char *handle_cli_iax2_show_callno_limits(struct ast_cli_entry *e, int cmd } ao2_ref(peercnt, -1); } + ao2_iterator_destroy(&i); if (a->argc == 4) { ast_cli(a->fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used); @@ -3440,6 +3442,7 @@ static char *complete_iax2_show_peer(const char *line, const char *word, int pos } peer_unref(peer); } + ao2_iterator_destroy(&i); return res; } @@ -5142,6 +5145,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin) } peer_unref(peer); } + ao2_iterator_destroy(&i); return res; } @@ -6036,6 +6040,7 @@ static char *handle_cli_iax2_show_users(struct ast_cli_entry *e, int cmd, struct user->contexts ? user->contexts->context : context, user->ha ? "Yes" : "No", pstr); } + ao2_iterator_destroy(&i); if (havepattern) regfree(®exbuf); @@ -6161,6 +6166,7 @@ static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc peer->encmethods ? "(E)" : " ", status, term); total_peers++; } + ao2_iterator_destroy(&i); if (!s) ast_cli(fd,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term); @@ -6304,6 +6310,7 @@ static char *complete_iax2_unregister(const char *line, const char *word, int po } peer_unref(p); } + ao2_iterator_destroy(&i); } return res; @@ -6421,6 +6428,7 @@ static int manager_iax2_show_peer_list(struct mansession *s, const struct messag astman_append(s, "Status: %s\r\n\r\n", status); peer_count++; } + ao2_iterator_destroy(&i); astman_append(s, "Event: PeerlistComplete\r\n%sListItems: %d\r\n\r\n", idtext, peer_count); return RESULT_SUCCESS; @@ -7033,6 +7041,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies } user_unref(user); } + ao2_iterator_destroy(&i); user = best; if (!user && !ast_strlen_zero(iaxs[callno]->username)) { user = realtime_user(iaxs[callno]->username, sin); @@ -7562,6 +7571,7 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin, } peer_unref(peer); } + ao2_iterator_destroy(&i); if (!peer) { /* We checked our list and didn't find one. It's unlikely, but possible, that we're trying to authenticate *to* a realtime peer */ @@ -12139,6 +12149,7 @@ static void prune_users(void) } user_unref(user); } + ao2_iterator_destroy(&i); } /* Prune peers who still are supposed to be deleted */ @@ -12154,6 +12165,7 @@ static void prune_peers(void) } peer_unref(peer); } + ao2_iterator_destroy(&i); } static void set_timing(void) @@ -12607,6 +12619,7 @@ static void poke_all_peers(void) iax2_poke_peer(peer, 0); peer_unref(peer); } + ao2_iterator_destroy(&i); } static int reload_config(void) { diff --git a/funcs/func_dialgroup.c b/funcs/func_dialgroup.c index 6a9596e628..3a3ad4cb0a 100644 --- a/funcs/func_dialgroup.c +++ b/funcs/func_dialgroup.c @@ -126,6 +126,7 @@ static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, } ao2_ref(entry, -1); } + ao2_iterator_destroy(&i); return res; } diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h index 7db77a5175..22270c19a7 100644 --- a/include/asterisk/astobj2.h +++ b/include/asterisk/astobj2.h @@ -277,6 +277,8 @@ Operations on container include: ... do something on o ... ao2_ref(o, -1); } + + ao2_iterator_destroy(&i); \endcode The difference with the callback is that the control @@ -505,15 +507,15 @@ void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); /*! \brief * * - * When we need to walk through a container, we use + * When we need to walk through a container, we use an * ao2_iterator to keep track of the current position. * * Because the navigation is typically done without holding the - * lock on the container across the loop, - * objects can be inserted or deleted or moved - * while we work. As a consequence, there is no guarantee that - * the we manage to touch all the elements on the list, or it - * is possible that we touch the same object multiple times. + * lock on the container across the loop, objects can be inserted or deleted + * or moved while we work. As a consequence, there is no guarantee that + * we manage to touch all the elements in the container, and it is possible + * that we touch the same object multiple times. + * * However, within the current hash table container, the following is true: * - It is not possible to miss an object in the container while iterating * unless it gets added after the iteration begins and is added to a bucket @@ -532,6 +534,10 @@ void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); * ao2_iterator_next() has its refcount incremented, * and the reference must be explicitly released when done with it. * + * In addition, ao2_iterator_init() will hold a reference to the container + * being iterated, which will be freed when ao2_iterator_destroy() is called + * to free up the resources used by the iterator (if any). + * * Example: * * \code @@ -547,12 +553,14 @@ void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); * ao2_ref(o, -1); * } * + * ao2_iterator_destroy(&i); + * * \endcode * */ /*! \brief - * The Astobj2 iterator + * The astobj2 iterator * * \note You are not supposed to know the internals of an iterator! * We would like the iterator to be opaque, unfortunately @@ -568,21 +576,20 @@ void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags); * - a bucket number; * - the object_id, which is also the container version number * when the object was inserted. This identifies the object - * univoquely, however reaching the desired object requires + * uniquely, however reaching the desired object requires * scanning a list. * - a pointer, and a container version when we saved the pointer. * If the container has not changed its version number, then we * can safely follow the pointer to reach the object in constant time. * * Details are in the implementation of ao2_iterator_next() - * A freshly-initialized iterator has bucket=0, version = 0. + * A freshly-initialized iterator has bucket=0, version=0. */ struct ao2_iterator { /*! the container */ struct ao2_container *c; /*! operation flags */ int flags; -#define F_AO2I_DONTLOCK 1 /*!< don't lock when iterating */ /*! current bucket */ int bucket; /*! container version */ @@ -593,10 +600,49 @@ struct ao2_iterator { unsigned int version; }; +/*! Flags that can be passed to ao2_iterator_init() to modify the behavior + * of the iterator. + */ +enum ao2_iterator_flags { + /*! Prevents ao2_iterator_next() from locking the container + * while retrieving the next object from it. + */ + AO2_ITERATOR_DONTLOCK = (1 << 0), +}; + +/*! + * \brief Create an iterator for a container + * + * \param c the container + * \param flags one or more flags from ao2_iterator_flags + * + * \retval the constructed iterator + * + * \note This function does \b not take a pointer to an iterator; + * rather, it returns an iterator structure that should be + * assigned to (overwriting) an existing iterator structure + * allocated on the stack or on the heap. + * + * This function will take a reference on the container being iterated. + * + */ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags); void *ao2_iterator_next(struct ao2_iterator *a); +/*! + * \brief Destroy a container iterator + * + * \param i the iterator to destroy + * + * \retval none + * + * This function will release the container reference held by the iterator + * and any other resources it may be holding. + * + */ +void ao2_iterator_destroy(struct ao2_iterator *i); + /* extra functions */ void ao2_bt(void); /* backtrace */ #endif /* _ASTERISK_ASTOBJ2_H */ diff --git a/main/astobj2.c b/main/astobj2.c index 4603b73ac5..11846d6695 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -566,10 +566,21 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) .c = c, .flags = flags }; + + ao2_ref(c, +1); return a; } +/*! + * destroy an iterator + */ +void ao2_iterator_destroy(struct ao2_iterator *i) +{ + ao2_ref(i->c, -1); + i->c = NULL; +} + /* * move to the next element in the container. */ @@ -582,7 +593,7 @@ void * ao2_iterator_next(struct ao2_iterator *a) if (INTERNAL_OBJ(a->c) == NULL) return NULL; - if (!(a->flags & F_AO2I_DONTLOCK)) + if (!(a->flags & AO2_ITERATOR_DONTLOCK)) ao2_lock(a->c); /* optimization. If the container is unchanged and @@ -623,7 +634,7 @@ found: ao2_ref(ret, 1); } - if (!(a->flags & F_AO2I_DONTLOCK)) + if (!(a->flags & AO2_ITERATOR_DONTLOCK)) ao2_unlock(a->c); return ret; diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 1eb8d10df8..d8fc58d4a9 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -1586,7 +1586,6 @@ static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct return CLI_SHOWUSAGE; i = ao2_iterator_init(mohclasses, 0); - for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) { int x; @@ -1599,6 +1598,7 @@ static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]); } } + ao2_iterator_destroy(&i); return CLI_SUCCESS; } @@ -1623,7 +1623,6 @@ static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struc return CLI_SHOWUSAGE; i = ao2_iterator_init(mohclasses, 0); - for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) { ast_cli(a->fd, "Class: %s\n", class->name); ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "")); @@ -1635,6 +1634,7 @@ static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struc ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format)); } } + ao2_iterator_destroy(&i); return CLI_SUCCESS; } diff --git a/res/res_phoneprov.c b/res/res_phoneprov.c index 9ef5a1ac41..0cc054a458 100644 --- a/res/res_phoneprov.c +++ b/res/res_phoneprov.c @@ -891,6 +891,7 @@ static void delete_routes(void) ao2_unlink(http_routes, route); route = unref_route(route); } + ao2_iterator_destroy(&i); } /*! \brief Delete all phone profiles, freeing their memory */ @@ -904,6 +905,7 @@ static void delete_profiles(void) ao2_unlink(profiles, profile); profile = unref_profile(profile); } + ao2_iterator_destroy(&i); } /*! \brief A dialplan function that can be used to print a string for each phoneprov user */ @@ -974,6 +976,7 @@ static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli ast_cli(a->fd, FORMAT, route->uri, route->file->template); route = unref_route(route); } + ao2_iterator_destroy(&i); ast_cli(a->fd, "\nDynamic routes\n\n"); ast_cli(a->fd, FORMAT, "Relative URI", "Template"); @@ -984,6 +987,7 @@ static char *handle_show_routes(struct ast_cli_entry *e, int cmd, struct ast_cli ast_cli(a->fd, FORMAT, route->uri, route->file->template); route = unref_route(route); } + ao2_iterator_destroy(&i); return CLI_SUCCESS; }