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