Thu Jul 9 13:40:23 2009

Asterisk developer's documentation


astobj2.c

Go to the documentation of this file.
00001 /*
00002  * astobj2 - replacement containers for asterisk data structures.
00003  *
00004  * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*
00018  * Function implementing astobj2 objects.
00019  */
00020 #include "asterisk.h"
00021 
00022 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 175122 $")
00023 
00024 #include "asterisk/_private.h"
00025 #include "asterisk/astobj2.h"
00026 #include "asterisk/utils.h"
00027 #include "asterisk/cli.h"
00028 
00029 /*!
00030  * astobj2 objects are always preceded by this data structure,
00031  * which contains a lock, a reference counter,
00032  * the flags and a pointer to a destructor.
00033  * The refcount is used to decide when it is time to
00034  * invoke the destructor.
00035  * The magic number is used for consistency check.
00036  * XXX the lock is not always needed, and its initialization may be
00037  * expensive. Consider making it external.
00038  */
00039 struct __priv_data {
00040    ast_mutex_t lock;
00041    int ref_counter;
00042    ao2_destructor_fn destructor_fn;
00043    /*! for stats */
00044    size_t data_size;
00045    /*! magic number.  This is used to verify that a pointer passed in is a
00046     *  valid astobj2 */
00047    uint32_t magic;
00048 };
00049 
00050 #define  AO2_MAGIC   0xa570b123
00051 
00052 /*!
00053  * What an astobj2 object looks like: fixed-size private data
00054  * followed by variable-size user data.
00055  */
00056 struct astobj2 {
00057    struct __priv_data priv_data;
00058    void *user_data[0];
00059 };
00060 
00061 #ifdef AST_DEVMODE
00062 #define AO2_DEBUG 1
00063 #endif
00064 
00065 #ifdef AO2_DEBUG
00066 struct ao2_stats {
00067    volatile int total_objects;
00068    volatile int total_mem;
00069    volatile int total_containers;
00070    volatile int total_refs;
00071    volatile int total_locked;
00072 };
00073 
00074 static struct ao2_stats ao2;
00075 #endif
00076 
00077 #ifndef HAVE_BKTR /* backtrace support */
00078 void ao2_bt(void) {}
00079 #else
00080 #include <execinfo.h>    /* for backtrace */
00081 
00082 void ao2_bt(void)
00083 {
00084    int c, i;
00085 #define N1  20
00086    void *addresses[N1];
00087    char **strings;
00088 
00089    c = backtrace(addresses, N1);
00090    strings = backtrace_symbols(addresses,c);
00091    ast_verbose("backtrace returned: %d\n", c);
00092    for(i = 0; i < c; i++) {
00093       ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
00094    }
00095    free(strings);
00096 }
00097 #endif
00098 
00099 /*!
00100  * \brief convert from a pointer _p to a user-defined object
00101  *
00102  * \return the pointer to the astobj2 structure
00103  */
00104 static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
00105 {
00106    struct astobj2 *p;
00107 
00108    if (!user_data) {
00109       ast_log(LOG_ERROR, "user_data is NULL\n");
00110       return NULL;
00111    }
00112 
00113    p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
00114    if (AO2_MAGIC != (p->priv_data.magic) ) {
00115       ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
00116       p = NULL;
00117    }
00118 
00119    return p;
00120 }
00121 
00122 /*!
00123  * \brief convert from a pointer _p to an astobj2 object
00124  *
00125  * \return the pointer to the user-defined portion.
00126  */
00127 #define EXTERNAL_OBJ(_p)   ((_p) == NULL ? NULL : (_p)->user_data)
00128 
00129 #ifndef DEBUG_THREADS
00130 int ao2_lock(void *user_data)
00131 #else
00132 int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
00133 #endif
00134 {
00135    struct astobj2 *p = INTERNAL_OBJ(user_data);
00136 
00137    if (p == NULL)
00138       return -1;
00139 
00140 #ifdef AO2_DEBUG
00141    ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00142 #endif
00143 
00144 #ifndef DEBUG_THREADS
00145    return ast_mutex_lock(&p->priv_data.lock);
00146 #else
00147    return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
00148 #endif
00149 }
00150 
00151 #ifndef DEBUG_THREADS
00152 int ao2_unlock(void *user_data)
00153 #else
00154 int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
00155 #endif
00156 {
00157    struct astobj2 *p = INTERNAL_OBJ(user_data);
00158 
00159    if (p == NULL)
00160       return -1;
00161 
00162 #ifdef AO2_DEBUG
00163    ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00164 #endif
00165 
00166 #ifndef DEBUG_THREADS
00167    return ast_mutex_unlock(&p->priv_data.lock);
00168 #else
00169    return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
00170 #endif
00171 }
00172 
00173 #ifndef DEBUG_THREADS
00174 int ao2_trylock(void *user_data)
00175 #else
00176 int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
00177 #endif
00178 {
00179    struct astobj2 *p = INTERNAL_OBJ(user_data);
00180    int ret;
00181    
00182    if (p == NULL)
00183       return -1;
00184 #ifndef DEBUG_THREADS
00185    ret = ast_mutex_trylock(&p->priv_data.lock);
00186 #else
00187    ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
00188 #endif
00189 
00190 #ifdef AO2_DEBUG
00191    if (!ret)
00192       ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00193 #endif
00194    return ret;
00195 }
00196 
00197 /*
00198  * The argument is a pointer to the user portion.
00199  */
00200 int ao2_ref(void *user_data, const int delta)
00201 {
00202    int current_value;
00203    int ret;
00204    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00205 
00206    if (obj == NULL)
00207       return -1;
00208 
00209    /* if delta is 0, just return the refcount */
00210    if (delta == 0)
00211       return (obj->priv_data.ref_counter);
00212 
00213    /* we modify with an atomic operation the reference counter */
00214    ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
00215    current_value = ret + delta;
00216 
00217 #ifdef AO2_DEBUG  
00218    ast_atomic_fetchadd_int(&ao2.total_refs, delta);
00219 #endif
00220 
00221    /* this case must never happen */
00222    if (current_value < 0)
00223       ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
00224 
00225    if (current_value <= 0) { /* last reference, destroy the object */
00226       if (obj->priv_data.destructor_fn != NULL) 
00227          obj->priv_data.destructor_fn(user_data);
00228 
00229       ast_mutex_destroy(&obj->priv_data.lock);
00230 #ifdef AO2_DEBUG
00231       ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
00232       ast_atomic_fetchadd_int(&ao2.total_objects, -1);
00233 #endif
00234       /* for safety, zero-out the astobj2 header and also the
00235        * first word of the user-data, which we make sure is always
00236        * allocated. */
00237       memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
00238       free(obj);
00239    }
00240 
00241    return ret;
00242 }
00243 
00244 /*
00245  * We always alloc at least the size of a void *,
00246  * for debugging purposes.
00247  */
00248 void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
00249 {
00250    /* allocation */
00251    struct astobj2 *obj;
00252 
00253    if (data_size < sizeof(void *))
00254       data_size = sizeof(void *);
00255 
00256    obj = ast_calloc(1, sizeof(*obj) + data_size);
00257 
00258    if (obj == NULL)
00259       return NULL;
00260 
00261    ast_mutex_init(&obj->priv_data.lock);
00262    obj->priv_data.magic = AO2_MAGIC;
00263    obj->priv_data.data_size = data_size;
00264    obj->priv_data.ref_counter = 1;
00265    obj->priv_data.destructor_fn = destructor_fn;   /* can be NULL */
00266 
00267 #ifdef AO2_DEBUG
00268    ast_atomic_fetchadd_int(&ao2.total_objects, 1);
00269    ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
00270    ast_atomic_fetchadd_int(&ao2.total_refs, 1);
00271 #endif
00272 
00273    /* return a pointer to the user data */
00274    return EXTERNAL_OBJ(obj);
00275 }
00276 
00277 /* internal callback to destroy a container. */
00278 static void container_destruct(void *c);
00279 
00280 /* each bucket in the container is a tailq. */
00281 AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
00282 
00283 /*!
00284  * A container; stores the hash and callback functions, information on
00285  * the size, the hash bucket heads, and a version number, starting at 0
00286  * (for a newly created, empty container)
00287  * and incremented every time an object is inserted or deleted.
00288  * The assumption is that an object is never moved in a container,
00289  * but removed and readded with the new number.
00290  * The version number is especially useful when implementing iterators.
00291  * In fact, we can associate a unique, monotonically increasing number to
00292  * each object, which means that, within an iterator, we can store the
00293  * version number of the current object, and easily look for the next one,
00294  * which is the next one in the list with a higher number.
00295  * Since all objects have a version >0, we can use 0 as a marker for
00296  * 'we need the first object in the bucket'.
00297  *
00298  * \todo Linking and unlink objects is typically expensive, as it
00299  * involves a malloc() of a small object which is very inefficient.
00300  * To optimize this, we allocate larger arrays of bucket_list's
00301  * when we run out of them, and then manage our own freelist.
00302  * This will be more efficient as we can do the freelist management while
00303  * we hold the lock (that we need anyways).
00304  */
00305 struct ao2_container {
00306    ao2_hash_fn *hash_fn;
00307    ao2_callback_fn *cmp_fn;
00308    int n_buckets;
00309    /*! Number of elements in the container */
00310    int elements;
00311    /*! described above */
00312    int version;
00313    /*! variable size */
00314    struct bucket buckets[0];
00315 };
00316  
00317 /*!
00318  * \brief always zero hash function
00319  *
00320  * it is convenient to have a hash function that always returns 0.
00321  * This is basically used when we want to have a container that is
00322  * a simple linked list.
00323  *
00324  * \returns 0
00325  */
00326 static int hash_zero(const void *user_obj, const int flags)
00327 {
00328    return 0;
00329 }
00330 
00331 /*
00332  * A container is just an object, after all!
00333  */
00334 struct ao2_container *
00335 ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00336       ao2_callback_fn *cmp_fn)
00337 {
00338    /* XXX maybe consistency check on arguments ? */
00339    /* compute the container size */
00340    size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00341 
00342    struct ao2_container *c = ao2_alloc(container_size, container_destruct);
00343 
00344    if (!c)
00345       return NULL;
00346    
00347    c->version = 1;   /* 0 is a reserved value here */
00348    c->n_buckets = n_buckets;
00349    c->hash_fn = hash_fn ? hash_fn : hash_zero;
00350    c->cmp_fn = cmp_fn;
00351 
00352 #ifdef AO2_DEBUG
00353    ast_atomic_fetchadd_int(&ao2.total_containers, 1);
00354 #endif
00355 
00356    return c;
00357 }
00358 
00359 /*!
00360  * return the number of elements in the container
00361  */
00362 int ao2_container_count(struct ao2_container *c)
00363 {
00364    return c->elements;
00365 }
00366 
00367 /*!
00368  * A structure to create a linked list of entries,
00369  * used within a bucket.
00370  * XXX \todo this should be private to the container code
00371  */
00372 struct bucket_list {
00373    AST_LIST_ENTRY(bucket_list) entry;
00374    int version;
00375    struct astobj2 *astobj;    /* pointer to internal data */
00376 }; 
00377 
00378 /*
00379  * link an object to a container
00380  */
00381 void *ao2_link(struct ao2_container *c, void *user_data)
00382 {
00383    int i;
00384    /* create a new list entry */
00385    struct bucket_list *p;
00386    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00387    
00388    if (!obj)
00389       return NULL;
00390 
00391    if (INTERNAL_OBJ(c) == NULL)
00392       return NULL;
00393 
00394    p = ast_calloc(1, sizeof(*p));
00395    if (!p)
00396       return NULL;
00397 
00398    i = c->hash_fn(user_data, OBJ_POINTER);
00399 
00400    ao2_lock(c);
00401    i %= c->n_buckets;
00402    p->astobj = obj;
00403    p->version = ast_atomic_fetchadd_int(&c->version, 1);
00404    AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
00405    ast_atomic_fetchadd_int(&c->elements, 1);
00406    ao2_ref(user_data, +1);
00407    ao2_unlock(c);
00408    
00409    return p;
00410 }
00411 
00412 /*!
00413  * \brief another convenience function is a callback that matches on address
00414  */
00415 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00416 {
00417    return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00418 }
00419 
00420 /*
00421  * Unlink an object from the container
00422  * and destroy the associated * ao2_bucket_list structure.
00423  */
00424 void *ao2_unlink(struct ao2_container *c, void *user_data)
00425 {
00426    if (INTERNAL_OBJ(user_data) == NULL)   /* safety check on the argument */
00427       return NULL;
00428 
00429    ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
00430 
00431    return NULL;
00432 }
00433 
00434 /*! 
00435  * \brief special callback that matches all 
00436  */ 
00437 static int cb_true(void *user_data, void *arg, int flags)
00438 {
00439    return CMP_MATCH;
00440 }
00441 
00442 /*!
00443  * Browse the container using different stategies accoding the flags.
00444  * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is 
00445  * specified.
00446  */
00447 void *ao2_callback(struct ao2_container *c,
00448    const enum search_flags flags,
00449    ao2_callback_fn *cb_fn, void *arg)
00450 {
00451    int i, last;   /* search boundaries */
00452    void *ret = NULL;
00453 
00454    if (INTERNAL_OBJ(c) == NULL)  /* safety check on the argument */
00455       return NULL;
00456 
00457    if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
00458       ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
00459       return NULL;
00460    }
00461 
00462    /* override the match function if necessary */
00463    if (cb_fn == NULL)   /* if NULL, match everything */
00464       cb_fn = cb_true;
00465    /*
00466     * XXX this can be optimized.
00467     * If we have a hash function and lookup by pointer,
00468     * run the hash function. Otherwise, scan the whole container
00469     * (this only for the time being. We need to optimize this.)
00470     */
00471    if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
00472       i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
00473    else        /* don't know, let's scan all buckets */
00474       i = -1;     /* XXX this must be fixed later. */
00475 
00476    /* determine the search boundaries: i..last-1 */
00477    if (i < 0) {
00478       i = 0;
00479       last = c->n_buckets;
00480    } else {
00481       last = i + 1;
00482    }
00483 
00484    ao2_lock(c);   /* avoid modifications to the content */
00485 
00486    for (; i < last ; i++) {
00487       /* scan the list with prev-cur pointers */
00488       struct bucket_list *cur;
00489 
00490       AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
00491          int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
00492 
00493          /* we found the object, performing operations according flags */
00494          if (match == 0) { /* no match, no stop, continue */
00495             continue;
00496          } else if (match == CMP_STOP) {  /* no match but stop, we are done */
00497             i = last;
00498             break;
00499          }
00500          /* we have a match (CMP_MATCH) here */
00501          if (!(flags & OBJ_NODATA)) {  /* if must return the object, record the value */
00502             /* it is important to handle this case before the unlink */
00503             ret = EXTERNAL_OBJ(cur->astobj);
00504             ao2_ref(ret, 1);
00505          }
00506 
00507          if (flags & OBJ_UNLINK) {  /* must unlink */
00508             struct bucket_list *x = cur;
00509 
00510             /* we are going to modify the container, so update version */
00511             ast_atomic_fetchadd_int(&c->version, 1);
00512             AST_LIST_REMOVE_CURRENT(entry);
00513             /* update number of elements and version */
00514             ast_atomic_fetchadd_int(&c->elements, -1);
00515             ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
00516             free(x); /* free the link record */
00517          }
00518 
00519          if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
00520             /* We found the only match we need */
00521             i = last;   /* force exit from outer loop */
00522             break;
00523          }
00524          if (!(flags & OBJ_NODATA)) {
00525 #if 0 /* XXX to be completed */
00526             /*
00527              * This is the multiple-return case. We need to link
00528              * the object in a list. The refcount is already increased.
00529              */
00530 #endif
00531          }
00532       }
00533       AST_LIST_TRAVERSE_SAFE_END;
00534    }
00535    ao2_unlock(c);
00536    return ret;
00537 }
00538 
00539 /*!
00540  * the find function just invokes the default callback with some reasonable flags.
00541  */
00542 void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
00543 {
00544    return ao2_callback(c, flags, c->cmp_fn, arg);
00545 }
00546 
00547 /*!
00548  * initialize an iterator so we start from the first object
00549  */
00550 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
00551 {
00552    struct ao2_iterator a = {
00553       .c = c,
00554       .flags = flags
00555    };
00556    
00557    return a;
00558 }
00559 
00560 /*
00561  * move to the next element in the container.
00562  */
00563 void * ao2_iterator_next(struct ao2_iterator *a)
00564 {
00565    int lim;
00566    struct bucket_list *p = NULL;
00567    void *ret = NULL;
00568 
00569    if (INTERNAL_OBJ(a->c) == NULL)
00570       return NULL;
00571 
00572    if (!(a->flags & F_AO2I_DONTLOCK))
00573       ao2_lock(a->c);
00574 
00575    /* optimization. If the container is unchanged and
00576     * we have a pointer, try follow it
00577     */
00578    if (a->c->version == a->c_version && (p = a->obj) ) {
00579       if ( (p = AST_LIST_NEXT(p, entry)) )
00580          goto found;
00581       /* nope, start from the next bucket */
00582       a->bucket++;
00583       a->version = 0;
00584       a->obj = NULL;
00585    }
00586 
00587    lim = a->c->n_buckets;
00588 
00589    /* Browse the buckets array, moving to the next
00590     * buckets if we don't find the entry in the current one.
00591     * Stop when we find an element with version number greater
00592     * than the current one (we reset the version to 0 when we
00593     * switch buckets).
00594     */
00595    for (; a->bucket < lim; a->bucket++, a->version = 0) {
00596       /* scan the current bucket */
00597       AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
00598          if (p->version > a->version)
00599             goto found;
00600       }
00601    }
00602 
00603 found:
00604    if (p) {
00605       a->version = p->version;
00606       a->obj = p;
00607       a->c_version = a->c->version;
00608       ret = EXTERNAL_OBJ(p->astobj);
00609       /* inc refcount of returned object */
00610       ao2_ref(ret, 1);
00611    }
00612 
00613    if (!(a->flags & F_AO2I_DONTLOCK))
00614       ao2_unlock(a->c);
00615 
00616    return ret;
00617 }
00618 
00619 /* callback for destroying container.
00620  * we can make it simple as we know what it does
00621  */
00622 static int cd_cb(void *obj, void *arg, int flag)
00623 {
00624    ao2_ref(obj, -1);
00625    return 0;
00626 }
00627    
00628 static void container_destruct(void *_c)
00629 {
00630    struct ao2_container *c = _c;
00631    int i;
00632 
00633    ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
00634 
00635    for (i = 0; i < c->n_buckets; i++) {
00636       struct bucket_list *cur;
00637 
00638       while ((cur = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00639          ast_free(cur);
00640       }
00641    }
00642 
00643 #ifdef AO2_DEBUG
00644    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00645 #endif
00646 }
00647 
00648 #ifdef AO2_DEBUG
00649 static int print_cb(void *obj, void *arg, int flag)
00650 {
00651    int *fd = arg;
00652    char *s = (char *)obj;
00653 
00654    ast_cli(*fd, "string <%s>\n", s);
00655    return 0;
00656 }
00657 
00658 /*
00659  * Print stats
00660  */
00661 static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00662 {
00663    switch (cmd) {
00664    case CLI_INIT:
00665       e->command = "astobj2 stats";
00666       e->usage = "Usage: astobj2 stats\n"
00667             "       Show astobj2 stats\n";
00668       return NULL;
00669    case CLI_GENERATE:
00670       return NULL;
00671    }
00672    ast_cli(a->fd, "Objects    : %d\n", ao2.total_objects);
00673    ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
00674    ast_cli(a->fd, "Memory     : %d\n", ao2.total_mem);
00675    ast_cli(a->fd, "Locked     : %d\n", ao2.total_locked);
00676    ast_cli(a->fd, "Refs       : %d\n", ao2.total_refs);
00677    return CLI_SUCCESS;
00678 }
00679 
00680 /*
00681  * This is testing code for astobj
00682  */
00683 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00684 {
00685    struct ao2_container *c1;
00686    int i, lim;
00687    char *obj;
00688    static int prof_id = -1;
00689    struct ast_cli_args fake_args = { a->fd, 0, NULL };
00690 
00691    switch (cmd) {
00692    case CLI_INIT:
00693       e->command = "astobj2 test";
00694       e->usage = "Usage: astobj2 test <num>\n"
00695             "       Runs astobj2 test. Creates 'num' objects,\n"
00696             "       and test iterators, callbacks and may be other stuff\n";
00697       return NULL;
00698    case CLI_GENERATE:
00699       return NULL;
00700    }
00701 
00702    if (a->argc != 3) {
00703       return CLI_SHOWUSAGE;
00704    }
00705 
00706    if (prof_id == -1)
00707       prof_id = ast_add_profile("ao2_alloc", 0);
00708 
00709    ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
00710    lim = atoi(a->argv[2]);
00711    ast_cli(a->fd, "called astobj_test\n");
00712 
00713    handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
00714    /*
00715     * allocate a container with no default callback, and no hash function.
00716     * No hash means everything goes in the same bucket.
00717     */
00718    c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
00719    ast_cli(a->fd, "container allocated as %p\n", c1);
00720 
00721    /*
00722     * fill the container with objects.
00723     * ao2_alloc() gives us a reference which we pass to the
00724     * container when we do the insert.
00725     */
00726    for (i = 0; i < lim; i++) {
00727       ast_mark(prof_id, 1 /* start */);
00728       obj = ao2_alloc(80, NULL);
00729       ast_mark(prof_id, 0 /* stop */);
00730       ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
00731       sprintf(obj, "-- this is obj %d --", i);
00732       ao2_link(c1, obj);
00733    }
00734    ast_cli(a->fd, "testing callbacks\n");
00735    ao2_callback(c1, 0, print_cb, &a->fd);
00736 
00737    ast_cli(a->fd, "testing iterators, remove every second object\n");
00738    {
00739       struct ao2_iterator ai;
00740       int x = 0;
00741 
00742       ai = ao2_iterator_init(c1, 0);
00743       while ( (obj = ao2_iterator_next(&ai)) ) {
00744          ast_cli(a->fd, "iterator on <%s>\n", obj);
00745          if (x++ & 1)
00746             ao2_unlink(c1, obj);
00747          ao2_ref(obj, -1);
00748       }
00749       ast_cli(a->fd, "testing iterators again\n");
00750       ai = ao2_iterator_init(c1, 0);
00751       while ( (obj = ao2_iterator_next(&ai)) ) {
00752          ast_cli(a->fd, "iterator on <%s>\n", obj);
00753          ao2_ref(obj, -1);
00754       }
00755    }
00756    ast_cli(a->fd, "testing callbacks again\n");
00757    ao2_callback(c1, 0, print_cb, &a->fd);
00758 
00759    ast_verbose("now you should see an error message:\n");
00760    ao2_ref(&i, -1);  /* i is not a valid object so we print an error here */
00761 
00762    ast_cli(a->fd, "destroy container\n");
00763    ao2_ref(c1, -1);  /* destroy container */
00764    handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
00765    return CLI_SUCCESS;
00766 }
00767 
00768 static struct ast_cli_entry cli_astobj2[] = {
00769    AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
00770    AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
00771 };
00772 #endif /* AO2_DEBUG */
00773 
00774 int astobj2_init(void)
00775 {
00776 #ifdef AO2_DEBUG
00777    ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
00778 #endif
00779 
00780    return 0;
00781 }

Generated on Thu Jul 9 13:40:23 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7