astobj2_container.c

Go to the documentation of this file.
00001 /* astobj2 - replacement containers for asterisk data structures.
00002  *
00003  * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
00004  *
00005  * See http://www.asterisk.org for more information about
00006  * the Asterisk project. Please do not directly contact
00007  * any of the maintainers of this project for assistance;
00008  * the project provides a web site, mailing lists and IRC
00009  * channels for your use.
00010  *
00011  * This program is free software, distributed under the terms of
00012  * the GNU General Public License Version 2. See the LICENSE file
00013  * at the top of the source tree.
00014  */
00015 
00016 /*! \file
00017  *
00018  * \brief Functions implementing astobj2 objects.
00019  *
00020  * \author Richard Mudgett <rmudgett@digium.com>
00021  */
00022 
00023 #include "asterisk.h"
00024 
00025 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 433498 $")
00026 
00027 #include "asterisk/_private.h"
00028 #include "asterisk/astobj2.h"
00029 #include "astobj2_private.h"
00030 #include "astobj2_container_private.h"
00031 #include "asterisk/cli.h"
00032 
00033 /*!
00034  * return the number of elements in the container
00035  */
00036 int ao2_container_count(struct ao2_container *c)
00037 {
00038    return ast_atomic_fetchadd_int(&c->elements, 0);
00039 }
00040 
00041 int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flags,
00042    const char *tag, const char *file, int line, const char *func)
00043 {
00044    struct ao2_container *container = node->my_container;
00045 
00046    if (container == NULL && (flags & AO2_UNLINK_NODE_DEC_COUNT)) {
00047       return 0;
00048    }
00049 
00050    if ((flags & AO2_UNLINK_NODE_UNLINK_OBJECT)
00051       && !(flags & AO2_UNLINK_NODE_NOUNREF_OBJECT)) {
00052       if (tag) {
00053          __ao2_ref_debug(node->obj, -1, tag, file, line, func);
00054       } else {
00055          ao2_t_ref(node->obj, -1, "Remove obj from container");
00056       }
00057    }
00058 
00059    node->obj = NULL;
00060 
00061    if (flags & AO2_UNLINK_NODE_DEC_COUNT) {
00062       ast_atomic_fetchadd_int(&container->elements, -1);
00063 #if defined(AO2_DEBUG)
00064       {
00065          int empty = container->nodes - container->elements;
00066 
00067          if (container->max_empty_nodes < empty) {
00068             container->max_empty_nodes = empty;
00069          }
00070          if (container->v_table->unlink_stat) {
00071             container->v_table->unlink_stat(container, node);
00072          }
00073       }
00074 #endif   /* defined(AO2_DEBUG) */
00075    }
00076 
00077    if (flags & AO2_UNLINK_NODE_UNREF_NODE) {
00078       /* Remove node from container */
00079       __ao2_ref(node, -1);
00080    }
00081 
00082    return 1;
00083 }
00084 
00085 /*!
00086  * \internal
00087  * \brief Link an object into this container.  (internal)
00088  *
00089  * \param self Container to operate upon.
00090  * \param obj_new Object to insert into the container.
00091  * \param flags search_flags to control linking the object.  (OBJ_NOLOCK)
00092  * \param tag used for debugging.
00093  * \param file Debug file name invoked from
00094  * \param line Debug line invoked from
00095  * \param func Debug function name invoked from
00096  *
00097  * \retval 0 on errors.
00098  * \retval 1 on success.
00099  */
00100 static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
00101 {
00102    int res;
00103    enum ao2_lock_req orig_lock;
00104    struct ao2_container_node *node;
00105 
00106    if (!is_ao2_object(obj_new) || !is_ao2_object(self)
00107       || !self->v_table || !self->v_table->new_node || !self->v_table->insert) {
00108       /* Sanity checks. */
00109       ast_assert(0);
00110       return 0;
00111    }
00112 
00113    if (flags & OBJ_NOLOCK) {
00114       orig_lock = __adjust_lock(self, AO2_LOCK_REQ_WRLOCK, 1);
00115    } else {
00116       ao2_wrlock(self);
00117       orig_lock = AO2_LOCK_REQ_MUTEX;
00118    }
00119 
00120    res = 0;
00121    node = self->v_table->new_node(self, obj_new, tag, file, line, func);
00122    if (node) {
00123 #if defined(AO2_DEBUG)
00124       if (ao2_container_check(self, OBJ_NOLOCK)) {
00125          ast_log(LOG_ERROR, "Container integrity failed before insert.\n");
00126       }
00127 #endif   /* defined(AO2_DEBUG) */
00128 
00129       /* Insert the new node. */
00130       switch (self->v_table->insert(self, node)) {
00131       case AO2_CONTAINER_INSERT_NODE_INSERTED:
00132          node->is_linked = 1;
00133          ast_atomic_fetchadd_int(&self->elements, 1);
00134 #if defined(AO2_DEBUG)
00135          AO2_DEVMODE_STAT(++self->nodes);
00136          if (self->v_table->link_stat) {
00137             self->v_table->link_stat(self, node);
00138          }
00139 #endif   /* defined(AO2_DEBUG) */
00140          /* Fall through */
00141       case AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED:
00142 #if defined(AO2_DEBUG)
00143          if (ao2_container_check(self, OBJ_NOLOCK)) {
00144             ast_log(LOG_ERROR, "Container integrity failed after insert or replace.\n");
00145          }
00146 #endif   /* defined(AO2_DEBUG) */
00147          res = 1;
00148          break;
00149       case AO2_CONTAINER_INSERT_NODE_REJECTED:
00150          __ao2_ref(node, -1);
00151          break;
00152       }
00153    }
00154 
00155    if (flags & OBJ_NOLOCK) {
00156       __adjust_lock(self, orig_lock, 0);
00157    } else {
00158       ao2_unlock(self);
00159    }
00160 
00161    return res;
00162 }
00163 
00164 int __ao2_link_debug(struct ao2_container *c, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
00165 {
00166    return internal_ao2_link(c, obj_new, flags, tag, file, line, func);
00167 }
00168 
00169 int __ao2_link(struct ao2_container *c, void *obj_new, int flags)
00170 {
00171    return internal_ao2_link(c, obj_new, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
00172 }
00173 
00174 /*!
00175  * \brief another convenience function is a callback that matches on address
00176  */
00177 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00178 {
00179    return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00180 }
00181 
00182 /*
00183  * Unlink an object from the container
00184  * and destroy the associated * bucket_entry structure.
00185  */
00186 void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
00187    const char *tag, const char *file, int line, const char *func)
00188 {
00189    if (!is_ao2_object(user_data)) {
00190       /* Sanity checks. */
00191       ast_assert(0);
00192       return NULL;
00193    }
00194 
00195    flags &= ~OBJ_SEARCH_MASK;
00196    flags |= (OBJ_UNLINK | OBJ_SEARCH_OBJECT | OBJ_NODATA);
00197    __ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
00198 
00199    return NULL;
00200 }
00201 
00202 void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
00203 {
00204    if (!is_ao2_object(user_data)) {
00205       /* Sanity checks. */
00206       ast_assert(0);
00207       return NULL;
00208    }
00209 
00210    flags &= ~OBJ_SEARCH_MASK;
00211    flags |= (OBJ_UNLINK | OBJ_SEARCH_OBJECT | OBJ_NODATA);
00212    __ao2_callback(c, flags, ao2_match_by_addr, user_data);
00213 
00214    return NULL;
00215 }
00216 
00217 /*!
00218  * \brief special callback that matches all
00219  */
00220 static int cb_true(void *user_data, void *arg, int flags)
00221 {
00222    return CMP_MATCH;
00223 }
00224 
00225 /*!
00226  * \brief similar to cb_true, but is an ao2_callback_data_fn instead
00227  */
00228 static int cb_true_data(void *user_data, void *arg, void *data, int flags)
00229 {
00230    return CMP_MATCH;
00231 }
00232 
00233 /*!
00234  * \internal
00235  * \brief Traverse the container.  (internal)
00236  *
00237  * \param self Container to operate upon.
00238  * \param flags search_flags to control traversing the container
00239  * \param cb_fn Comparison callback function.
00240  * \param arg Comparison callback arg parameter.
00241  * \param data Data comparison callback data parameter.
00242  * \param type Type of comparison callback cb_fn.
00243  * \param tag used for debugging.
00244  * \param file Debug file name invoked from
00245  * \param line Debug line invoked from
00246  * \param func Debug function name invoked from
00247  *
00248  * \retval NULL on failure or no matching object found.
00249  *
00250  * \retval object found if OBJ_MULTIPLE is not set in the flags
00251  * parameter.
00252  *
00253  * \retval ao2_iterator pointer if OBJ_MULTIPLE is set in the
00254  * flags parameter.  The iterator must be destroyed with
00255  * ao2_iterator_destroy() when the caller no longer needs it.
00256  */
00257 static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags flags,
00258    void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
00259    const char *tag, const char *file, int line, const char *func)
00260 {
00261    void *ret;
00262    ao2_callback_fn *cb_default = NULL;
00263    ao2_callback_data_fn *cb_withdata = NULL;
00264    struct ao2_container_node *node;
00265    void *traversal_state;
00266 
00267    enum ao2_lock_req orig_lock;
00268    struct ao2_container *multi_container = NULL;
00269    struct ao2_iterator *multi_iterator = NULL;
00270 
00271    if (!is_ao2_object(self) || !self->v_table || !self->v_table->traverse_first
00272       || !self->v_table->traverse_next) {
00273       /* Sanity checks. */
00274       ast_assert(0);
00275       return NULL;
00276    }
00277 
00278    /*
00279     * This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
00280     * turned off.  This if statement checks for the special condition
00281     * where multiple items may need to be returned.
00282     */
00283    if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
00284       /* we need to return an ao2_iterator with the results,
00285        * as there could be more than one. the iterator will
00286        * hold the only reference to a container that has all the
00287        * matching objects linked into it, so when the iterator
00288        * is destroyed, the container will be automatically
00289        * destroyed as well.
00290        */
00291       multi_container = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL,
00292          NULL, "OBJ_MULTIPLE return container creation");
00293       if (!multi_container) {
00294          return NULL;
00295       }
00296       if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
00297          ao2_t_ref(multi_container, -1, "OBJ_MULTIPLE interator creation failed.");
00298          return NULL;
00299       }
00300    }
00301 
00302    if (!cb_fn) {
00303       /* Match everything if no callback match function provided. */
00304       if (type == AO2_CALLBACK_WITH_DATA) {
00305          cb_withdata = cb_true_data;
00306       } else {
00307          cb_default = cb_true;
00308       }
00309    } else {
00310       /*
00311        * We do this here to avoid the per object casting penalty (even
00312        * though that is probably optimized away anyway).
00313        */
00314       if (type == AO2_CALLBACK_WITH_DATA) {
00315          cb_withdata = cb_fn;
00316       } else {
00317          cb_default = cb_fn;
00318       }
00319    }
00320 
00321    /* avoid modifications to the content */
00322    if (flags & OBJ_NOLOCK) {
00323       if (flags & OBJ_UNLINK) {
00324          orig_lock = __adjust_lock(self, AO2_LOCK_REQ_WRLOCK, 1);
00325       } else {
00326          orig_lock = __adjust_lock(self, AO2_LOCK_REQ_RDLOCK, 1);
00327       }
00328    } else {
00329       orig_lock = AO2_LOCK_REQ_MUTEX;
00330       if (flags & OBJ_UNLINK) {
00331          ao2_wrlock(self);
00332       } else {
00333          ao2_rdlock(self);
00334       }
00335    }
00336 
00337    /* Create a buffer for the traversal state. */
00338    traversal_state = alloca(AO2_TRAVERSAL_STATE_SIZE);
00339 
00340    ret = NULL;
00341    for (node = self->v_table->traverse_first(self, flags, arg, traversal_state);
00342       node;
00343       node = self->v_table->traverse_next(self, traversal_state, node)) {
00344       int match;
00345 
00346       /* Visit the current node. */
00347       match = (CMP_MATCH | CMP_STOP);
00348       if (type == AO2_CALLBACK_WITH_DATA) {
00349          match &= cb_withdata(node->obj, arg, data, flags);
00350       } else {
00351          match &= cb_default(node->obj, arg, flags);
00352       }
00353       if (match == 0) {
00354          /* no match, no stop, continue */
00355          continue;
00356       }
00357       if (match == CMP_STOP) {
00358          /* no match but stop, we are done */
00359          break;
00360       }
00361 
00362       /*
00363        * CMP_MATCH is set here
00364        *
00365        * we found the object, performing operations according to flags
00366        */
00367       if (node->obj) {
00368          /* The object is still in the container. */
00369          if (!(flags & OBJ_NODATA)) {
00370             /*
00371              * We are returning the object, record the value.  It is
00372              * important to handle this case before the unlink.
00373              */
00374             if (multi_container) {
00375                /*
00376                 * Link the object into the container that will hold the
00377                 * results.
00378                 */
00379                if (tag) {
00380                   __ao2_link_debug(multi_container, node->obj, flags,
00381                      tag, file, line, func);
00382                } else {
00383                   __ao2_link(multi_container, node->obj, flags);
00384                }
00385             } else {
00386                ret = node->obj;
00387                /* Returning a single object. */
00388                if (!(flags & OBJ_UNLINK)) {
00389                   /*
00390                    * Bump the ref count since we are not going to unlink and
00391                    * transfer the container's object ref to the returned object.
00392                    */
00393                   if (tag) {
00394                      __ao2_ref_debug(ret, 1, tag, file, line, func);
00395                   } else {
00396                      ao2_t_ref(ret, 1, "Traversal found object");
00397                   }
00398                }
00399             }
00400          }
00401 
00402          if (flags & OBJ_UNLINK) {
00403             int ulflag = AO2_UNLINK_NODE_UNREF_NODE | AO2_UNLINK_NODE_DEC_COUNT;
00404             if (multi_container || (flags & OBJ_NODATA)) {
00405                ulflag |= AO2_UNLINK_NODE_UNLINK_OBJECT;
00406             }
00407             __container_unlink_node_debug(node, ulflag, tag, file, line, func);
00408          }
00409       }
00410 
00411       if ((match & CMP_STOP) || !(flags & OBJ_MULTIPLE)) {
00412          /* We found our only (or last) match, so we are done */
00413          break;
00414       }
00415    }
00416    if (self->v_table->traverse_cleanup) {
00417       self->v_table->traverse_cleanup(traversal_state);
00418    }
00419    if (node) {
00420       /* Unref the node from self->v_table->traverse_first/traverse_next() */
00421       __ao2_ref(node, -1);
00422    }
00423 
00424    if (flags & OBJ_NOLOCK) {
00425       __adjust_lock(self, orig_lock, 0);
00426    } else {
00427       ao2_unlock(self);
00428    }
00429 
00430    /* if multi_container was created, we are returning multiple objects */
00431    if (multi_container) {
00432       *multi_iterator = ao2_iterator_init(multi_container,
00433          AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
00434       ao2_t_ref(multi_container, -1,
00435          "OBJ_MULTIPLE for multiple objects traversal complete.");
00436       return multi_iterator;
00437    } else {
00438       return ret;
00439    }
00440 }
00441 
00442 void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
00443    ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
00444    const char *func)
00445 {
00446    return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, tag, file, line, func);
00447 }
00448 
00449 void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
00450    ao2_callback_fn *cb_fn, void *arg)
00451 {
00452    return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, NULL, NULL, 0, NULL);
00453 }
00454 
00455 void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
00456    ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
00457    int line, const char *func)
00458 {
00459    return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, tag, file, line, func);
00460 }
00461 
00462 void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
00463    ao2_callback_data_fn *cb_fn, void *arg, void *data)
00464 {
00465    return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, NULL, NULL, 0, NULL);
00466 }
00467 
00468 /*!
00469  * the find function just invokes the default callback with some reasonable flags.
00470  */
00471 void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
00472    const char *tag, const char *file, int line, const char *func)
00473 {
00474    void *arged = (void *) arg;/* Done to avoid compiler const warning */
00475 
00476    if (!c) {
00477       /* Sanity checks. */
00478       ast_assert(0);
00479       return NULL;
00480    }
00481    return __ao2_callback_debug(c, flags, c->cmp_fn, arged, tag, file, line, func);
00482 }
00483 
00484 void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags)
00485 {
00486    void *arged = (void *) arg;/* Done to avoid compiler const warning */
00487 
00488    if (!c) {
00489       /* Sanity checks. */
00490       ast_assert(0);
00491       return NULL;
00492    }
00493    return __ao2_callback(c, flags, c->cmp_fn, arged);
00494 }
00495 
00496 /*!
00497  * initialize an iterator so we start from the first object
00498  */
00499 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
00500 {
00501    struct ao2_iterator a = {
00502       .c = c,
00503       .flags = flags
00504    };
00505 
00506    ao2_t_ref(c, +1, "Init iterator with container.");
00507 
00508    return a;
00509 }
00510 
00511 void ao2_iterator_restart(struct ao2_iterator *iter)
00512 {
00513    /* Release the last container node reference if we have one. */
00514    if (iter->last_node) {
00515       enum ao2_lock_req orig_lock;
00516 
00517       /*
00518        * Do a read lock in case the container node unref does not
00519        * destroy the node.  If the container node is destroyed then
00520        * the lock will be upgraded to a write lock.
00521        */
00522       if (iter->flags & AO2_ITERATOR_DONTLOCK) {
00523          orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
00524       } else {
00525          orig_lock = AO2_LOCK_REQ_MUTEX;
00526          ao2_rdlock(iter->c);
00527       }
00528 
00529       __ao2_ref(iter->last_node, -1);
00530       iter->last_node = NULL;
00531 
00532       if (iter->flags & AO2_ITERATOR_DONTLOCK) {
00533          __adjust_lock(iter->c, orig_lock, 0);
00534       } else {
00535          ao2_unlock(iter->c);
00536       }
00537    }
00538 
00539    /* The iteration is no longer complete. */
00540    iter->complete = 0;
00541 }
00542 
00543 void ao2_iterator_destroy(struct ao2_iterator *iter)
00544 {
00545    /* Release any last container node reference. */
00546    ao2_iterator_restart(iter);
00547 
00548    /* Release the iterated container reference. */
00549    ao2_t_ref(iter->c, -1, "Unref iterator in ao2_iterator_destroy");
00550    iter->c = NULL;
00551 
00552    /* Free the malloced iterator. */
00553    if (iter->flags & AO2_ITERATOR_MALLOCD) {
00554       ast_free(iter);
00555    }
00556 }
00557 
00558 void ao2_iterator_cleanup(struct ao2_iterator *iter)
00559 {
00560    if (iter) {
00561       ao2_iterator_destroy(iter);
00562    }
00563 }
00564 
00565 /*
00566  * move to the next element in the container.
00567  */
00568 static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
00569 {
00570    enum ao2_lock_req orig_lock;
00571    struct ao2_container_node *node;
00572    void *ret;
00573 
00574    if (!is_ao2_object(iter->c) || !iter->c->v_table || !iter->c->v_table->iterator_next) {
00575       /* Sanity checks. */
00576       ast_assert(0);
00577       return NULL;
00578    }
00579 
00580    if (iter->complete) {
00581       /* Don't return any more objects. */
00582       return NULL;
00583    }
00584 
00585    if (iter->flags & AO2_ITERATOR_DONTLOCK) {
00586       if (iter->flags & AO2_ITERATOR_UNLINK) {
00587          orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_WRLOCK, 1);
00588       } else {
00589          orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
00590       }
00591    } else {
00592       orig_lock = AO2_LOCK_REQ_MUTEX;
00593       if (iter->flags & AO2_ITERATOR_UNLINK) {
00594          ao2_wrlock(iter->c);
00595       } else {
00596          ao2_rdlock(iter->c);
00597       }
00598    }
00599 
00600    node = iter->c->v_table->iterator_next(iter->c, iter->last_node, iter->flags);
00601    if (node) {
00602       ret = node->obj;
00603 
00604       if (iter->flags & AO2_ITERATOR_UNLINK) {
00605          /* Transfer the object ref from the container to the returned object. */
00606          __container_unlink_node_debug(node, AO2_UNLINK_NODE_DEC_COUNT, tag, file, line, func);
00607 
00608          /* Transfer the container's node ref to the iterator. */
00609       } else {
00610          /* Bump ref of returned object */
00611          if (tag) {
00612             __ao2_ref_debug(ret, +1, tag, file, line, func);
00613          } else {
00614             ao2_t_ref(ret, +1, "Next iterator object.");
00615          }
00616 
00617          /* Bump the container's node ref for the iterator. */
00618          __ao2_ref(node, +1);
00619       }
00620    } else {
00621       /* The iteration has completed. */
00622       iter->complete = 1;
00623       ret = NULL;
00624    }
00625 
00626    /* Replace the iterator's node */
00627    if (iter->last_node) {
00628       __ao2_ref(iter->last_node, -1);
00629    }
00630    iter->last_node = node;
00631 
00632    if (iter->flags & AO2_ITERATOR_DONTLOCK) {
00633       __adjust_lock(iter->c, orig_lock, 0);
00634    } else {
00635       ao2_unlock(iter->c);
00636    }
00637 
00638    return ret;
00639 }
00640 
00641 void *__ao2_iterator_next_debug(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
00642 {
00643    return internal_ao2_iterator_next(iter, tag, file, line, func);
00644 }
00645 
00646 void *__ao2_iterator_next(struct ao2_iterator *iter)
00647 {
00648    return internal_ao2_iterator_next(iter, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
00649 }
00650 
00651 int ao2_iterator_count(struct ao2_iterator *iter)
00652 {
00653    return ao2_container_count(iter->c);
00654 }
00655 
00656 void container_destruct(void *_c)
00657 {
00658    struct ao2_container *c = _c;
00659 
00660    /* Unlink any stored objects in the container. */
00661    c->destroying = 1;
00662    __ao2_callback(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
00663 
00664    /* Perform any extra container cleanup. */
00665    if (c->v_table && c->v_table->destroy) {
00666       c->v_table->destroy(c);
00667    }
00668 
00669 #if defined(AO2_DEBUG)
00670    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00671 #endif
00672 }
00673 
00674 void container_destruct_debug(void *_c)
00675 {
00676    struct ao2_container *c = _c;
00677 
00678    /* Unlink any stored objects in the container. */
00679    c->destroying = 1;
00680    __ao2_callback_debug(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL,
00681       "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
00682 
00683    /* Perform any extra container cleanup. */
00684    if (c->v_table && c->v_table->destroy) {
00685       c->v_table->destroy(c);
00686    }
00687 
00688 #if defined(AO2_DEBUG)
00689    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00690 #endif
00691 }
00692 
00693 /*!
00694  * \internal
00695  * \brief Put obj into the arg container.
00696  * \since 11.0
00697  *
00698  * \param obj  pointer to the (user-defined part) of an object.
00699  * \param arg callback argument from ao2_callback()
00700  * \param flags flags from ao2_callback()
00701  *
00702  * \retval 0 on success.
00703  * \retval CMP_STOP|CMP_MATCH on error.
00704  */
00705 static int dup_obj_cb(void *obj, void *arg, int flags)
00706 {
00707    struct ao2_container *dest = arg;
00708 
00709    return __ao2_link(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
00710 }
00711 
00712 int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
00713 {
00714    void *obj;
00715    int res = 0;
00716 
00717    if (!(flags & OBJ_NOLOCK)) {
00718       ao2_rdlock(src);
00719       ao2_wrlock(dest);
00720    }
00721    obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
00722    if (obj) {
00723       /* Failed to put this obj into the dest container. */
00724       ao2_t_ref(obj, -1, "Failed to put this object into the dest container.");
00725 
00726       /* Remove all items from the dest container. */
00727       __ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
00728          NULL);
00729       res = -1;
00730    }
00731    if (!(flags & OBJ_NOLOCK)) {
00732       ao2_unlock(dest);
00733       ao2_unlock(src);
00734    }
00735 
00736    return res;
00737 }
00738 
00739 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
00740 {
00741    struct ao2_container *clone;
00742    int failed;
00743 
00744    /* Create the clone container with the same properties as the original. */
00745    if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone) {
00746       /* Sanity checks. */
00747       ast_assert(0);
00748       return NULL;
00749    }
00750    clone = orig->v_table->alloc_empty_clone(orig);
00751    if (!clone) {
00752       return NULL;
00753    }
00754 
00755    if (flags & OBJ_NOLOCK) {
00756       ao2_wrlock(clone);
00757    }
00758    failed = ao2_container_dup(clone, orig, flags);
00759    if (flags & OBJ_NOLOCK) {
00760       ao2_unlock(clone);
00761    }
00762    if (failed) {
00763       /* Object copy into the clone container failed. */
00764       ao2_t_ref(clone, -1, "Clone creation failed.");
00765       clone = NULL;
00766    }
00767    return clone;
00768 }
00769 
00770 struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func, int ref_debug)
00771 {
00772    struct ao2_container *clone;
00773    int failed;
00774 
00775    /* Create the clone container with the same properties as the original. */
00776    if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone_debug) {
00777       /* Sanity checks. */
00778       ast_assert(0);
00779       return NULL;
00780    }
00781    clone = orig->v_table->alloc_empty_clone_debug(orig, tag, file, line, func, ref_debug);
00782    if (!clone) {
00783       return NULL;
00784    }
00785 
00786    if (flags & OBJ_NOLOCK) {
00787       ao2_wrlock(clone);
00788    }
00789    failed = ao2_container_dup(clone, orig, flags);
00790    if (flags & OBJ_NOLOCK) {
00791       ao2_unlock(clone);
00792    }
00793    if (failed) {
00794       /* Object copy into the clone container failed. */
00795       if (ref_debug) {
00796          __ao2_ref_debug(clone, -1, tag, file, line, func);
00797       } else {
00798          ao2_t_ref(clone, -1, "Clone creation failed.");
00799       }
00800       clone = NULL;
00801    }
00802    return clone;
00803 }
00804 
00805 void ao2_container_dump(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt, ao2_prnt_obj_fn *prnt_obj)
00806 {
00807    if (!is_ao2_object(self) || !self->v_table) {
00808       prnt(where, "Invalid container\n");
00809       ast_assert(0);
00810       return;
00811    }
00812 
00813    if (!(flags & OBJ_NOLOCK)) {
00814       ao2_rdlock(self);
00815    }
00816    if (name) {
00817       prnt(where, "Container name: %s\n", name);
00818    }
00819 #if defined(AO2_DEBUG)
00820    if (self->v_table->dump) {
00821       self->v_table->dump(self, where, prnt, prnt_obj);
00822    } else
00823 #endif   /* defined(AO2_DEBUG) */
00824    {
00825       prnt(where, "Container dump not available.\n");
00826    }
00827    if (!(flags & OBJ_NOLOCK)) {
00828       ao2_unlock(self);
00829    }
00830 }
00831 
00832 void ao2_container_stats(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt)
00833 {
00834    if (!is_ao2_object(self) || !self->v_table) {
00835       prnt(where, "Invalid container\n");
00836       ast_assert(0);
00837       return;
00838    }
00839 
00840    if (!(flags & OBJ_NOLOCK)) {
00841       ao2_rdlock(self);
00842    }
00843    if (name) {
00844       prnt(where, "Container name: %s\n", name);
00845    }
00846    prnt(where, "Number of objects: %d\n", self->elements);
00847 #if defined(AO2_DEBUG)
00848    prnt(where, "Number of nodes: %d\n", self->nodes);
00849    prnt(where, "Number of empty nodes: %d\n", self->nodes - self->elements);
00850    /*
00851     * XXX
00852     * If the max_empty_nodes count gets out of single digits you
00853     * likely have a code path where ao2_iterator_destroy() is not
00854     * called.
00855     *
00856     * Empty nodes do not harm the container but they do make
00857     * container operations less efficient.
00858     */
00859    prnt(where, "Maximum empty nodes: %d\n", self->max_empty_nodes);
00860    if (self->v_table->stats) {
00861       self->v_table->stats(self, where, prnt);
00862    }
00863 #endif   /* defined(AO2_DEBUG) */
00864    if (!(flags & OBJ_NOLOCK)) {
00865       ao2_unlock(self);
00866    }
00867 }
00868 
00869 int ao2_container_check(struct ao2_container *self, enum search_flags flags)
00870 {
00871    int res = 0;
00872 
00873    if (!is_ao2_object(self) || !self->v_table) {
00874       /* Sanity checks. */
00875       ast_assert(0);
00876       return -1;
00877    }
00878 #if defined(AO2_DEBUG)
00879    if (!self->v_table->integrity) {
00880       /* No ingetrigy check available.  Assume container is ok. */
00881       return 0;
00882    }
00883 
00884    if (!(flags & OBJ_NOLOCK)) {
00885       ao2_rdlock(self);
00886    }
00887    res = self->v_table->integrity(self);
00888    if (!(flags & OBJ_NOLOCK)) {
00889       ao2_unlock(self);
00890    }
00891 #endif   /* defined(AO2_DEBUG) */
00892    return res;
00893 }
00894 
00895 #if defined(AO2_DEBUG)
00896 static struct ao2_container *reg_containers;
00897 
00898 struct ao2_reg_container {
00899    /*! Registered container pointer. */
00900    struct ao2_container *registered;
00901    /*! Callback function to print the given object's key. (NULL if not available) */
00902    ao2_prnt_obj_fn *prnt_obj;
00903    /*! Name container registered under. */
00904    char name[1];
00905 };
00906 
00907 struct ao2_reg_partial_key {
00908    /*! Length of partial key match. */
00909    int len;
00910    /*! Registration partial key name. */
00911    const char *name;
00912 };
00913 
00914 struct ao2_reg_match {
00915    /*! The nth match to find. */
00916    int find_nth;
00917    /*! Count of the matches already found. */
00918    int count;
00919 };
00920 #endif   /* defined(AO2_DEBUG) */
00921 
00922 #if defined(AO2_DEBUG)
00923 static int ao2_reg_sort_cb(const void *obj_left, const void *obj_right, int flags)
00924 {
00925    const struct ao2_reg_container *reg_left = obj_left;
00926    int cmp;
00927 
00928    switch (flags & OBJ_SEARCH_MASK) {
00929    case OBJ_SEARCH_OBJECT:
00930       {
00931          const struct ao2_reg_container *reg_right = obj_right;
00932 
00933          cmp = strcasecmp(reg_left->name, reg_right->name);
00934       }
00935       break;
00936    case OBJ_SEARCH_KEY:
00937       {
00938          const char *name = obj_right;
00939 
00940          cmp = strcasecmp(reg_left->name, name);
00941       }
00942       break;
00943    case OBJ_SEARCH_PARTIAL_KEY:
00944       {
00945          const struct ao2_reg_partial_key *partial_key = obj_right;
00946 
00947          cmp = strncasecmp(reg_left->name, partial_key->name, partial_key->len);
00948       }
00949       break;
00950    default:
00951       /* Sort can only work on something with a full or partial key. */
00952       ast_assert(0);
00953       cmp = 0;
00954       break;
00955    }
00956    return cmp;
00957 }
00958 #endif   /* defined(AO2_DEBUG) */
00959 
00960 #if defined(AO2_DEBUG)
00961 static void ao2_reg_destructor(void *v_doomed)
00962 {
00963    struct ao2_reg_container *doomed = v_doomed;
00964 
00965    if (doomed->registered) {
00966       ao2_t_ref(doomed->registered, -1, "Releasing registered container.");
00967    }
00968 }
00969 #endif   /* defined(AO2_DEBUG) */
00970 
00971 int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
00972 {
00973    int res = 0;
00974 #if defined(AO2_DEBUG)
00975    struct ao2_reg_container *reg;
00976 
00977    reg = ao2_t_alloc_options(sizeof(*reg) + strlen(name), ao2_reg_destructor,
00978       AO2_ALLOC_OPT_LOCK_NOLOCK, "Container registration object.");
00979    if (!reg) {
00980       return -1;
00981    }
00982 
00983    /* Fill in registered entry */
00984    ao2_t_ref(self, +1, "Registering container.");
00985    reg->registered = self;
00986    reg->prnt_obj = prnt_obj;
00987    strcpy(reg->name, name);/* safe */
00988 
00989    if (!ao2_t_link(reg_containers, reg, "Save registration object.")) {
00990       res = -1;
00991    }
00992 
00993    ao2_t_ref(reg, -1, "Done registering container.");
00994 #endif   /* defined(AO2_DEBUG) */
00995    return res;
00996 }
00997 
00998 void ao2_container_unregister(const char *name)
00999 {
01000 #if defined(AO2_DEBUG)
01001    ao2_t_find(reg_containers, name, OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY,
01002       "Unregister container");
01003 #endif   /* defined(AO2_DEBUG) */
01004 }
01005 
01006 #if defined(AO2_DEBUG)
01007 static int ao2_complete_reg_cb(void *obj, void *arg, void *data, int flags)
01008 {
01009    struct ao2_reg_match *which = data;
01010 
01011    /* ao2_reg_sort_cb() has already filtered the search to matching keys */
01012    return (which->find_nth < ++which->count) ? (CMP_MATCH | CMP_STOP) : 0;
01013 }
01014 #endif   /* defined(AO2_DEBUG) */
01015 
01016 #if defined(AO2_DEBUG)
01017 static char *complete_container_names(struct ast_cli_args *a)
01018 {
01019    struct ao2_reg_partial_key partial_key;
01020    struct ao2_reg_match which;
01021    struct ao2_reg_container *reg;
01022    char *name;
01023 
01024    if (a->pos != 3) {
01025       return NULL;
01026    }
01027 
01028    partial_key.len = strlen(a->word);
01029    partial_key.name = a->word;
01030    which.find_nth = a->n;
01031    which.count = 0;
01032    reg = ao2_t_callback_data(reg_containers, partial_key.len ? OBJ_SEARCH_PARTIAL_KEY : 0,
01033       ao2_complete_reg_cb, &partial_key, &which, "Find partial registered container");
01034    if (reg) {
01035       name = ast_strdup(reg->name);
01036       ao2_t_ref(reg, -1, "Done with registered container object.");
01037    } else {
01038       name = NULL;
01039    }
01040    return name;
01041 }
01042 #endif   /* defined(AO2_DEBUG) */
01043 
01044 #if defined(AO2_DEBUG)
01045 AST_THREADSTORAGE(ao2_out_buf);
01046 
01047 /*!
01048  * \brief Print CLI output.
01049  * \since 12.0.0
01050  *
01051  * \param where User data pointer needed to determine where to put output.
01052  * \param fmt printf type format string.
01053  *
01054  * \return Nothing
01055  */
01056 static void cli_output(void *where, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
01057 static void cli_output(void *where, const char *fmt, ...)
01058 {
01059    int res;
01060    struct ast_str *buf;
01061    va_list ap;
01062 
01063    buf = ast_str_thread_get(&ao2_out_buf, 256);
01064    if (!buf) {
01065       return;
01066    }
01067 
01068    va_start(ap, fmt);
01069    res = ast_str_set_va(&buf, 0, fmt, ap);
01070    va_end(ap);
01071 
01072    if (res != AST_DYNSTR_BUILD_FAILED) {
01073       ast_cli(*(int *) where, "%s", ast_str_buffer(buf));
01074    }
01075 }
01076 #endif   /* defined(AO2_DEBUG) */
01077 
01078 #if defined(AO2_DEBUG)
01079 /*! \brief Show container contents - CLI command */
01080 static char *handle_cli_astobj2_container_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01081 {
01082    const char *name;
01083    struct ao2_reg_container *reg;
01084 
01085    switch (cmd) {
01086    case CLI_INIT:
01087       e->command = "astobj2 container dump";
01088       e->usage =
01089          "Usage: astobj2 container dump <name>\n"
01090          "  Show contents of the container <name>.\n";
01091       return NULL;
01092    case CLI_GENERATE:
01093       return complete_container_names(a);
01094    }
01095 
01096    if (a->argc != 4) {
01097       return CLI_SHOWUSAGE;
01098    }
01099 
01100    name = a->argv[3];
01101    reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
01102    if (reg) {
01103       ao2_container_dump(reg->registered, 0, name, (void *) &a->fd, cli_output,
01104          reg->prnt_obj);
01105       ao2_t_ref(reg, -1, "Done with registered container object.");
01106    } else {
01107       ast_cli(a->fd, "Container '%s' not found.\n", name);
01108    }
01109 
01110    return CLI_SUCCESS;
01111 }
01112 #endif   /* defined(AO2_DEBUG) */
01113 
01114 #if defined(AO2_DEBUG)
01115 /*! \brief Show container statistics - CLI command */
01116 static char *handle_cli_astobj2_container_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01117 {
01118    const char *name;
01119    struct ao2_reg_container *reg;
01120 
01121    switch (cmd) {
01122    case CLI_INIT:
01123       e->command = "astobj2 container stats";
01124       e->usage =
01125          "Usage: astobj2 container stats <name>\n"
01126          "  Show statistics about the specified container <name>.\n";
01127       return NULL;
01128    case CLI_GENERATE:
01129       return complete_container_names(a);
01130    }
01131 
01132    if (a->argc != 4) {
01133       return CLI_SHOWUSAGE;
01134    }
01135 
01136    name = a->argv[3];
01137    reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
01138    if (reg) {
01139       ao2_container_stats(reg->registered, 0, name, (void *) &a->fd, cli_output);
01140       ao2_t_ref(reg, -1, "Done with registered container object.");
01141    } else {
01142       ast_cli(a->fd, "Container '%s' not found.\n", name);
01143    }
01144 
01145    return CLI_SUCCESS;
01146 }
01147 #endif   /* defined(AO2_DEBUG) */
01148 
01149 #if defined(AO2_DEBUG)
01150 /*! \brief Show container check results - CLI command */
01151 static char *handle_cli_astobj2_container_check(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01152 {
01153    const char *name;
01154    struct ao2_reg_container *reg;
01155 
01156    switch (cmd) {
01157    case CLI_INIT:
01158       e->command = "astobj2 container check";
01159       e->usage =
01160          "Usage: astobj2 container check <name>\n"
01161          "  Perform a container integrity check on <name>.\n";
01162       return NULL;
01163    case CLI_GENERATE:
01164       return complete_container_names(a);
01165    }
01166 
01167    if (a->argc != 4) {
01168       return CLI_SHOWUSAGE;
01169    }
01170 
01171    name = a->argv[3];
01172    reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
01173    if (reg) {
01174       ast_cli(a->fd, "Container check of '%s': %s.\n", name,
01175          ao2_container_check(reg->registered, 0) ? "failed" : "OK");
01176       ao2_t_ref(reg, -1, "Done with registered container object.");
01177    } else {
01178       ast_cli(a->fd, "Container '%s' not found.\n", name);
01179    }
01180 
01181    return CLI_SUCCESS;
01182 }
01183 #endif   /* defined(AO2_DEBUG) */
01184 
01185 #if defined(AO2_DEBUG)
01186 static struct ast_cli_entry cli_astobj2[] = {
01187    AST_CLI_DEFINE(handle_cli_astobj2_container_dump, "Show container contents"),
01188    AST_CLI_DEFINE(handle_cli_astobj2_container_stats, "Show container statistics"),
01189    AST_CLI_DEFINE(handle_cli_astobj2_container_check, "Perform a container integrity check"),
01190 };
01191 #endif   /* defined(AO2_DEBUG) */
01192 
01193 #if defined(AO2_DEBUG)
01194 static void container_cleanup(void)
01195 {
01196    ao2_t_ref(reg_containers, -1, "Releasing container registration container");
01197    reg_containers = NULL;
01198 
01199    ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01200 }
01201 #endif   /* defined(AO2_DEBUG) */
01202 
01203 int container_init(void)
01204 {
01205 #if defined(AO2_DEBUG)
01206    reg_containers = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
01207       AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_reg_sort_cb, NULL,
01208       "Container registration container.");
01209    if (!reg_containers) {
01210       return -1;
01211    }
01212 
01213    ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01214    ast_register_cleanup(container_cleanup);
01215 #endif   /* defined(AO2_DEBUG) */
01216 
01217    return 0;
01218 }
01219 

Generated on Thu Apr 16 06:27:15 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6