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