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: 409567 $")
00028
00029 #include "asterisk/_private.h"
00030 #include "asterisk/astobj2.h"
00031 #include "asterisk/linkedlists.h"
00032 #include "asterisk/utils.h"
00033 #include "asterisk/cli.h"
00034 #define REF_FILE "/tmp/refs"
00035
00036 #if defined(TEST_FRAMEWORK)
00037
00038 #define AO2_DEBUG 1
00039 #endif
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 struct __priv_data {
00050 int ref_counter;
00051 ao2_destructor_fn destructor_fn;
00052
00053 size_t data_size;
00054
00055 uint32_t options;
00056
00057
00058 uint32_t magic;
00059 };
00060
00061 #define AO2_MAGIC 0xa570b123
00062
00063
00064
00065
00066
00067 struct astobj2 {
00068 struct __priv_data priv_data;
00069 void *user_data[0];
00070 };
00071
00072 struct ao2_lock_priv {
00073 ast_mutex_t lock;
00074 };
00075
00076
00077 struct astobj2_lock {
00078 struct ao2_lock_priv mutex;
00079 struct __priv_data priv_data;
00080 void *user_data[0];
00081 };
00082
00083 struct ao2_rwlock_priv {
00084 ast_rwlock_t lock;
00085
00086 int num_lockers;
00087 };
00088
00089
00090 struct astobj2_rwlock {
00091 struct ao2_rwlock_priv rwlock;
00092 struct __priv_data priv_data;
00093 void *user_data[0];
00094 };
00095
00096 #ifdef AO2_DEBUG
00097 struct ao2_stats {
00098 volatile int total_objects;
00099 volatile int total_mem;
00100 volatile int total_containers;
00101 volatile int total_refs;
00102 volatile int total_locked;
00103 };
00104
00105 static struct ao2_stats ao2;
00106 #endif
00107
00108 #ifndef HAVE_BKTR
00109 void ao2_bt(void) {}
00110 #else
00111 #include <execinfo.h>
00112
00113 void ao2_bt(void)
00114 {
00115 int c, i;
00116 #define N1 20
00117 void *addresses[N1];
00118 char **strings;
00119
00120 c = backtrace(addresses, N1);
00121 strings = ast_bt_get_symbols(addresses,c);
00122 ast_verbose("backtrace returned: %d\n", c);
00123 for(i = 0; i < c; i++) {
00124 ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
00125 }
00126 ast_std_free(strings);
00127 }
00128 #endif
00129
00130 #define INTERNAL_OBJ_MUTEX(user_data) \
00131 ((struct astobj2_lock *) (((char *) (user_data)) - sizeof(struct astobj2_lock)))
00132
00133 #define INTERNAL_OBJ_RWLOCK(user_data) \
00134 ((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
00135
00136
00137
00138
00139
00140
00141 static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
00142 {
00143 struct astobj2 *p;
00144
00145 if (!user_data) {
00146 ast_log(LOG_ERROR, "user_data is NULL\n");
00147 return NULL;
00148 }
00149
00150 p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
00151 if (AO2_MAGIC != p->priv_data.magic) {
00152 if (p->priv_data.magic) {
00153 ast_log(LOG_ERROR, "bad magic number 0x%x for object %p\n",
00154 p->priv_data.magic, user_data);
00155 } else {
00156 ast_log(LOG_ERROR,
00157 "bad magic number for object %p. Object is likely destroyed.\n",
00158 user_data);
00159 }
00160 ast_assert(0);
00161 return NULL;
00162 }
00163
00164 return p;
00165 }
00166
00167 enum ao2_callback_type {
00168 DEFAULT,
00169 WITH_DATA,
00170 };
00171
00172
00173
00174
00175
00176
00177 #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
00178
00179 int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
00180 {
00181 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00182 struct astobj2_lock *obj_mutex;
00183 struct astobj2_rwlock *obj_rwlock;
00184 int res = 0;
00185
00186 if (obj == NULL) {
00187 return -1;
00188 }
00189
00190 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00191 case AO2_ALLOC_OPT_LOCK_MUTEX:
00192 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
00193 res = __ast_pthread_mutex_lock(file, line, func, var, &obj_mutex->mutex.lock);
00194 #ifdef AO2_DEBUG
00195 if (!res) {
00196 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00197 }
00198 #endif
00199 break;
00200 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00201 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
00202 switch (lock_how) {
00203 case AO2_LOCK_REQ_MUTEX:
00204 case AO2_LOCK_REQ_WRLOCK:
00205 res = __ast_rwlock_wrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
00206 if (!res) {
00207 ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
00208 #ifdef AO2_DEBUG
00209 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00210 #endif
00211 }
00212 break;
00213 case AO2_LOCK_REQ_RDLOCK:
00214 res = __ast_rwlock_rdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
00215 if (!res) {
00216 ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
00217 #ifdef AO2_DEBUG
00218 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00219 #endif
00220 }
00221 break;
00222 }
00223 break;
00224 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00225
00226 break;
00227 default:
00228 ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
00229 user_data);
00230 return -1;
00231 }
00232
00233 return res;
00234 }
00235
00236 int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
00237 {
00238 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00239 struct astobj2_lock *obj_mutex;
00240 struct astobj2_rwlock *obj_rwlock;
00241 int res = 0;
00242 int current_value;
00243
00244 if (obj == NULL) {
00245 return -1;
00246 }
00247
00248 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00249 case AO2_ALLOC_OPT_LOCK_MUTEX:
00250 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
00251 res = __ast_pthread_mutex_unlock(file, line, func, var, &obj_mutex->mutex.lock);
00252 #ifdef AO2_DEBUG
00253 if (!res) {
00254 ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00255 }
00256 #endif
00257 break;
00258 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00259 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
00260
00261 current_value = ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1) - 1;
00262 if (current_value < 0) {
00263
00264 ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -current_value);
00265 }
00266 res = __ast_rwlock_unlock(file, line, func, &obj_rwlock->rwlock.lock, var);
00267 #ifdef AO2_DEBUG
00268 if (!res) {
00269 ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00270 }
00271 #endif
00272 break;
00273 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00274
00275 break;
00276 default:
00277 ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
00278 user_data);
00279 res = -1;
00280 break;
00281 }
00282 return res;
00283 }
00284
00285 int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
00286 {
00287 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00288 struct astobj2_lock *obj_mutex;
00289 struct astobj2_rwlock *obj_rwlock;
00290 int res = 0;
00291
00292 if (obj == NULL) {
00293 return -1;
00294 }
00295
00296 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00297 case AO2_ALLOC_OPT_LOCK_MUTEX:
00298 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
00299 res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock);
00300 #ifdef AO2_DEBUG
00301 if (!res) {
00302 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00303 }
00304 #endif
00305 break;
00306 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00307 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
00308 switch (lock_how) {
00309 case AO2_LOCK_REQ_MUTEX:
00310 case AO2_LOCK_REQ_WRLOCK:
00311 res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
00312 if (!res) {
00313 ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
00314 #ifdef AO2_DEBUG
00315 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00316 #endif
00317 }
00318 break;
00319 case AO2_LOCK_REQ_RDLOCK:
00320 res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
00321 if (!res) {
00322 ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
00323 #ifdef AO2_DEBUG
00324 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00325 #endif
00326 }
00327 break;
00328 }
00329 break;
00330 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00331
00332 return 0;
00333 default:
00334 ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
00335 user_data);
00336 return -1;
00337 }
00338
00339
00340 return res;
00341 }
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360 static enum ao2_lock_req adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
00361 {
00362 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00363 struct astobj2_rwlock *obj_rwlock;
00364 enum ao2_lock_req orig_lock;
00365
00366 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00367 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00368 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
00369 if (obj_rwlock->rwlock.num_lockers < 0) {
00370 orig_lock = AO2_LOCK_REQ_WRLOCK;
00371 } else {
00372 orig_lock = AO2_LOCK_REQ_RDLOCK;
00373 }
00374 switch (lock_how) {
00375 case AO2_LOCK_REQ_MUTEX:
00376 lock_how = AO2_LOCK_REQ_WRLOCK;
00377
00378 case AO2_LOCK_REQ_WRLOCK:
00379 if (lock_how != orig_lock) {
00380
00381 ao2_unlock(user_data);
00382 ao2_wrlock(user_data);
00383 }
00384 break;
00385 case AO2_LOCK_REQ_RDLOCK:
00386 if (!keep_stronger && lock_how != orig_lock) {
00387
00388 ao2_unlock(user_data);
00389 ao2_rdlock(user_data);
00390 }
00391 break;
00392 }
00393 break;
00394 default:
00395 ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
00396
00397 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00398 case AO2_ALLOC_OPT_LOCK_MUTEX:
00399 orig_lock = AO2_LOCK_REQ_MUTEX;
00400 break;
00401 }
00402
00403 return orig_lock;
00404 }
00405
00406 void *ao2_object_get_lockaddr(void *user_data)
00407 {
00408 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00409 struct astobj2_lock *obj_mutex;
00410
00411 if (obj == NULL) {
00412 return NULL;
00413 }
00414
00415 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00416 case AO2_ALLOC_OPT_LOCK_MUTEX:
00417 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
00418 return &obj_mutex->mutex.lock;
00419 default:
00420 break;
00421 }
00422
00423 return NULL;
00424 }
00425
00426 static int internal_ao2_ref(void *user_data, int delta, const char *file, int line, const char *func)
00427 {
00428 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00429 struct astobj2_lock *obj_mutex;
00430 struct astobj2_rwlock *obj_rwlock;
00431 int current_value;
00432 int ret;
00433
00434 if (obj == NULL) {
00435 return -1;
00436 }
00437
00438
00439 if (delta == 0) {
00440 return obj->priv_data.ref_counter;
00441 }
00442
00443
00444 ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
00445 current_value = ret + delta;
00446
00447 #ifdef AO2_DEBUG
00448 ast_atomic_fetchadd_int(&ao2.total_refs, delta);
00449 #endif
00450
00451 if (0 < current_value) {
00452
00453 return ret;
00454 }
00455
00456
00457 if (current_value < 0) {
00458 ast_log(__LOG_ERROR, file, line, func,
00459 "Invalid refcount %d on ao2 object %p\n", current_value, user_data);
00460 }
00461
00462
00463 if (obj->priv_data.destructor_fn != NULL) {
00464 obj->priv_data.destructor_fn(user_data);
00465 }
00466
00467 #ifdef AO2_DEBUG
00468 ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
00469 ast_atomic_fetchadd_int(&ao2.total_objects, -1);
00470 #endif
00471
00472 switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
00473 case AO2_ALLOC_OPT_LOCK_MUTEX:
00474 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
00475 ast_mutex_destroy(&obj_mutex->mutex.lock);
00476
00477
00478
00479
00480
00481
00482 memset(obj_mutex, '\0', sizeof(*obj_mutex) + sizeof(void *) );
00483 ast_free(obj_mutex);
00484 break;
00485 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00486 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
00487 ast_rwlock_destroy(&obj_rwlock->rwlock.lock);
00488
00489
00490
00491
00492
00493
00494 memset(obj_rwlock, '\0', sizeof(*obj_rwlock) + sizeof(void *) );
00495 ast_free(obj_rwlock);
00496 break;
00497 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00498
00499
00500
00501
00502
00503 memset(obj, '\0', sizeof(*obj) + sizeof(void *) );
00504 ast_free(obj);
00505 break;
00506 default:
00507 ast_log(__LOG_ERROR, file, line, func,
00508 "Invalid lock option on ao2 object %p\n", user_data);
00509 break;
00510 }
00511
00512 return ret;
00513 }
00514
00515 int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *func)
00516 {
00517 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00518
00519 if (obj == NULL)
00520 return -1;
00521
00522 if (delta != 0) {
00523 FILE *refo = fopen(REF_FILE, "a");
00524 if (refo) {
00525 fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
00526 delta, file, line, func, tag, obj->priv_data.ref_counter);
00527 fclose(refo);
00528 }
00529 }
00530 if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) {
00531 FILE *refo = fopen(REF_FILE, "a");
00532 if (refo) {
00533 fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, func, tag);
00534 fclose(refo);
00535 }
00536 }
00537 return internal_ao2_ref(user_data, delta, file, line, func);
00538 }
00539
00540 int __ao2_ref(void *user_data, int delta)
00541 {
00542 return internal_ao2_ref(user_data, delta, __FILE__, __LINE__, __FUNCTION__);
00543 }
00544
00545 static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *file, int line, const char *func)
00546 {
00547
00548 struct astobj2 *obj;
00549 struct astobj2_lock *obj_mutex;
00550 struct astobj2_rwlock *obj_rwlock;
00551
00552 if (data_size < sizeof(void *)) {
00553
00554
00555
00556
00557 data_size = sizeof(void *);
00558 }
00559
00560 switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
00561 case AO2_ALLOC_OPT_LOCK_MUTEX:
00562 #if defined(__AST_DEBUG_MALLOC)
00563 obj_mutex = __ast_calloc(1, sizeof(*obj_mutex) + data_size, file, line, func);
00564 #else
00565 obj_mutex = ast_calloc(1, sizeof(*obj_mutex) + data_size);
00566 #endif
00567 if (obj_mutex == NULL) {
00568 return NULL;
00569 }
00570
00571 ast_mutex_init(&obj_mutex->mutex.lock);
00572 obj = (struct astobj2 *) &obj_mutex->priv_data;
00573 break;
00574 case AO2_ALLOC_OPT_LOCK_RWLOCK:
00575 #if defined(__AST_DEBUG_MALLOC)
00576 obj_rwlock = __ast_calloc(1, sizeof(*obj_rwlock) + data_size, file, line, func);
00577 #else
00578 obj_rwlock = ast_calloc(1, sizeof(*obj_rwlock) + data_size);
00579 #endif
00580 if (obj_rwlock == NULL) {
00581 return NULL;
00582 }
00583
00584 ast_rwlock_init(&obj_rwlock->rwlock.lock);
00585 obj = (struct astobj2 *) &obj_rwlock->priv_data;
00586 break;
00587 case AO2_ALLOC_OPT_LOCK_NOLOCK:
00588 #if defined(__AST_DEBUG_MALLOC)
00589 obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, func);
00590 #else
00591 obj = ast_calloc(1, sizeof(*obj) + data_size);
00592 #endif
00593 if (obj == NULL) {
00594 return NULL;
00595 }
00596 break;
00597 default:
00598
00599 ast_log(__LOG_DEBUG, file, line, func, "Invalid lock option requested\n");
00600 return NULL;
00601 }
00602
00603
00604 obj->priv_data.ref_counter = 1;
00605 obj->priv_data.destructor_fn = destructor_fn;
00606 obj->priv_data.data_size = data_size;
00607 obj->priv_data.options = options;
00608 obj->priv_data.magic = AO2_MAGIC;
00609
00610 #ifdef AO2_DEBUG
00611 ast_atomic_fetchadd_int(&ao2.total_objects, 1);
00612 ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
00613 ast_atomic_fetchadd_int(&ao2.total_refs, 1);
00614 #endif
00615
00616
00617 return EXTERNAL_OBJ(obj);
00618 }
00619
00620 void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
00621 const char *file, int line, const char *func, int ref_debug)
00622 {
00623
00624 void *obj;
00625 FILE *refo;
00626
00627 if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, func)) == NULL) {
00628 return NULL;
00629 }
00630
00631 if (ref_debug && (refo = fopen(REF_FILE, "a"))) {
00632 fprintf(refo, "%p =1 %s:%d:%s (%s)\n", obj, file, line, func, tag);
00633 fclose(refo);
00634 }
00635
00636
00637 return obj;
00638 }
00639
00640 void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options)
00641 {
00642 return internal_ao2_alloc(data_size, destructor_fn, options, __FILE__, __LINE__, __FUNCTION__);
00643 }
00644
00645
00646 void __ao2_global_obj_release(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name)
00647 {
00648 if (!holder) {
00649
00650 ast_log(LOG_ERROR, "Must be called with a global object!\n");
00651 return;
00652 }
00653 if (__ast_rwlock_wrlock(file, line, func, &holder->lock, name)) {
00654
00655 return;
00656 }
00657
00658
00659 if (holder->obj) {
00660 if (tag) {
00661 __ao2_ref_debug(holder->obj, -1, tag, file, line, func);
00662 } else {
00663 __ao2_ref(holder->obj, -1);
00664 }
00665 holder->obj = NULL;
00666 }
00667
00668 __ast_rwlock_unlock(file, line, func, &holder->lock, name);
00669 }
00670
00671 void *__ao2_global_obj_replace(struct ao2_global_obj *holder, void *obj, const char *tag, const char *file, int line, const char *func, const char *name)
00672 {
00673 void *obj_old;
00674
00675 if (!holder) {
00676
00677 ast_log(LOG_ERROR, "Must be called with a global object!\n");
00678 return NULL;
00679 }
00680 if (__ast_rwlock_wrlock(file, line, func, &holder->lock, name)) {
00681
00682 return NULL;
00683 }
00684
00685 if (obj) {
00686 if (tag) {
00687 __ao2_ref_debug(obj, +1, tag, file, line, func);
00688 } else {
00689 __ao2_ref(obj, +1);
00690 }
00691 }
00692 obj_old = holder->obj;
00693 holder->obj = obj;
00694
00695 __ast_rwlock_unlock(file, line, func, &holder->lock, name);
00696
00697 return obj_old;
00698 }
00699
00700 int __ao2_global_obj_replace_unref(struct ao2_global_obj *holder, void *obj, const char *tag, const char *file, int line, const char *func, const char *name)
00701 {
00702 void *obj_old;
00703
00704 obj_old = __ao2_global_obj_replace(holder, obj, tag, file, line, func, name);
00705 if (obj_old) {
00706 if (tag) {
00707 __ao2_ref_debug(obj_old, -1, tag, file, line, func);
00708 } else {
00709 __ao2_ref(obj_old, -1);
00710 }
00711 return 1;
00712 }
00713 return 0;
00714 }
00715
00716 void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name)
00717 {
00718 void *obj;
00719
00720 if (!holder) {
00721
00722 ast_log(LOG_ERROR, "Must be called with a global object!\n");
00723 return NULL;
00724 }
00725
00726 if (__ast_rwlock_rdlock(file, line, func, &holder->lock, name)) {
00727
00728 return NULL;
00729 }
00730
00731 obj = holder->obj;
00732 if (obj) {
00733 if (tag) {
00734 __ao2_ref_debug(obj, +1, tag, file, line, func);
00735 } else {
00736 __ao2_ref(obj, +1);
00737 }
00738 }
00739
00740 __ast_rwlock_unlock(file, line, func, &holder->lock, name);
00741
00742 return obj;
00743 }
00744
00745
00746 static void container_destruct(void *c);
00747
00748
00749 static void container_destruct_debug(void *c);
00750
00751
00752
00753
00754
00755
00756 struct bucket_entry {
00757 AST_LIST_ENTRY(bucket_entry) entry;
00758 int version;
00759 struct astobj2 *astobj;
00760 };
00761
00762
00763 AST_LIST_HEAD_NOLOCK(bucket, bucket_entry);
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787 struct ao2_container {
00788 ao2_hash_fn *hash_fn;
00789 ao2_callback_fn *cmp_fn;
00790 int n_buckets;
00791
00792 int elements;
00793
00794 int version;
00795
00796 struct bucket buckets[0];
00797 };
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808 static int hash_zero(const void *user_obj, const int flags)
00809 {
00810 return 0;
00811 }
00812
00813
00814
00815
00816 static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c,
00817 unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
00818 {
00819
00820
00821
00822 if (!c) {
00823 return NULL;
00824 }
00825
00826 c->version = 1;
00827 c->n_buckets = hash_fn ? n_buckets : 1;
00828 c->hash_fn = hash_fn ? hash_fn : hash_zero;
00829 c->cmp_fn = cmp_fn;
00830
00831 #ifdef AO2_DEBUG
00832 ast_atomic_fetchadd_int(&ao2.total_containers, 1);
00833 #endif
00834
00835 return c;
00836 }
00837
00838 struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
00839 unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
00840 const char *tag, const char *file, int line, const char *func, int ref_debug)
00841 {
00842
00843
00844 unsigned int num_buckets = hash_fn ? n_buckets : 1;
00845 size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
00846 struct ao2_container *c = __ao2_alloc_debug(container_size, ref_debug ? container_destruct_debug : container_destruct, options, tag, file, line, func, ref_debug);
00847
00848 return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
00849 }
00850
00851 struct ao2_container *__ao2_container_alloc(unsigned int options,
00852 unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
00853 {
00854
00855
00856 const unsigned int num_buckets = hash_fn ? n_buckets : 1;
00857 size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
00858 struct ao2_container *c = __ao2_alloc(container_size, container_destruct, options);
00859
00860 return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
00861 }
00862
00863
00864
00865
00866 int ao2_container_count(struct ao2_container *c)
00867 {
00868 return c->elements;
00869 }
00870
00871
00872
00873
00874 static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *func)
00875 {
00876 int i;
00877 enum ao2_lock_req orig_lock;
00878
00879 struct bucket_entry *p;
00880 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00881
00882 if (obj == NULL) {
00883 return NULL;
00884 }
00885
00886 if (INTERNAL_OBJ(c) == NULL) {
00887 return NULL;
00888 }
00889
00890 p = ast_calloc(1, sizeof(*p));
00891 if (!p) {
00892 return NULL;
00893 }
00894
00895 i = abs(c->hash_fn(user_data, OBJ_POINTER));
00896
00897 if (flags & OBJ_NOLOCK) {
00898 orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
00899 } else {
00900 ao2_wrlock(c);
00901 orig_lock = AO2_LOCK_REQ_MUTEX;
00902 }
00903
00904 i %= c->n_buckets;
00905 p->astobj = obj;
00906 p->version = ast_atomic_fetchadd_int(&c->version, 1);
00907 AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
00908 ast_atomic_fetchadd_int(&c->elements, 1);
00909
00910 if (tag) {
00911 __ao2_ref_debug(user_data, +1, tag, file, line, func);
00912 } else {
00913 __ao2_ref(user_data, +1);
00914 }
00915
00916 if (flags & OBJ_NOLOCK) {
00917 adjust_lock(c, orig_lock, 0);
00918 } else {
00919 ao2_unlock(c);
00920 }
00921
00922 return p;
00923 }
00924
00925 void *__ao2_link_debug(struct ao2_container *c, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
00926 {
00927 return internal_ao2_link(c, obj_new, flags, tag, file, line, func);
00928 }
00929
00930 void *__ao2_link(struct ao2_container *c, void *obj_new, int flags)
00931 {
00932 return internal_ao2_link(c, obj_new, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
00933 }
00934
00935
00936
00937
00938 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00939 {
00940 return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00941 }
00942
00943
00944
00945
00946
00947 void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
00948 const char *tag, const char *file, int line, const char *func)
00949 {
00950 if (INTERNAL_OBJ(user_data) == NULL) {
00951 return NULL;
00952 }
00953
00954 flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
00955 __ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
00956
00957 return NULL;
00958 }
00959
00960 void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
00961 {
00962 if (INTERNAL_OBJ(user_data) == NULL) {
00963 return NULL;
00964 }
00965
00966 flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
00967 __ao2_callback(c, flags, ao2_match_by_addr, user_data);
00968
00969 return NULL;
00970 }
00971
00972
00973
00974
00975 static int cb_true(void *user_data, void *arg, int flags)
00976 {
00977 return CMP_MATCH;
00978 }
00979
00980
00981
00982
00983 static int cb_true_data(void *user_data, void *arg, void *data, int flags)
00984 {
00985 return CMP_MATCH;
00986 }
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996 static void *internal_ao2_callback(struct ao2_container *c, enum search_flags flags,
00997 void *cb_fn, void *arg, void *data, enum ao2_callback_type type, const char *tag,
00998 const char *file, int line, const char *func)
00999 {
01000 int i, start, last;
01001 enum ao2_lock_req orig_lock;
01002 void *ret = NULL;
01003 ao2_callback_fn *cb_default = NULL;
01004 ao2_callback_data_fn *cb_withdata = NULL;
01005 struct ao2_container *multi_container = NULL;
01006 struct ao2_iterator *multi_iterator = NULL;
01007
01008 if (INTERNAL_OBJ(c) == NULL) {
01009 return NULL;
01010 }
01011
01012
01013
01014
01015
01016
01017 if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
01018
01019
01020
01021
01022
01023
01024
01025 multi_container = __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
01026 if (!multi_container) {
01027 return NULL;
01028 }
01029 if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
01030 ao2_ref(multi_container, -1);
01031 return NULL;
01032 }
01033 }
01034
01035
01036 if (cb_fn == NULL) {
01037 if (type == WITH_DATA) {
01038 cb_withdata = cb_true_data;
01039 } else {
01040 cb_default = cb_true;
01041 }
01042 } else {
01043
01044
01045 if (type == WITH_DATA) {
01046 cb_withdata = cb_fn;
01047 } else {
01048 cb_default = cb_fn;
01049 }
01050 }
01051
01052
01053
01054
01055
01056
01057
01058 if ((flags & (OBJ_POINTER | OBJ_KEY))) {
01059
01060 start = i = c->hash_fn(arg, flags & (OBJ_POINTER | OBJ_KEY)) % c->n_buckets;
01061 } else {
01062
01063 start = i = -1;
01064 }
01065
01066
01067 if (i < 0) {
01068 start = i = 0;
01069 last = c->n_buckets;
01070 } else if ((flags & OBJ_CONTINUE)) {
01071 last = c->n_buckets;
01072 } else {
01073 last = i + 1;
01074 }
01075
01076
01077 if (flags & OBJ_NOLOCK) {
01078 if (flags & OBJ_UNLINK) {
01079 orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
01080 } else {
01081 orig_lock = adjust_lock(c, AO2_LOCK_REQ_RDLOCK, 1);
01082 }
01083 } else {
01084 orig_lock = AO2_LOCK_REQ_MUTEX;
01085 if (flags & OBJ_UNLINK) {
01086 ao2_wrlock(c);
01087 } else {
01088 ao2_rdlock(c);
01089 }
01090 }
01091
01092 for (; i < last ; i++) {
01093
01094 struct bucket_entry *cur;
01095
01096 AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
01097 int match = (CMP_MATCH | CMP_STOP);
01098
01099 if (type == WITH_DATA) {
01100 match &= cb_withdata(EXTERNAL_OBJ(cur->astobj), arg, data, flags);
01101 } else {
01102 match &= cb_default(EXTERNAL_OBJ(cur->astobj), arg, flags);
01103 }
01104
01105
01106 if (match == 0) {
01107 continue;
01108 } else if (match == CMP_STOP) {
01109 i = last;
01110 break;
01111 }
01112
01113
01114 if (!(flags & OBJ_NODATA)) {
01115
01116 ret = EXTERNAL_OBJ(cur->astobj);
01117 if (!(flags & (OBJ_UNLINK | OBJ_MULTIPLE))) {
01118 if (tag) {
01119 __ao2_ref_debug(ret, 1, tag, file, line, func);
01120 } else {
01121 __ao2_ref(ret, 1);
01122 }
01123 }
01124 }
01125
01126
01127
01128
01129 if (ret && (multi_container != NULL)) {
01130 if (tag) {
01131 __ao2_link_debug(multi_container, ret, flags, tag, file, line, func);
01132 } else {
01133 __ao2_link(multi_container, ret, flags);
01134 }
01135 ret = NULL;
01136 }
01137
01138 if (flags & OBJ_UNLINK) {
01139
01140 ast_atomic_fetchadd_int(&c->version, 1);
01141 AST_LIST_REMOVE_CURRENT(entry);
01142
01143 ast_atomic_fetchadd_int(&c->elements, -1);
01144
01145
01146
01147
01148
01149
01150
01151 if (flags & (OBJ_NODATA | OBJ_MULTIPLE)) {
01152 if (tag)
01153 __ao2_ref_debug(EXTERNAL_OBJ(cur->astobj), -1, tag, file, line, func);
01154 else
01155 __ao2_ref(EXTERNAL_OBJ(cur->astobj), -1);
01156 }
01157 ast_free(cur);
01158 }
01159
01160 if ((match & CMP_STOP) || !(flags & OBJ_MULTIPLE)) {
01161
01162
01163 i = last;
01164 break;
01165 }
01166 }
01167 AST_LIST_TRAVERSE_SAFE_END;
01168
01169 if (ret) {
01170 break;
01171 }
01172
01173 if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
01174
01175 i = -1;
01176 last = start;
01177 }
01178 }
01179
01180 if (flags & OBJ_NOLOCK) {
01181 adjust_lock(c, orig_lock, 0);
01182 } else {
01183 ao2_unlock(c);
01184 }
01185
01186
01187 if (multi_container != NULL) {
01188 *multi_iterator = ao2_iterator_init(multi_container,
01189 AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
01190 ao2_ref(multi_container, -1);
01191 return multi_iterator;
01192 } else {
01193 return ret;
01194 }
01195 }
01196
01197 void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
01198 ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
01199 const char *func)
01200 {
01201 return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, func);
01202 }
01203
01204 void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
01205 ao2_callback_fn *cb_fn, void *arg)
01206 {
01207 return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL);
01208 }
01209
01210 void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
01211 ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
01212 int line, const char *func)
01213 {
01214 return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, func);
01215 }
01216
01217 void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
01218 ao2_callback_data_fn *cb_fn, void *arg, void *data)
01219 {
01220 return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL);
01221 }
01222
01223
01224
01225
01226 void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
01227 const char *tag, const char *file, int line, const char *func)
01228 {
01229 void *arged = (void *) arg;
01230
01231 return __ao2_callback_debug(c, flags, c->cmp_fn, arged, tag, file, line, func);
01232 }
01233
01234 void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags)
01235 {
01236 void *arged = (void *) arg;
01237
01238 return __ao2_callback(c, flags, c->cmp_fn, arged);
01239 }
01240
01241
01242
01243
01244 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
01245 {
01246 struct ao2_iterator a = {
01247 .c = c,
01248 .flags = flags
01249 };
01250
01251 ao2_ref(c, +1);
01252
01253 return a;
01254 }
01255
01256
01257
01258
01259 void ao2_iterator_destroy(struct ao2_iterator *iter)
01260 {
01261 ao2_ref(iter->c, -1);
01262 if (iter->flags & AO2_ITERATOR_MALLOCD) {
01263 ast_free(iter);
01264 } else {
01265 iter->c = NULL;
01266 }
01267 }
01268
01269
01270
01271
01272 static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
01273 {
01274 int lim;
01275 enum ao2_lock_req orig_lock;
01276 struct bucket_entry *p = NULL;
01277 void *ret;
01278
01279 if (INTERNAL_OBJ(iter->c) == NULL) {
01280 return NULL;
01281 }
01282
01283 if (iter->flags & AO2_ITERATOR_DONTLOCK) {
01284 if (iter->flags & AO2_ITERATOR_UNLINK) {
01285 orig_lock = adjust_lock(iter->c, AO2_LOCK_REQ_WRLOCK, 1);
01286 } else {
01287 orig_lock = adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
01288 }
01289 } else {
01290 orig_lock = AO2_LOCK_REQ_MUTEX;
01291 if (iter->flags & AO2_ITERATOR_UNLINK) {
01292 ao2_wrlock(iter->c);
01293 } else {
01294 ao2_rdlock(iter->c);
01295 }
01296 }
01297
01298
01299
01300
01301 if (iter->c->version == iter->c_version && (p = iter->obj)) {
01302 if ((p = AST_LIST_NEXT(p, entry))) {
01303 goto found;
01304 }
01305
01306 iter->bucket++;
01307 iter->version = 0;
01308 iter->obj = NULL;
01309 }
01310
01311 lim = iter->c->n_buckets;
01312
01313
01314
01315
01316
01317
01318
01319 for (; iter->bucket < lim; iter->bucket++, iter->version = 0) {
01320
01321 AST_LIST_TRAVERSE(&iter->c->buckets[iter->bucket], p, entry) {
01322 if (p->version > iter->version) {
01323 goto found;
01324 }
01325 }
01326 }
01327
01328 found:
01329 if (p) {
01330 ret = EXTERNAL_OBJ(p->astobj);
01331 if (iter->flags & AO2_ITERATOR_UNLINK) {
01332
01333 ast_atomic_fetchadd_int(&iter->c->version, 1);
01334 AST_LIST_REMOVE(&iter->c->buckets[iter->bucket], p, entry);
01335
01336 ast_atomic_fetchadd_int(&iter->c->elements, -1);
01337 iter->version = 0;
01338 iter->obj = NULL;
01339 iter->c_version = iter->c->version;
01340 ast_free(p);
01341 } else {
01342 iter->version = p->version;
01343 iter->obj = p;
01344 iter->c_version = iter->c->version;
01345
01346
01347 if (tag) {
01348 __ao2_ref_debug(ret, 1, tag, file, line, func);
01349 } else {
01350 __ao2_ref(ret, 1);
01351 }
01352 }
01353 } else {
01354 ret = NULL;
01355 }
01356
01357 if (iter->flags & AO2_ITERATOR_DONTLOCK) {
01358 adjust_lock(iter->c, orig_lock, 0);
01359 } else {
01360 ao2_unlock(iter->c);
01361 }
01362
01363 return ret;
01364 }
01365
01366 void *__ao2_iterator_next_debug(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
01367 {
01368 return internal_ao2_iterator_next(iter, tag, file, line, func);
01369 }
01370
01371 void *__ao2_iterator_next(struct ao2_iterator *iter)
01372 {
01373 return internal_ao2_iterator_next(iter, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
01374 }
01375
01376
01377
01378
01379 static int cd_cb(void *obj, void *arg, int flag)
01380 {
01381 __ao2_ref(obj, -1);
01382 return 0;
01383 }
01384
01385 static int cd_cb_debug(void *obj, void *arg, int flag)
01386 {
01387 __ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__);
01388 return 0;
01389 }
01390
01391 static void container_destruct(void *_c)
01392 {
01393 struct ao2_container *c = _c;
01394 int i;
01395
01396 __ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
01397
01398 for (i = 0; i < c->n_buckets; i++) {
01399 struct bucket_entry *current;
01400
01401 while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
01402 ast_free(current);
01403 }
01404 }
01405
01406 #ifdef AO2_DEBUG
01407 ast_atomic_fetchadd_int(&ao2.total_containers, -1);
01408 #endif
01409 }
01410
01411 static void container_destruct_debug(void *_c)
01412 {
01413 struct ao2_container *c = _c;
01414 int i;
01415
01416 __ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
01417
01418 for (i = 0; i < c->n_buckets; i++) {
01419 struct bucket_entry *current;
01420
01421 while ((current = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
01422 ast_free(current);
01423 }
01424 }
01425
01426 #ifdef AO2_DEBUG
01427 ast_atomic_fetchadd_int(&ao2.total_containers, -1);
01428 #endif
01429 }
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443 static int dup_obj_cb(void *obj, void *arg, int flags)
01444 {
01445 struct ao2_container *dest = arg;
01446
01447 return __ao2_link(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
01448 }
01449
01450 int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
01451 {
01452 void *obj;
01453 int res = 0;
01454
01455 if (!(flags & OBJ_NOLOCK)) {
01456 ao2_rdlock(src);
01457 ao2_wrlock(dest);
01458 }
01459 obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
01460 if (obj) {
01461
01462 __ao2_ref(obj, -1);
01463
01464
01465 __ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
01466 NULL);
01467 res = -1;
01468 }
01469 if (!(flags & OBJ_NOLOCK)) {
01470 ao2_unlock(dest);
01471 ao2_unlock(src);
01472 }
01473
01474 return res;
01475 }
01476
01477 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
01478 {
01479 struct ao2_container *clone;
01480 struct astobj2 *orig_obj;
01481 unsigned int options;
01482 int failed;
01483
01484 orig_obj = INTERNAL_OBJ(orig);
01485 if (!orig_obj) {
01486 return NULL;
01487 }
01488 options = orig_obj->priv_data.options;
01489
01490
01491 clone = __ao2_container_alloc(options, orig->n_buckets, orig->hash_fn, orig->cmp_fn);
01492 if (!clone) {
01493 return NULL;
01494 }
01495
01496 if (flags & OBJ_NOLOCK) {
01497 ao2_wrlock(clone);
01498 }
01499 failed = ao2_container_dup(clone, orig, flags);
01500 if (flags & OBJ_NOLOCK) {
01501 ao2_unlock(clone);
01502 }
01503 if (failed) {
01504
01505 __ao2_ref(clone, -1);
01506 clone = NULL;
01507 }
01508 return clone;
01509 }
01510
01511 struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func, int ref_debug)
01512 {
01513 struct ao2_container *clone;
01514 struct astobj2 *orig_obj;
01515 unsigned int options;
01516 int failed;
01517
01518 orig_obj = INTERNAL_OBJ(orig);
01519 if (!orig_obj) {
01520 return NULL;
01521 }
01522 options = orig_obj->priv_data.options;
01523
01524
01525 clone = __ao2_container_alloc_debug(options, orig->n_buckets, orig->hash_fn,
01526 orig->cmp_fn, tag, file, line, func, ref_debug);
01527 if (!clone) {
01528 return NULL;
01529 }
01530
01531 if (flags & OBJ_NOLOCK) {
01532 ao2_wrlock(clone);
01533 }
01534 failed = ao2_container_dup(clone, orig, flags);
01535 if (flags & OBJ_NOLOCK) {
01536 ao2_unlock(clone);
01537 }
01538 if (failed) {
01539
01540 if (ref_debug) {
01541 __ao2_ref_debug(clone, -1, tag, file, line, func);
01542 } else {
01543 __ao2_ref(clone, -1);
01544 }
01545 clone = NULL;
01546 }
01547 return clone;
01548 }
01549
01550 void __ao2_cleanup_debug(void *obj, const char *file, int line, const char *function)
01551 {
01552 if (obj) {
01553 __ao2_ref_debug(obj, -1, "ao2_cleanup", file, line, function);
01554 }
01555 }
01556
01557 void __ao2_cleanup(void *obj)
01558 {
01559 if (obj) {
01560 ao2_ref(obj, -1);
01561 }
01562 }
01563
01564 void ao2_iterator_cleanup(struct ao2_iterator *iter)
01565 {
01566 if (iter) {
01567 ao2_iterator_destroy(iter);
01568 }
01569 }
01570
01571 #ifdef AO2_DEBUG
01572 static int print_cb(void *obj, void *arg, int flag)
01573 {
01574 struct ast_cli_args *a = (struct ast_cli_args *) arg;
01575 char *s = (char *)obj;
01576
01577 ast_cli(a->fd, "string <%s>\n", s);
01578 return 0;
01579 }
01580
01581
01582
01583
01584 static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01585 {
01586 switch (cmd) {
01587 case CLI_INIT:
01588 e->command = "astobj2 show stats";
01589 e->usage = "Usage: astobj2 show stats\n"
01590 " Show astobj2 show stats\n";
01591 return NULL;
01592 case CLI_GENERATE:
01593 return NULL;
01594 }
01595 ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
01596 ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
01597 ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
01598 ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
01599 ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
01600 return CLI_SUCCESS;
01601 }
01602
01603
01604
01605
01606 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01607 {
01608 struct ao2_container *c1;
01609 struct ao2_container *c2;
01610 int i, lim;
01611 char *obj;
01612 static int prof_id = -1;
01613 struct ast_cli_args fake_args = { a->fd, 0, NULL };
01614
01615 switch (cmd) {
01616 case CLI_INIT:
01617 e->command = "astobj2 test";
01618 e->usage = "Usage: astobj2 test <num>\n"
01619 " Runs astobj2 test. Creates 'num' objects,\n"
01620 " and test iterators, callbacks and may be other stuff\n";
01621 return NULL;
01622 case CLI_GENERATE:
01623 return NULL;
01624 }
01625
01626 if (a->argc != 3) {
01627 return CLI_SHOWUSAGE;
01628 }
01629
01630 if (prof_id == -1)
01631 prof_id = ast_add_profile("ao2_alloc", 0);
01632
01633 ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
01634 lim = atoi(a->argv[2]);
01635 ast_cli(a->fd, "called astobj_test\n");
01636
01637 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
01638
01639
01640
01641
01642 c1 = ao2_t_container_alloc(100, NULL , NULL ,"test");
01643 ast_cli(a->fd, "container allocated as %p\n", c1);
01644
01645
01646
01647
01648
01649
01650 for (i = 0; i < lim; i++) {
01651 ast_mark(prof_id, 1 );
01652 obj = ao2_t_alloc(80, NULL,"test");
01653 ast_mark(prof_id, 0 );
01654 ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
01655 sprintf(obj, "-- this is obj %d --", i);
01656 ao2_link(c1, obj);
01657
01658
01659
01660
01661
01662 ao2_t_ref(obj, -1, "test");
01663 }
01664
01665 ast_cli(a->fd, "testing callbacks\n");
01666 ao2_t_callback(c1, 0, print_cb, a, "test callback");
01667
01668 ast_cli(a->fd, "testing container cloning\n");
01669 c2 = ao2_container_clone(c1, 0);
01670 if (ao2_container_count(c1) != ao2_container_count(c2)) {
01671 ast_cli(a->fd, "Cloned container does not have the same number of objects!\n");
01672 }
01673 ao2_t_callback(c2, 0, print_cb, a, "test callback");
01674
01675 ast_cli(a->fd, "testing iterators, remove every second object\n");
01676 {
01677 struct ao2_iterator ai;
01678 int x = 0;
01679
01680 ai = ao2_iterator_init(c1, 0);
01681 while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01682 ast_cli(a->fd, "iterator on <%s>\n", obj);
01683 if (x++ & 1)
01684 ao2_t_unlink(c1, obj,"test");
01685 ao2_t_ref(obj, -1,"test");
01686 }
01687 ao2_iterator_destroy(&ai);
01688 ast_cli(a->fd, "testing iterators again\n");
01689 ai = ao2_iterator_init(c1, 0);
01690 while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
01691 ast_cli(a->fd, "iterator on <%s>\n", obj);
01692 ao2_t_ref(obj, -1,"test");
01693 }
01694 ao2_iterator_destroy(&ai);
01695 }
01696
01697 ast_cli(a->fd, "testing callbacks again\n");
01698 ao2_t_callback(c1, 0, print_cb, a, "test callback");
01699
01700 ast_verbose("now you should see an error message:\n");
01701 ao2_t_ref(&i, -1, "");
01702
01703 ast_cli(a->fd, "destroy container\n");
01704 ao2_t_ref(c1, -1, "");
01705 ao2_t_ref(c2, -1, "");
01706 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
01707 return CLI_SUCCESS;
01708 }
01709
01710 static struct ast_cli_entry cli_astobj2[] = {
01711 AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
01712 AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
01713 };
01714
01715 static void astobj2_cleanup(void)
01716 {
01717 ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01718 }
01719 #endif
01720
01721 int astobj2_init(void)
01722 {
01723 #ifdef AO2_DEBUG
01724 ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
01725 ast_register_atexit(astobj2_cleanup);
01726 #endif
01727
01728 return 0;
01729 }