00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "asterisk.h"
00021
00022 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 254717 $")
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
00032
00033
00034
00035
00036
00037
00038
00039
00040 struct __priv_data {
00041 ast_mutex_t lock;
00042 int ref_counter;
00043 ao2_destructor_fn destructor_fn;
00044
00045 size_t data_size;
00046
00047
00048 uint32_t magic;
00049 };
00050
00051 #define AO2_MAGIC 0xa570b123
00052
00053
00054
00055
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
00079 void ao2_bt(void) {}
00080 #else
00081 #include <execinfo.h>
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
00102
00103
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
00125
00126
00127
00128 #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
00129
00130
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 #ifdef DEBUG_THREADS
00142
00143 #undef ao2_lock
00144 #undef ao2_trylock
00145 #undef ao2_unlock
00146 #endif
00147
00148 int ao2_lock(void *user_data)
00149 {
00150 struct astobj2 *p = INTERNAL_OBJ(user_data);
00151
00152 if (p == NULL)
00153 return -1;
00154
00155 #ifdef AO2_DEBUG
00156 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00157 #endif
00158
00159 return ast_mutex_lock(&p->priv_data.lock);
00160 }
00161
00162 int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
00163 {
00164 struct astobj2 *p = INTERNAL_OBJ(user_data);
00165
00166 if (p == NULL)
00167 return -1;
00168
00169 #ifdef AO2_DEBUG
00170 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00171 #endif
00172
00173 #ifndef DEBUG_THREADS
00174 return ast_mutex_lock(&p->priv_data.lock);
00175 #else
00176 return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
00177 #endif
00178 }
00179
00180 int ao2_unlock(void *user_data)
00181 {
00182 struct astobj2 *p = INTERNAL_OBJ(user_data);
00183
00184 if (p == NULL)
00185 return -1;
00186
00187 #ifdef AO2_DEBUG
00188 ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00189 #endif
00190
00191 return ast_mutex_unlock(&p->priv_data.lock);
00192 }
00193
00194 int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
00195 {
00196 struct astobj2 *p = INTERNAL_OBJ(user_data);
00197
00198 if (p == NULL)
00199 return -1;
00200
00201 #ifdef AO2_DEBUG
00202 ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00203 #endif
00204
00205 #ifndef DEBUG_THREADS
00206 return ast_mutex_unlock(&p->priv_data.lock);
00207 #else
00208 return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
00209 #endif
00210 }
00211
00212 int ao2_trylock(void *user_data)
00213 {
00214 struct astobj2 *p = INTERNAL_OBJ(user_data);
00215 int ret;
00216
00217 if (p == NULL)
00218 return -1;
00219 ret = ast_mutex_trylock(&p->priv_data.lock);
00220
00221 #ifdef AO2_DEBUG
00222 if (!ret)
00223 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00224 #endif
00225 return ret;
00226 }
00227
00228 int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
00229 {
00230 struct astobj2 *p = INTERNAL_OBJ(user_data);
00231 int ret;
00232
00233 if (p == NULL)
00234 return -1;
00235 #ifndef DEBUG_THREADS
00236 ret = ast_mutex_trylock(&p->priv_data.lock);
00237 #else
00238 ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
00239 #endif
00240
00241 #ifdef AO2_DEBUG
00242 if (!ret)
00243 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00244 #endif
00245 return ret;
00246 }
00247
00248 void *ao2_object_get_lockaddr(void *obj)
00249 {
00250 struct astobj2 *p = INTERNAL_OBJ(obj);
00251
00252 if (p == NULL)
00253 return NULL;
00254
00255 return &p->priv_data.lock;
00256 }
00257
00258
00259
00260
00261
00262
00263 int _ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname)
00264 {
00265 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00266
00267 if (obj == NULL)
00268 return -1;
00269
00270 if (delta != 0) {
00271 FILE *refo = fopen(REF_FILE,"a");
00272 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);
00273 fclose(refo);
00274 }
00275 if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) {
00276 FILE *refo = fopen(REF_FILE,"a");
00277 fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
00278 fclose(refo);
00279 }
00280 return __ao2_ref(user_data, delta);
00281 }
00282
00283 int _ao2_ref(void *user_data, const int delta)
00284 {
00285 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00286
00287 if (obj == NULL)
00288 return -1;
00289
00290 return __ao2_ref(user_data, delta);
00291 }
00292
00293 static int __ao2_ref(void *user_data, const int delta)
00294 {
00295 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00296 int current_value;
00297 int ret;
00298
00299 if (obj == NULL)
00300 return -1;
00301
00302
00303 if (delta == 0)
00304 return (obj->priv_data.ref_counter);
00305
00306
00307 ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
00308 current_value = ret + delta;
00309
00310 #ifdef AO2_DEBUG
00311 ast_atomic_fetchadd_int(&ao2.total_refs, delta);
00312 #endif
00313
00314
00315 if (current_value < 0)
00316 ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
00317
00318 if (current_value <= 0) {
00319 if (obj->priv_data.destructor_fn != NULL) {
00320 obj->priv_data.destructor_fn(user_data);
00321 }
00322
00323 ast_mutex_destroy(&obj->priv_data.lock);
00324 #ifdef AO2_DEBUG
00325 ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
00326 ast_atomic_fetchadd_int(&ao2.total_objects, -1);
00327 #endif
00328
00329
00330
00331 memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
00332 free(obj);
00333 }
00334
00335 return ret;
00336 }
00337
00338
00339
00340
00341
00342 static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, const char *file, int line, const char *funcname)
00343 {
00344
00345 struct astobj2 *obj;
00346
00347 if (data_size < sizeof(void *))
00348 data_size = sizeof(void *);
00349
00350 #if defined(__AST_DEBUG_MALLOC)
00351 obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
00352 #else
00353 obj = ast_calloc(1, sizeof(*obj) + data_size);
00354 #endif
00355
00356 if (obj == NULL)
00357 return NULL;
00358
00359 ast_mutex_init(&obj->priv_data.lock);
00360 obj->priv_data.magic = AO2_MAGIC;
00361 obj->priv_data.data_size = data_size;
00362 obj->priv_data.ref_counter = 1;
00363 obj->priv_data.destructor_fn = destructor_fn;
00364
00365 #ifdef AO2_DEBUG
00366 ast_atomic_fetchadd_int(&ao2.total_objects, 1);
00367 ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
00368 ast_atomic_fetchadd_int(&ao2.total_refs, 1);
00369 #endif
00370
00371
00372 return EXTERNAL_OBJ(obj);
00373 }
00374
00375 void *_ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag,
00376 const char *file, int line, const char *funcname, int ref_debug)
00377 {
00378
00379 void *obj;
00380 FILE *refo = ref_debug ? fopen(REF_FILE,"a") : NULL;
00381
00382 obj = __ao2_alloc(data_size, destructor_fn, file, line, funcname);
00383
00384 if (obj == NULL)
00385 return NULL;
00386
00387 if (refo) {
00388 fprintf(refo, "%p =1 %s:%d:%s (%s)\n", obj, file, line, funcname, tag);
00389 fclose(refo);
00390 }
00391
00392
00393 return obj;
00394 }
00395
00396 void *_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
00397 {
00398 return __ao2_alloc(data_size, destructor_fn, __FILE__, __LINE__, __FUNCTION__);
00399 }
00400
00401
00402
00403 static void container_destruct(void *c);
00404
00405
00406 static void container_destruct_debug(void *c);
00407
00408
00409 AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 struct ao2_container {
00434 ao2_hash_fn *hash_fn;
00435 ao2_callback_fn *cmp_fn;
00436 int n_buckets;
00437
00438 int elements;
00439
00440 int version;
00441
00442 struct bucket buckets[0];
00443 };
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454 static int hash_zero(const void *user_obj, const int flags)
00455 {
00456 return 0;
00457 }
00458
00459
00460
00461
00462 static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00463 ao2_callback_fn *cmp_fn)
00464 {
00465
00466
00467
00468 if (!c)
00469 return NULL;
00470
00471 c->version = 1;
00472 c->n_buckets = n_buckets;
00473 c->hash_fn = hash_fn ? hash_fn : hash_zero;
00474 c->cmp_fn = cmp_fn;
00475
00476 #ifdef AO2_DEBUG
00477 ast_atomic_fetchadd_int(&ao2.total_containers, 1);
00478 #endif
00479
00480 return c;
00481 }
00482
00483 struct ao2_container *_ao2_container_alloc_debug(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00484 ao2_callback_fn *cmp_fn, char *tag, char *file, int line,
00485 const char *funcname, int ref_debug)
00486 {
00487
00488
00489 size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00490 struct ao2_container *c = _ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname, ref_debug);
00491
00492 return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
00493 }
00494
00495 struct ao2_container *
00496 _ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00497 ao2_callback_fn *cmp_fn)
00498 {
00499
00500
00501
00502 size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00503 struct ao2_container *c = _ao2_alloc(container_size, container_destruct);
00504
00505 return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
00506 }
00507
00508
00509
00510
00511 int ao2_container_count(struct ao2_container *c)
00512 {
00513 return c->elements;
00514 }
00515
00516
00517
00518
00519
00520
00521 struct bucket_list {
00522 AST_LIST_ENTRY(bucket_list) entry;
00523 int version;
00524 struct astobj2 *astobj;
00525 };
00526
00527
00528
00529
00530
00531 static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data)
00532 {
00533 int i;
00534
00535 struct bucket_list *p;
00536 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00537
00538 if (obj == NULL)
00539 return NULL;
00540
00541 if (INTERNAL_OBJ(c) == NULL)
00542 return NULL;
00543
00544 p = ast_calloc(1, sizeof(*p));
00545 if (!p)
00546 return NULL;
00547
00548 i = abs(c->hash_fn(user_data, OBJ_POINTER));
00549
00550 ao2_lock(c);
00551 i %= c->n_buckets;
00552 p->astobj = obj;
00553 p->version = ast_atomic_fetchadd_int(&c->version, 1);
00554 AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
00555 ast_atomic_fetchadd_int(&c->elements, 1);
00556
00557
00558 return p;
00559 }
00560
00561 void *_ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname)
00562 {
00563 struct bucket_list *p = __ao2_link(c, user_data);
00564
00565 if (p) {
00566 _ao2_ref_debug(user_data, +1, tag, file, line, funcname);
00567 ao2_unlock(c);
00568 }
00569 return p;
00570 }
00571
00572 void *_ao2_link(struct ao2_container *c, void *user_data)
00573 {
00574 struct bucket_list *p = __ao2_link(c, user_data);
00575
00576 if (p) {
00577 _ao2_ref(user_data, +1);
00578 ao2_unlock(c);
00579 }
00580 return p;
00581 }
00582
00583
00584
00585
00586 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00587 {
00588 return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00589 }
00590
00591
00592
00593
00594
00595 void *_ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag,
00596 char *file, int line, const char *funcname)
00597 {
00598 if (INTERNAL_OBJ(user_data) == NULL)
00599 return NULL;
00600
00601 _ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname);
00602
00603 return NULL;
00604 }
00605
00606 void *_ao2_unlink(struct ao2_container *c, void *user_data)
00607 {
00608 if (INTERNAL_OBJ(user_data) == NULL)
00609 return NULL;
00610
00611 _ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
00612
00613 return NULL;
00614 }
00615
00616
00617
00618
00619 static int cb_true(void *user_data, void *arg, int flags)
00620 {
00621 return CMP_MATCH;
00622 }
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632 static void *__ao2_callback(struct ao2_container *c,
00633 const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg,
00634 char *tag, char *file, int line, const char *funcname)
00635 {
00636 int i, start, last;
00637 void *ret = NULL;
00638
00639 if (INTERNAL_OBJ(c) == NULL)
00640 return NULL;
00641
00642 if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
00643 ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
00644 return NULL;
00645 }
00646
00647
00648 if (cb_fn == NULL)
00649 cb_fn = cb_true;
00650
00651
00652
00653
00654
00655
00656 if ((flags & OBJ_POINTER))
00657 start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
00658 else
00659 start = i = -1;
00660
00661
00662 if (i < 0) {
00663 start = i = 0;
00664 last = c->n_buckets;
00665 } else if ((flags & OBJ_CONTINUE)) {
00666 last = c->n_buckets;
00667 } else {
00668 last = i + 1;
00669 }
00670
00671 ao2_lock(c);
00672
00673 for (; i < last ; i++) {
00674
00675 struct bucket_list *cur;
00676
00677 AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
00678 int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
00679
00680
00681 if (match == 0) {
00682 continue;
00683 } else if (match == CMP_STOP) {
00684 i = last;
00685 break;
00686 }
00687
00688 if (!(flags & OBJ_NODATA)) {
00689
00690 ret = EXTERNAL_OBJ(cur->astobj);
00691 if (tag)
00692 _ao2_ref_debug(ret, 1, tag, file, line, funcname);
00693 else
00694 _ao2_ref(ret, 1);
00695 }
00696
00697 if (flags & OBJ_UNLINK) {
00698 struct bucket_list *x = cur;
00699
00700
00701 ast_atomic_fetchadd_int(&c->version, 1);
00702 AST_LIST_REMOVE_CURRENT(entry);
00703
00704 ast_atomic_fetchadd_int(&c->elements, -1);
00705 if (tag)
00706 _ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname);
00707 else
00708 _ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
00709 free(x);
00710 }
00711
00712 if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
00713
00714 i = last;
00715 break;
00716 }
00717 if (!(flags & OBJ_NODATA)) {
00718 #if 0
00719
00720
00721
00722
00723 #endif
00724 }
00725 }
00726 AST_LIST_TRAVERSE_SAFE_END;
00727
00728 if (ret) {
00729
00730 break;
00731 }
00732
00733 if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
00734
00735 i = -1;
00736 last = start;
00737 }
00738 }
00739 ao2_unlock(c);
00740 return ret;
00741 }
00742
00743 void *_ao2_callback_debug(struct ao2_container *c,
00744 const enum search_flags flags,
00745 ao2_callback_fn *cb_fn, void *arg,
00746 char *tag, char *file, int line, const char *funcname)
00747 {
00748 return __ao2_callback(c,flags, cb_fn, arg, tag, file, line, funcname);
00749 }
00750
00751 void *_ao2_callback(struct ao2_container *c,const enum search_flags flags,
00752 ao2_callback_fn *cb_fn, void *arg)
00753 {
00754 return __ao2_callback(c,flags, cb_fn, arg, NULL, NULL, 0, NULL);
00755 }
00756
00757
00758
00759
00760 void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname)
00761 {
00762 return _ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname);
00763 }
00764
00765 void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
00766 {
00767 return _ao2_callback(c, flags, c->cmp_fn, arg);
00768 }
00769
00770
00771
00772
00773 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
00774 {
00775 struct ao2_iterator a = {
00776 .c = c,
00777 .flags = flags
00778 };
00779
00780 ao2_ref(c, +1);
00781
00782 return a;
00783 }
00784
00785
00786
00787
00788 void ao2_iterator_destroy(struct ao2_iterator *i)
00789 {
00790 ao2_ref(i->c, -1);
00791 i->c = NULL;
00792 }
00793
00794
00795
00796
00797 static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q)
00798 {
00799 int lim;
00800 struct bucket_list *p = NULL;
00801 void *ret = NULL;
00802
00803 *q = NULL;
00804
00805 if (INTERNAL_OBJ(a->c) == NULL)
00806 return NULL;
00807
00808 if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00809 ao2_lock(a->c);
00810
00811
00812
00813
00814 if (a->c->version == a->c_version && (p = a->obj) ) {
00815 if ( (p = AST_LIST_NEXT(p, entry)) )
00816 goto found;
00817
00818 a->bucket++;
00819 a->version = 0;
00820 a->obj = NULL;
00821 }
00822
00823 lim = a->c->n_buckets;
00824
00825
00826
00827
00828
00829
00830
00831 for (; a->bucket < lim; a->bucket++, a->version = 0) {
00832
00833 AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
00834 if (p->version > a->version)
00835 goto found;
00836 }
00837 }
00838
00839 found:
00840 if (p) {
00841 a->version = p->version;
00842 a->obj = p;
00843 a->c_version = a->c->version;
00844 ret = EXTERNAL_OBJ(p->astobj);
00845
00846 *q = p;
00847 }
00848
00849 return ret;
00850 }
00851
00852 void * _ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname)
00853 {
00854 struct bucket_list *p;
00855 void *ret = NULL;
00856
00857 ret = __ao2_iterator_next(a, &p);
00858
00859 if (p) {
00860
00861 _ao2_ref_debug(ret, 1, tag, file, line, funcname);
00862 }
00863
00864 if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00865 ao2_unlock(a->c);
00866
00867 return ret;
00868 }
00869
00870 void * _ao2_iterator_next(struct ao2_iterator *a)
00871 {
00872 struct bucket_list *p = NULL;
00873 void *ret = NULL;
00874
00875 ret = __ao2_iterator_next(a, &p);
00876
00877 if (p) {
00878
00879 _ao2_ref(ret, 1);
00880 }
00881
00882 if (!(a->flags & AO2_ITERATOR_DONTLOCK))
00883 ao2_unlock(a->c);
00884
00885 return ret;
00886 }
00887
00888
00889
00890
00891 static int cd_cb(void *obj, void *arg, int flag)
00892 {
00893 _ao2_ref(obj, -1);
00894 return 0;
00895 }
00896
00897 static int cd_cb_debug(void *obj, void *arg, int flag)
00898 {
00899 _ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__);
00900 return 0;
00901 }
00902
00903 static void container_destruct(void *_c)
00904 {
00905 struct ao2_container *c = _c;
00906 int i;
00907
00908 _ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
00909
00910 for (i = 0; i < c->n_buckets; i++) {
00911 struct bucket_list *current;
00912
00913 while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00914 ast_free(current);
00915 }
00916 }
00917
00918 #ifdef AO2_DEBUG
00919 ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00920 #endif
00921 }
00922
00923 static void container_destruct_debug(void *_c)
00924 {
00925 struct ao2_container *c = _c;
00926 int i;
00927
00928 _ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
00929
00930 for (i = 0; i < c->n_buckets; i++) {
00931 struct bucket_list *current;
00932
00933 while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00934 ast_free(current);
00935 }
00936 }
00937
00938 #ifdef AO2_DEBUG
00939 ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00940 #endif
00941 }
00942
00943 #ifdef AO2_DEBUG
00944 static int print_cb(void *obj, void *arg, int flag)
00945 {
00946 int *fd = arg;
00947 char *s = (char *)obj;
00948
00949 ast_cli(*fd, "string <%s>\n", s);
00950 return 0;
00951 }
00952
00953
00954
00955
00956 static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00957 {
00958 switch (cmd) {
00959 case CLI_INIT:
00960 e->command = "astobj2 stats";
00961 e->usage = "Usage: astobj2 stats\n"
00962 " Show astobj2 stats\n";
00963 return NULL;
00964 case CLI_GENERATE:
00965 return NULL;
00966 }
00967 ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
00968 ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
00969 ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
00970 ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
00971 ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
00972 return CLI_SUCCESS;
00973 }
00974
00975
00976
00977
00978 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00979 {
00980 struct ao2_container *c1;
00981 int i, lim;
00982 char *obj;
00983 static int prof_id = -1;
00984 struct ast_cli_args fake_args = { a->fd, 0, NULL };
00985
00986 switch (cmd) {
00987 case CLI_INIT:
00988 e->command = "astobj2 test";
00989 e->usage = "Usage: astobj2 test <num>\n"
00990 " Runs astobj2 test. Creates 'num' objects,\n"
00991 " and test iterators, callbacks and may be other stuff\n";
00992 return NULL;
00993 case CLI_GENERATE:
00994 return NULL;
00995 }
00996
00997 if (a->argc != 3) {
00998 return CLI_SHOWUSAGE;
00999 }
01000
01001 if (prof_id == -1)
01002 prof_id = ast_add_profile("ao2_alloc", 0);
01003
01004 ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
01005 lim = atoi(a->argv[2]);
01006 ast_cli(a->fd, "called astobj_test\n");
01007
01008 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
01009
01010
01011
01012
01013 c1 = ao2_t_container_alloc(100, NULL , NULL ,"test");
01014 ast_cli(a->fd, "container allocated as %p\n", c1);
01015
01016
01017
01018
01019
01020
01021 for (i = 0; i < lim; i++) {
01022 ast_mark(prof_id, 1 );
01023 obj = ao2_t_alloc(80, NULL,"test");
01024 ast_mark(prof_id, 0 );
01025 ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
01026 sprintf(obj, "-- this is obj %d --", i);
01027 ao2_link(c1, obj);
01028
01029
01030
01031
01032
01033 ao2_t_ref(obj, -1, "test");
01034 }
01035 ast_cli(a->fd, "testing callbacks\n");
01036 ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
01037 ast_cli(a->fd, "testing iterators, remove every second object\n");
01038 {
01039 struct ao2_iterator ai;
01040 int x = 0;
01041
01042 ai = ao2_iterator_init(c1, 0);
01043 while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01044 ast_cli(a->fd, "iterator on <%s>\n", obj);
01045 if (x++ & 1)
01046 ao2_t_unlink(c1, obj,"test");
01047 ao2_t_ref(obj, -1,"test");
01048 }
01049 ast_cli(a->fd, "testing iterators again\n");
01050 ai = ao2_iterator_init(c1, 0);
01051 while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01052 ast_cli(a->fd, "iterator on <%s>\n", obj);
01053 ao2_t_ref(obj, -1,"test");
01054 }
01055 }
01056 ast_cli(a->fd, "testing callbacks again\n");
01057 ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
01058
01059 ast_verbose("now you should see an error message:\n");
01060 ao2_t_ref(&i, -1, "");
01061
01062 ast_cli(a->fd, "destroy container\n");
01063 ao2_t_ref(c1, -1, "");
01064 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
01065 return CLI_SUCCESS;
01066 }
01067
01068 static struct ast_cli_entry cli_astobj2[] = {
01069 AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
01070 AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
01071 };
01072 #endif
01073
01074 int astobj2_init(void)
01075 {
01076 #ifdef AO2_DEBUG
01077 ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01078 #endif
01079
01080 return 0;
01081 }