Wed Oct 28 11:50:55 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: 222186 $")
00023 
00024 #include "asterisk/_private.h"
00025 #include "asterisk/astobj2.h"
00026 #include "asterisk/utils.h"
00027 #include "asterisk/cli.h"
00028 #define REF_FILE "/tmp/refs"
00029 
00030 /*!
00031  * astobj2 objects are always preceded by this data structure,
00032  * which contains a lock, a reference counter,
00033  * the flags and a pointer to a destructor.
00034  * The refcount is used to decide when it is time to
00035  * invoke the destructor.
00036  * The magic number is used for consistency check.
00037  * XXX the lock is not always needed, and its initialization may be
00038  * expensive. Consider making it external.
00039  */
00040 struct __priv_data {
00041    ast_mutex_t lock;
00042    int ref_counter;
00043    ao2_destructor_fn destructor_fn;
00044    /*! for stats */
00045    size_t data_size;
00046    /*! magic number.  This is used to verify that a pointer passed in is a
00047     *  valid astobj2 */
00048    uint32_t magic;
00049 };
00050 
00051 #define  AO2_MAGIC   0xa570b123
00052 
00053 /*!
00054  * What an astobj2 object looks like: fixed-size private data
00055  * followed by variable-size user data.
00056  */
00057 struct astobj2 {
00058    struct __priv_data priv_data;
00059    void *user_data[0];
00060 };
00061 
00062 #ifdef AST_DEVMODE
00063 #define AO2_DEBUG 1
00064 #endif
00065 
00066 #ifdef AO2_DEBUG
00067 struct ao2_stats {
00068    volatile int total_objects;
00069    volatile int total_mem;
00070    volatile int total_containers;
00071    volatile int total_refs;
00072    volatile int total_locked;
00073 };
00074 
00075 static struct ao2_stats ao2;
00076 #endif
00077 
00078 #ifndef HAVE_BKTR /* backtrace support */
00079 void ao2_bt(void) {}
00080 #else
00081 #include <execinfo.h>    /* for backtrace */
00082 
00083 void ao2_bt(void)
00084 {
00085    int c, i;
00086 #define N1  20
00087    void *addresses[N1];
00088    char **strings;
00089 
00090    c = backtrace(addresses, N1);
00091    strings = backtrace_symbols(addresses,c);
00092    ast_verbose("backtrace returned: %d\n", c);
00093    for(i = 0; i < c; i++) {
00094       ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
00095    }
00096    free(strings);
00097 }
00098 #endif
00099 
00100 /*!
00101  * \brief convert from a pointer _p to a user-defined object
00102  *
00103  * \return the pointer to the astobj2 structure
00104  */
00105 static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
00106 {
00107    struct astobj2 *p;
00108 
00109    if (!user_data) {
00110       ast_log(LOG_ERROR, "user_data is NULL\n");
00111       return NULL;
00112    }
00113 
00114    p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
00115    if (AO2_MAGIC != (p->priv_data.magic) ) {
00116       ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
00117       p = NULL;
00118    }
00119 
00120    return p;
00121 }
00122 
00123 /*!
00124  * \brief convert from a pointer _p to an astobj2 object
00125  *
00126  * \return the pointer to the user-defined portion.
00127  */
00128 #define EXTERNAL_OBJ(_p)   ((_p) == NULL ? NULL : (_p)->user_data)
00129 
00130 /* the underlying functions common to debug and non-debug versions */
00131 
00132 static int __ao2_ref(void *user_data, const int delta);
00133 static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn,
00134                                  ao2_callback_fn *cmp_fn);
00135 static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data);
00136 static void *__ao2_callback(struct ao2_container *c,
00137    const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg,
00138                 char *tag, char *file, int line, const char *funcname);
00139 static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q);
00140 
00141 #ifndef DEBUG_THREADS
00142 int ao2_lock(void *user_data)
00143 #else
00144 int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
00145 #endif
00146 {
00147    struct astobj2 *p = INTERNAL_OBJ(user_data);
00148 
00149    if (p == NULL)
00150       return -1;
00151 
00152 #ifdef AO2_DEBUG
00153    ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00154 #endif
00155 
00156 #ifndef DEBUG_THREADS
00157    return ast_mutex_lock(&p->priv_data.lock);
00158 #else
00159    return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
00160 #endif
00161 }
00162 
00163 #ifndef DEBUG_THREADS
00164 int ao2_unlock(void *user_data)
00165 #else
00166 int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
00167 #endif
00168 {
00169    struct astobj2 *p = INTERNAL_OBJ(user_data);
00170 
00171    if (p == NULL)
00172       return -1;
00173 
00174 #ifdef AO2_DEBUG
00175    ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00176 #endif
00177 
00178 #ifndef DEBUG_THREADS
00179    return ast_mutex_unlock(&p->priv_data.lock);
00180 #else
00181    return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
00182 #endif
00183 }
00184 
00185 #ifndef DEBUG_THREADS
00186 int ao2_trylock(void *user_data)
00187 #else
00188 int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
00189 #endif
00190 {
00191    struct astobj2 *p = INTERNAL_OBJ(user_data);
00192    int ret;
00193    
00194    if (p == NULL)
00195       return -1;
00196 #ifndef DEBUG_THREADS
00197    ret = ast_mutex_trylock(&p->priv_data.lock);
00198 #else
00199    ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
00200 #endif
00201 
00202 #ifdef AO2_DEBUG
00203    if (!ret)
00204       ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00205 #endif
00206    return ret;
00207 }
00208 
00209 void *ao2_object_get_lockaddr(void *obj)
00210 {
00211    struct astobj2 *p = INTERNAL_OBJ(obj);
00212    
00213    if (p == NULL)
00214       return NULL;
00215 
00216    return &p->priv_data.lock;
00217 }
00218 
00219 /*
00220  * The argument is a pointer to the user portion.
00221  */
00222 
00223 
00224 int _ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname)
00225 {
00226    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00227    
00228    if (obj == NULL)
00229       return -1;
00230 
00231    if (delta != 0) {
00232       FILE *refo = fopen(REF_FILE,"a");
00233       fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj->priv_data.ref_counter);
00234       fclose(refo);
00235    }
00236    if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
00237          FILE *refo = fopen(REF_FILE,"a");    
00238          fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);   
00239          fclose(refo);
00240    }
00241    return __ao2_ref(user_data, delta);
00242 }
00243 
00244 int _ao2_ref(void *user_data, const int delta)
00245 {
00246    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00247 
00248    if (obj == NULL)
00249       return -1;
00250 
00251    return __ao2_ref(user_data, delta);
00252 }
00253 
00254 static int __ao2_ref(void *user_data, const int delta)
00255 {
00256    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00257    int current_value;
00258    int ret;
00259 
00260    /* if delta is 0, just return the refcount */
00261    if (delta == 0)
00262       return (obj->priv_data.ref_counter);
00263 
00264    /* we modify with an atomic operation the reference counter */
00265    ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
00266    current_value = ret + delta;
00267 
00268 #ifdef AO2_DEBUG  
00269    ast_atomic_fetchadd_int(&ao2.total_refs, delta);
00270 #endif
00271 
00272    /* this case must never happen */
00273    if (current_value < 0)
00274       ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
00275 
00276    if (current_value <= 0) { /* last reference, destroy the object */
00277       if (obj->priv_data.destructor_fn != NULL) {
00278          obj->priv_data.destructor_fn(user_data);
00279       }
00280 
00281       ast_mutex_destroy(&obj->priv_data.lock);
00282 #ifdef AO2_DEBUG
00283       ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
00284       ast_atomic_fetchadd_int(&ao2.total_objects, -1);
00285 #endif
00286       /* for safety, zero-out the astobj2 header and also the
00287        * first word of the user-data, which we make sure is always
00288        * allocated. */
00289       memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
00290       free(obj);
00291    }
00292 
00293    return ret;
00294 }
00295 
00296 /*
00297  * We always alloc at least the size of a void *,
00298  * for debugging purposes.
00299  */
00300 static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, const char *file, int line, const char *funcname)
00301 {
00302    /* allocation */
00303    struct astobj2 *obj;
00304 
00305    if (data_size < sizeof(void *))
00306       data_size = sizeof(void *);
00307 
00308 #if defined(__AST_DEBUG_MALLOC)
00309    obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
00310 #else
00311    obj = ast_calloc(1, sizeof(*obj) + data_size);
00312 #endif
00313 
00314    if (obj == NULL)
00315       return NULL;
00316 
00317    ast_mutex_init(&obj->priv_data.lock);
00318    obj->priv_data.magic = AO2_MAGIC;
00319    obj->priv_data.data_size = data_size;
00320    obj->priv_data.ref_counter = 1;
00321    obj->priv_data.destructor_fn = destructor_fn;   /* can be NULL */
00322 
00323 #ifdef AO2_DEBUG
00324    ast_atomic_fetchadd_int(&ao2.total_objects, 1);
00325    ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
00326    ast_atomic_fetchadd_int(&ao2.total_refs, 1);
00327 #endif
00328 
00329    /* return a pointer to the user data */
00330    return EXTERNAL_OBJ(obj);
00331 }
00332 
00333 void *_ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag,
00334          const char *file, int line, const char *funcname, int ref_debug)
00335 {
00336    /* allocation */
00337    void *obj;
00338    FILE *refo = ref_debug ? fopen(REF_FILE,"a") : NULL;
00339 
00340    obj = __ao2_alloc(data_size, destructor_fn, file, line, funcname);
00341 
00342    if (obj == NULL)
00343       return NULL;
00344    
00345    if (refo) {
00346       fprintf(refo, "%p =1   %s:%d:%s (%s)\n", obj, file, line, funcname, tag);
00347       fclose(refo);
00348    }
00349 
00350    /* return a pointer to the user data */
00351    return obj;
00352 }
00353 
00354 void *_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
00355 {
00356    return __ao2_alloc(data_size, destructor_fn, __FILE__, __LINE__, __FUNCTION__);
00357 }
00358 
00359 
00360 /* internal callback to destroy a container. */
00361 static void container_destruct(void *c);
00362 
00363 /* internal callback to destroy a container. */
00364 static void container_destruct_debug(void *c);
00365 
00366 /* each bucket in the container is a tailq. */
00367 AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
00368 
00369 /*!
00370  * A container; stores the hash and callback functions, information on
00371  * the size, the hash bucket heads, and a version number, starting at 0
00372  * (for a newly created, empty container)
00373  * and incremented every time an object is inserted or deleted.
00374  * The assumption is that an object is never moved in a container,
00375  * but removed and readded with the new number.
00376  * The version number is especially useful when implementing iterators.
00377  * In fact, we can associate a unique, monotonically increasing number to
00378  * each object, which means that, within an iterator, we can store the
00379  * version number of the current object, and easily look for the next one,
00380  * which is the next one in the list with a higher number.
00381  * Since all objects have a version >0, we can use 0 as a marker for
00382  * 'we need the first object in the bucket'.
00383  *
00384  * \todo Linking and unlink objects is typically expensive, as it
00385  * involves a malloc() of a small object which is very inefficient.
00386  * To optimize this, we allocate larger arrays of bucket_list's
00387  * when we run out of them, and then manage our own freelist.
00388  * This will be more efficient as we can do the freelist management while
00389  * we hold the lock (that we need anyways).
00390  */
00391 struct ao2_container {
00392    ao2_hash_fn *hash_fn;
00393    ao2_callback_fn *cmp_fn;
00394    int n_buckets;
00395    /*! Number of elements in the container */
00396    int elements;
00397    /*! described above */
00398    int version;
00399    /*! variable size */
00400    struct bucket buckets[0];
00401 };
00402  
00403 /*!
00404  * \brief always zero hash function
00405  *
00406  * it is convenient to have a hash function that always returns 0.
00407  * This is basically used when we want to have a container that is
00408  * a simple linked list.
00409  *
00410  * \returns 0
00411  */
00412 static int hash_zero(const void *user_obj, const int flags)
00413 {
00414    return 0;
00415 }
00416 
00417 /*
00418  * A container is just an object, after all!
00419  */
00420 static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00421                                  ao2_callback_fn *cmp_fn)
00422 {
00423    /* XXX maybe consistency check on arguments ? */
00424    /* compute the container size */
00425 
00426    if (!c)
00427       return NULL;
00428    
00429    c->version = 1;   /* 0 is a reserved value here */
00430    c->n_buckets = n_buckets;
00431    c->hash_fn = hash_fn ? hash_fn : hash_zero;
00432    c->cmp_fn = cmp_fn;
00433 
00434 #ifdef AO2_DEBUG
00435    ast_atomic_fetchadd_int(&ao2.total_containers, 1);
00436 #endif
00437 
00438    return c;
00439 }
00440 
00441 struct ao2_container *_ao2_container_alloc_debug(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00442                     ao2_callback_fn *cmp_fn, char *tag, char *file, int line,
00443                     const char *funcname, int ref_debug)
00444 {
00445    /* XXX maybe consistency check on arguments ? */
00446    /* compute the container size */
00447    size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00448    struct ao2_container *c = _ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname, ref_debug);
00449 
00450    return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
00451 }
00452 
00453 struct ao2_container *
00454 _ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00455       ao2_callback_fn *cmp_fn)
00456 {
00457    /* XXX maybe consistency check on arguments ? */
00458    /* compute the container size */
00459 
00460    size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00461    struct ao2_container *c = _ao2_alloc(container_size, container_destruct);
00462 
00463    return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
00464 }
00465 
00466 /*!
00467  * return the number of elements in the container
00468  */
00469 int ao2_container_count(struct ao2_container *c)
00470 {
00471    return c->elements;
00472 }
00473 
00474 /*!
00475  * A structure to create a linked list of entries,
00476  * used within a bucket.
00477  * XXX \todo this should be private to the container code
00478  */
00479 struct bucket_list {
00480    AST_LIST_ENTRY(bucket_list) entry;
00481    int version;
00482    struct astobj2 *astobj;    /* pointer to internal data */
00483 }; 
00484 
00485 /*
00486  * link an object to a container
00487  */
00488 
00489 static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data)
00490 {
00491    int i;
00492    /* create a new list entry */
00493    struct bucket_list *p;
00494    struct astobj2 *obj = INTERNAL_OBJ(user_data);
00495    
00496    if (!obj)
00497       return NULL;
00498 
00499    if (INTERNAL_OBJ(c) == NULL)
00500       return NULL;
00501 
00502    p = ast_calloc(1, sizeof(*p));
00503    if (!p)
00504       return NULL;
00505 
00506    i = abs(c->hash_fn(user_data, OBJ_POINTER));
00507 
00508    ao2_lock(c);
00509    i %= c->n_buckets;
00510    p->astobj = obj;
00511    p->version = ast_atomic_fetchadd_int(&c->version, 1);
00512    AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
00513    ast_atomic_fetchadd_int(&c->elements, 1);
00514 
00515    /* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */
00516    return p;
00517 }
00518 
00519 void *_ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname)
00520 {
00521    struct bucket_list *p = __ao2_link(c, user_data);
00522    
00523    if (p) {
00524       _ao2_ref_debug(user_data, +1, tag, file, line, funcname);
00525       ao2_unlock(c);
00526    }
00527    return p;
00528 }
00529 
00530 void *_ao2_link(struct ao2_container *c, void *user_data)
00531 {
00532    struct bucket_list *p = __ao2_link(c, user_data);
00533    
00534    if (p) {
00535       _ao2_ref(user_data, +1);
00536       ao2_unlock(c);
00537    }
00538    return p;
00539 }
00540 
00541 /*!
00542  * \brief another convenience function is a callback that matches on address
00543  */
00544 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00545 {
00546    return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00547 }
00548 
00549 /*
00550  * Unlink an object from the container
00551  * and destroy the associated * ao2_bucket_list structure.
00552  */
00553 void *_ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag,
00554                   char *file, int line, const char *funcname)
00555 {
00556    if (INTERNAL_OBJ(user_data) == NULL)   /* safety check on the argument */
00557       return NULL;
00558 
00559    _ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname);
00560 
00561    return NULL;
00562 }
00563 
00564 void *_ao2_unlink(struct ao2_container *c, void *user_data)
00565 {
00566    if (INTERNAL_OBJ(user_data) == NULL)   /* safety check on the argument */
00567       return NULL;
00568 
00569    _ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
00570 
00571    return NULL;
00572 }
00573 
00574 /*! 
00575  * \brief special callback that matches all 
00576  */ 
00577 static int cb_true(void *user_data, void *arg, int flags)
00578 {
00579    return CMP_MATCH;
00580 }
00581 
00582 /*!
00583  * Browse the container using different stategies accoding the flags.
00584  * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is 
00585  * specified.
00586  * Luckily, for debug purposes, the added args (tag, file, line, funcname)
00587  * aren't an excessive load to the system, as the callback should not be
00588  * called as often as, say, the ao2_ref func is called.
00589  */
00590 static void *__ao2_callback(struct ao2_container *c,
00591    const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg,
00592    char *tag, char *file, int line, const char *funcname)
00593 {
00594    int i, start, last;  /* search boundaries */
00595    void *ret = NULL;
00596 
00597    if (INTERNAL_OBJ(c) == NULL)  /* safety check on the argument */
00598       return NULL;
00599 
00600    if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
00601       ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
00602       return NULL;
00603    }
00604 
00605    /* override the match function if necessary */
00606    if (cb_fn == NULL)   /* if NULL, match everything */
00607       cb_fn = cb_true;
00608    /*
00609     * XXX this can be optimized.
00610     * If we have a hash function and lookup by pointer,
00611     * run the hash function. Otherwise, scan the whole container
00612     * (this only for the time being. We need to optimize this.)
00613     */
00614    if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
00615       start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
00616    else        /* don't know, let's scan all buckets */
00617       start = i = -1;      /* XXX this must be fixed later. */
00618 
00619    /* determine the search boundaries: i..last-1 */
00620    if (i < 0) {
00621       start = i = 0;
00622       last = c->n_buckets;
00623    } else if ((flags & OBJ_CONTINUE)) {
00624       last = c->n_buckets;
00625    } else {
00626       last = i + 1;
00627    }
00628 
00629    ao2_lock(c);   /* avoid modifications to the content */
00630 
00631    for (; i < last ; i++) {
00632       /* scan the list with prev-cur pointers */
00633       struct bucket_list *cur;
00634 
00635       AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
00636          int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
00637 
00638          /* we found the object, performing operations according flags */
00639          if (match == 0) { /* no match, no stop, continue */
00640             continue;
00641          } else if (match == CMP_STOP) {  /* no match but stop, we are done */
00642             i = last;
00643             break;
00644          }
00645          /* we have a match (CMP_MATCH) here */
00646          if (!(flags & OBJ_NODATA)) {  /* if must return the object, record the value */
00647             /* it is important to handle this case before the unlink */
00648             ret = EXTERNAL_OBJ(cur->astobj);
00649             if (tag)
00650                _ao2_ref_debug(ret, 1, tag, file, line, funcname);
00651             else
00652                _ao2_ref(ret, 1);
00653          }
00654 
00655          if (flags & OBJ_UNLINK) {  /* must unlink */
00656             struct bucket_list *x = cur;
00657 
00658             /* we are going to modify the container, so update version */
00659             ast_atomic_fetchadd_int(&c->version, 1);
00660             AST_LIST_REMOVE_CURRENT(entry);
00661             /* update number of elements and version */
00662             ast_atomic_fetchadd_int(&c->elements, -1);
00663             if (tag)
00664                _ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname);
00665             else
00666                _ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
00667             free(x); /* free the link record */
00668          }
00669 
00670          if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
00671             /* We found the only match we need */
00672             i = last;   /* force exit from outer loop */
00673             break;
00674          }
00675          if (!(flags & OBJ_NODATA)) {
00676 #if 0 /* XXX to be completed */
00677             /*
00678              * This is the multiple-return case. We need to link
00679              * the object in a list. The refcount is already increased.
00680              */
00681 #endif
00682          }
00683       }
00684       AST_LIST_TRAVERSE_SAFE_END;
00685 
00686       if (ret) {
00687          /* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */
00688          break;
00689       }
00690 
00691       if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
00692          /* Move to the beginning to ensure we check every bucket */
00693          i = -1;
00694          last = start;
00695       }
00696    }
00697    ao2_unlock(c);
00698    return ret;
00699 }
00700 
00701 void *_ao2_callback_debug(struct ao2_container *c,
00702                    const enum search_flags flags,
00703                    ao2_callback_fn *cb_fn, void *arg, 
00704                    char *tag, char *file, int line, const char *funcname)
00705 {
00706    return __ao2_callback(c,flags, cb_fn, arg, tag, file, line, funcname);
00707 }
00708 
00709 void *_ao2_callback(struct ao2_container *c,const enum search_flags flags,
00710                     ao2_callback_fn *cb_fn, void *arg)
00711 {
00712    return __ao2_callback(c,flags, cb_fn, arg, NULL, NULL, 0, NULL);
00713 }
00714 
00715 /*!
00716  * the find function just invokes the default callback with some reasonable flags.
00717  */
00718 void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname)
00719 {
00720    return _ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname);
00721 }
00722 
00723 void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
00724 {
00725    return _ao2_callback(c, flags, c->cmp_fn, arg);
00726 }
00727 
00728 /*!
00729  * initialize an iterator so we start from the first object
00730  */
00731 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
00732 {
00733    struct ao2_iterator a = {
00734       .c = c,
00735       .flags = flags
00736    };
00737 
00738    ao2_ref(c, +1);
00739    
00740    return a;
00741 }
00742 
00743 /*!
00744  * destroy an iterator
00745  */
00746 void ao2_iterator_destroy(struct ao2_iterator *i)
00747 {
00748    ao2_ref(i->c, -1);
00749    i->c = NULL;
00750 }
00751 
00752 /*
00753  * move to the next element in the container.
00754  */
00755 static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q)
00756 {
00757    int lim;
00758    struct bucket_list *p = NULL;
00759    void *ret = NULL;
00760 
00761    *q = NULL;
00762    
00763    if (INTERNAL_OBJ(a->c) == NULL)
00764       return NULL;
00765 
00766    if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00767       ao2_lock(a->c);
00768 
00769    /* optimization. If the container is unchanged and
00770     * we have a pointer, try follow it
00771     */
00772    if (a->c->version == a->c_version && (p = a->obj) ) {
00773       if ( (p = AST_LIST_NEXT(p, entry)) )
00774          goto found;
00775       /* nope, start from the next bucket */
00776       a->bucket++;
00777       a->version = 0;
00778       a->obj = NULL;
00779    }
00780 
00781    lim = a->c->n_buckets;
00782 
00783    /* Browse the buckets array, moving to the next
00784     * buckets if we don't find the entry in the current one.
00785     * Stop when we find an element with version number greater
00786     * than the current one (we reset the version to 0 when we
00787     * switch buckets).
00788     */
00789    for (; a->bucket < lim; a->bucket++, a->version = 0) {
00790       /* scan the current bucket */
00791       AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
00792          if (p->version > a->version)
00793             goto found;
00794       }
00795    }
00796 
00797 found:
00798    if (p) {
00799       a->version = p->version;
00800       a->obj = p;
00801       a->c_version = a->c->version;
00802       ret = EXTERNAL_OBJ(p->astobj);
00803       /* inc refcount of returned object */
00804       *q = p;
00805    }
00806 
00807    return ret;
00808 }
00809 
00810 void * _ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname)
00811 {
00812    struct bucket_list *p;
00813    void *ret = NULL;
00814 
00815    ret = __ao2_iterator_next(a, &p);
00816    
00817    if (p) {
00818       /* inc refcount of returned object */
00819       _ao2_ref_debug(ret, 1, tag, file, line, funcname);
00820    }
00821 
00822    if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00823       ao2_unlock(a->c);
00824 
00825    return ret;
00826 }
00827 
00828 void * _ao2_iterator_next(struct ao2_iterator *a)
00829 {
00830    struct bucket_list *p = NULL;
00831    void *ret = NULL;
00832 
00833    ret = __ao2_iterator_next(a, &p);
00834    
00835    if (p) {
00836       /* inc refcount of returned object */
00837       _ao2_ref(ret, 1);
00838    }
00839 
00840    if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00841       ao2_unlock(a->c);
00842 
00843    return ret;
00844 }
00845 
00846 /* callback for destroying container.
00847  * we can make it simple as we know what it does
00848  */
00849 static int cd_cb(void *obj, void *arg, int flag)
00850 {
00851    _ao2_ref(obj, -1);
00852    return 0;
00853 }
00854    
00855 static int cd_cb_debug(void *obj, void *arg, int flag)
00856 {
00857    _ao2_ref_debug(obj, -1, "deref object via container destroy",  __FILE__, __LINE__, __PRETTY_FUNCTION__);
00858    return 0;
00859 }
00860    
00861 static void container_destruct(void *_c)
00862 {
00863    struct ao2_container *c = _c;
00864    int i;
00865 
00866    _ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
00867 
00868    for (i = 0; i < c->n_buckets; i++) {
00869       struct bucket_list *current;
00870 
00871       while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00872          ast_free(current);
00873       }
00874    }
00875 
00876 #ifdef AO2_DEBUG
00877    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00878 #endif
00879 }
00880 
00881 static void container_destruct_debug(void *_c)
00882 {
00883    struct ao2_container *c = _c;
00884    int i;
00885 
00886    _ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
00887 
00888    for (i = 0; i < c->n_buckets; i++) {
00889       struct bucket_list *current;
00890 
00891       while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00892          ast_free(current);
00893       }
00894    }
00895 
00896 #ifdef AO2_DEBUG
00897    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00898 #endif
00899 }
00900 
00901 #ifdef AO2_DEBUG
00902 static int print_cb(void *obj, void *arg, int flag)
00903 {
00904    int *fd = arg;
00905    char *s = (char *)obj;
00906 
00907    ast_cli(*fd, "string <%s>\n", s);
00908    return 0;
00909 }
00910 
00911 /*
00912  * Print stats
00913  */
00914 static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00915 {
00916    switch (cmd) {
00917    case CLI_INIT:
00918       e->command = "astobj2 stats";
00919       e->usage = "Usage: astobj2 stats\n"
00920             "       Show astobj2 stats\n";
00921       return NULL;
00922    case CLI_GENERATE:
00923       return NULL;
00924    }
00925    ast_cli(a->fd, "Objects    : %d\n", ao2.total_objects);
00926    ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
00927    ast_cli(a->fd, "Memory     : %d\n", ao2.total_mem);
00928    ast_cli(a->fd, "Locked     : %d\n", ao2.total_locked);
00929    ast_cli(a->fd, "Refs       : %d\n", ao2.total_refs);
00930    return CLI_SUCCESS;
00931 }
00932 
00933 /*
00934  * This is testing code for astobj
00935  */
00936 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00937 {
00938    struct ao2_container *c1;
00939    int i, lim;
00940    char *obj;
00941    static int prof_id = -1;
00942    struct ast_cli_args fake_args = { a->fd, 0, NULL };
00943 
00944    switch (cmd) {
00945    case CLI_INIT:
00946       e->command = "astobj2 test";
00947       e->usage = "Usage: astobj2 test <num>\n"
00948             "       Runs astobj2 test. Creates 'num' objects,\n"
00949             "       and test iterators, callbacks and may be other stuff\n";
00950       return NULL;
00951    case CLI_GENERATE:
00952       return NULL;
00953    }
00954 
00955    if (a->argc != 3) {
00956       return CLI_SHOWUSAGE;
00957    }
00958 
00959    if (prof_id == -1)
00960       prof_id = ast_add_profile("ao2_alloc", 0);
00961 
00962    ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
00963    lim = atoi(a->argv[2]);
00964    ast_cli(a->fd, "called astobj_test\n");
00965 
00966    handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
00967    /*
00968     * allocate a container with no default callback, and no hash function.
00969     * No hash means everything goes in the same bucket.
00970     */
00971    c1 = ao2_t_container_alloc(100, NULL /* no callback */, NULL /* no hash */,"test");
00972    ast_cli(a->fd, "container allocated as %p\n", c1);
00973 
00974    /*
00975     * fill the container with objects.
00976     * ao2_alloc() gives us a reference which we pass to the
00977     * container when we do the insert.
00978     */
00979    for (i = 0; i < lim; i++) {
00980       ast_mark(prof_id, 1 /* start */);
00981       obj = ao2_t_alloc(80, NULL,"test");
00982       ast_mark(prof_id, 0 /* stop */);
00983       ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
00984       sprintf(obj, "-- this is obj %d --", i);
00985       ao2_link(c1, obj);
00986       /* At this point, the refcount on obj is 2 due to the allocation
00987        * and linking. We can go ahead and reduce the refcount by 1
00988        * right here so that when the container is unreffed later, the
00989        * objects will be freed
00990        */
00991       ao2_t_ref(obj, -1, "test");
00992    }
00993    ast_cli(a->fd, "testing callbacks\n");
00994    ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
00995    ast_cli(a->fd, "testing iterators, remove every second object\n");
00996    {
00997       struct ao2_iterator ai;
00998       int x = 0;
00999 
01000       ai = ao2_iterator_init(c1, 0);
01001       while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01002          ast_cli(a->fd, "iterator on <%s>\n", obj);
01003          if (x++ & 1)
01004             ao2_t_unlink(c1, obj,"test");
01005          ao2_t_ref(obj, -1,"test");
01006       }
01007       ast_cli(a->fd, "testing iterators again\n");
01008       ai = ao2_iterator_init(c1, 0);
01009       while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01010          ast_cli(a->fd, "iterator on <%s>\n", obj);
01011          ao2_t_ref(obj, -1,"test");
01012       }
01013    }
01014    ast_cli(a->fd, "testing callbacks again\n");
01015    ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
01016 
01017    ast_verbose("now you should see an error message:\n");
01018    ao2_t_ref(&i, -1, "");  /* i is not a valid object so we print an error here */
01019 
01020    ast_cli(a->fd, "destroy container\n");
01021    ao2_t_ref(c1, -1, "");  /* destroy container */
01022    handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
01023    return CLI_SUCCESS;
01024 }
01025 
01026 static struct ast_cli_entry cli_astobj2[] = {
01027    AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
01028    AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
01029 };
01030 #endif /* AO2_DEBUG */
01031 
01032 int astobj2_init(void)
01033 {
01034 #ifdef AO2_DEBUG
01035    ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01036 #endif
01037 
01038    return 0;
01039 }

Generated on Wed Oct 28 11:50:55 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6