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