00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "asterisk.h"
00021
00022 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 175122 $")
00023
00024 #include "asterisk/_private.h"
00025 #include "asterisk/astobj2.h"
00026 #include "asterisk/utils.h"
00027 #include "asterisk/cli.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 struct __priv_data {
00040 ast_mutex_t lock;
00041 int ref_counter;
00042 ao2_destructor_fn destructor_fn;
00043
00044 size_t data_size;
00045
00046
00047 uint32_t magic;
00048 };
00049
00050 #define AO2_MAGIC 0xa570b123
00051
00052
00053
00054
00055
00056 struct astobj2 {
00057 struct __priv_data priv_data;
00058 void *user_data[0];
00059 };
00060
00061 #ifdef AST_DEVMODE
00062 #define AO2_DEBUG 1
00063 #endif
00064
00065 #ifdef AO2_DEBUG
00066 struct ao2_stats {
00067 volatile int total_objects;
00068 volatile int total_mem;
00069 volatile int total_containers;
00070 volatile int total_refs;
00071 volatile int total_locked;
00072 };
00073
00074 static struct ao2_stats ao2;
00075 #endif
00076
00077 #ifndef HAVE_BKTR
00078 void ao2_bt(void) {}
00079 #else
00080 #include <execinfo.h>
00081
00082 void ao2_bt(void)
00083 {
00084 int c, i;
00085 #define N1 20
00086 void *addresses[N1];
00087 char **strings;
00088
00089 c = backtrace(addresses, N1);
00090 strings = backtrace_symbols(addresses,c);
00091 ast_verbose("backtrace returned: %d\n", c);
00092 for(i = 0; i < c; i++) {
00093 ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
00094 }
00095 free(strings);
00096 }
00097 #endif
00098
00099
00100
00101
00102
00103
00104 static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
00105 {
00106 struct astobj2 *p;
00107
00108 if (!user_data) {
00109 ast_log(LOG_ERROR, "user_data is NULL\n");
00110 return NULL;
00111 }
00112
00113 p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
00114 if (AO2_MAGIC != (p->priv_data.magic) ) {
00115 ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
00116 p = NULL;
00117 }
00118
00119 return p;
00120 }
00121
00122
00123
00124
00125
00126
00127 #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
00128
00129 #ifndef DEBUG_THREADS
00130 int ao2_lock(void *user_data)
00131 #else
00132 int _ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
00133 #endif
00134 {
00135 struct astobj2 *p = INTERNAL_OBJ(user_data);
00136
00137 if (p == NULL)
00138 return -1;
00139
00140 #ifdef AO2_DEBUG
00141 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00142 #endif
00143
00144 #ifndef DEBUG_THREADS
00145 return ast_mutex_lock(&p->priv_data.lock);
00146 #else
00147 return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
00148 #endif
00149 }
00150
00151 #ifndef DEBUG_THREADS
00152 int ao2_unlock(void *user_data)
00153 #else
00154 int _ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
00155 #endif
00156 {
00157 struct astobj2 *p = INTERNAL_OBJ(user_data);
00158
00159 if (p == NULL)
00160 return -1;
00161
00162 #ifdef AO2_DEBUG
00163 ast_atomic_fetchadd_int(&ao2.total_locked, -1);
00164 #endif
00165
00166 #ifndef DEBUG_THREADS
00167 return ast_mutex_unlock(&p->priv_data.lock);
00168 #else
00169 return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
00170 #endif
00171 }
00172
00173 #ifndef DEBUG_THREADS
00174 int ao2_trylock(void *user_data)
00175 #else
00176 int _ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
00177 #endif
00178 {
00179 struct astobj2 *p = INTERNAL_OBJ(user_data);
00180 int ret;
00181
00182 if (p == NULL)
00183 return -1;
00184 #ifndef DEBUG_THREADS
00185 ret = ast_mutex_trylock(&p->priv_data.lock);
00186 #else
00187 ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
00188 #endif
00189
00190 #ifdef AO2_DEBUG
00191 if (!ret)
00192 ast_atomic_fetchadd_int(&ao2.total_locked, 1);
00193 #endif
00194 return ret;
00195 }
00196
00197
00198
00199
00200 int ao2_ref(void *user_data, const int delta)
00201 {
00202 int current_value;
00203 int ret;
00204 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00205
00206 if (obj == NULL)
00207 return -1;
00208
00209
00210 if (delta == 0)
00211 return (obj->priv_data.ref_counter);
00212
00213
00214 ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
00215 current_value = ret + delta;
00216
00217 #ifdef AO2_DEBUG
00218 ast_atomic_fetchadd_int(&ao2.total_refs, delta);
00219 #endif
00220
00221
00222 if (current_value < 0)
00223 ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
00224
00225 if (current_value <= 0) {
00226 if (obj->priv_data.destructor_fn != NULL)
00227 obj->priv_data.destructor_fn(user_data);
00228
00229 ast_mutex_destroy(&obj->priv_data.lock);
00230 #ifdef AO2_DEBUG
00231 ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
00232 ast_atomic_fetchadd_int(&ao2.total_objects, -1);
00233 #endif
00234
00235
00236
00237 memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
00238 free(obj);
00239 }
00240
00241 return ret;
00242 }
00243
00244
00245
00246
00247
00248 void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
00249 {
00250
00251 struct astobj2 *obj;
00252
00253 if (data_size < sizeof(void *))
00254 data_size = sizeof(void *);
00255
00256 obj = ast_calloc(1, sizeof(*obj) + data_size);
00257
00258 if (obj == NULL)
00259 return NULL;
00260
00261 ast_mutex_init(&obj->priv_data.lock);
00262 obj->priv_data.magic = AO2_MAGIC;
00263 obj->priv_data.data_size = data_size;
00264 obj->priv_data.ref_counter = 1;
00265 obj->priv_data.destructor_fn = destructor_fn;
00266
00267 #ifdef AO2_DEBUG
00268 ast_atomic_fetchadd_int(&ao2.total_objects, 1);
00269 ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
00270 ast_atomic_fetchadd_int(&ao2.total_refs, 1);
00271 #endif
00272
00273
00274 return EXTERNAL_OBJ(obj);
00275 }
00276
00277
00278 static void container_destruct(void *c);
00279
00280
00281 AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 struct ao2_container {
00306 ao2_hash_fn *hash_fn;
00307 ao2_callback_fn *cmp_fn;
00308 int n_buckets;
00309
00310 int elements;
00311
00312 int version;
00313
00314 struct bucket buckets[0];
00315 };
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326 static int hash_zero(const void *user_obj, const int flags)
00327 {
00328 return 0;
00329 }
00330
00331
00332
00333
00334 struct ao2_container *
00335 ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
00336 ao2_callback_fn *cmp_fn)
00337 {
00338
00339
00340 size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
00341
00342 struct ao2_container *c = ao2_alloc(container_size, container_destruct);
00343
00344 if (!c)
00345 return NULL;
00346
00347 c->version = 1;
00348 c->n_buckets = n_buckets;
00349 c->hash_fn = hash_fn ? hash_fn : hash_zero;
00350 c->cmp_fn = cmp_fn;
00351
00352 #ifdef AO2_DEBUG
00353 ast_atomic_fetchadd_int(&ao2.total_containers, 1);
00354 #endif
00355
00356 return c;
00357 }
00358
00359
00360
00361
00362 int ao2_container_count(struct ao2_container *c)
00363 {
00364 return c->elements;
00365 }
00366
00367
00368
00369
00370
00371
00372 struct bucket_list {
00373 AST_LIST_ENTRY(bucket_list) entry;
00374 int version;
00375 struct astobj2 *astobj;
00376 };
00377
00378
00379
00380
00381 void *ao2_link(struct ao2_container *c, void *user_data)
00382 {
00383 int i;
00384
00385 struct bucket_list *p;
00386 struct astobj2 *obj = INTERNAL_OBJ(user_data);
00387
00388 if (!obj)
00389 return NULL;
00390
00391 if (INTERNAL_OBJ(c) == NULL)
00392 return NULL;
00393
00394 p = ast_calloc(1, sizeof(*p));
00395 if (!p)
00396 return NULL;
00397
00398 i = c->hash_fn(user_data, OBJ_POINTER);
00399
00400 ao2_lock(c);
00401 i %= c->n_buckets;
00402 p->astobj = obj;
00403 p->version = ast_atomic_fetchadd_int(&c->version, 1);
00404 AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
00405 ast_atomic_fetchadd_int(&c->elements, 1);
00406 ao2_ref(user_data, +1);
00407 ao2_unlock(c);
00408
00409 return p;
00410 }
00411
00412
00413
00414
00415 int ao2_match_by_addr(void *user_data, void *arg, int flags)
00416 {
00417 return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
00418 }
00419
00420
00421
00422
00423
00424 void *ao2_unlink(struct ao2_container *c, void *user_data)
00425 {
00426 if (INTERNAL_OBJ(user_data) == NULL)
00427 return NULL;
00428
00429 ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
00430
00431 return NULL;
00432 }
00433
00434
00435
00436
00437 static int cb_true(void *user_data, void *arg, int flags)
00438 {
00439 return CMP_MATCH;
00440 }
00441
00442
00443
00444
00445
00446
00447 void *ao2_callback(struct ao2_container *c,
00448 const enum search_flags flags,
00449 ao2_callback_fn *cb_fn, void *arg)
00450 {
00451 int i, last;
00452 void *ret = NULL;
00453
00454 if (INTERNAL_OBJ(c) == NULL)
00455 return NULL;
00456
00457 if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
00458 ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
00459 return NULL;
00460 }
00461
00462
00463 if (cb_fn == NULL)
00464 cb_fn = cb_true;
00465
00466
00467
00468
00469
00470
00471 if ((flags & OBJ_POINTER))
00472 i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
00473 else
00474 i = -1;
00475
00476
00477 if (i < 0) {
00478 i = 0;
00479 last = c->n_buckets;
00480 } else {
00481 last = i + 1;
00482 }
00483
00484 ao2_lock(c);
00485
00486 for (; i < last ; i++) {
00487
00488 struct bucket_list *cur;
00489
00490 AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
00491 int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
00492
00493
00494 if (match == 0) {
00495 continue;
00496 } else if (match == CMP_STOP) {
00497 i = last;
00498 break;
00499 }
00500
00501 if (!(flags & OBJ_NODATA)) {
00502
00503 ret = EXTERNAL_OBJ(cur->astobj);
00504 ao2_ref(ret, 1);
00505 }
00506
00507 if (flags & OBJ_UNLINK) {
00508 struct bucket_list *x = cur;
00509
00510
00511 ast_atomic_fetchadd_int(&c->version, 1);
00512 AST_LIST_REMOVE_CURRENT(entry);
00513
00514 ast_atomic_fetchadd_int(&c->elements, -1);
00515 ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
00516 free(x);
00517 }
00518
00519 if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
00520
00521 i = last;
00522 break;
00523 }
00524 if (!(flags & OBJ_NODATA)) {
00525 #if 0
00526
00527
00528
00529
00530 #endif
00531 }
00532 }
00533 AST_LIST_TRAVERSE_SAFE_END;
00534 }
00535 ao2_unlock(c);
00536 return ret;
00537 }
00538
00539
00540
00541
00542 void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
00543 {
00544 return ao2_callback(c, flags, c->cmp_fn, arg);
00545 }
00546
00547
00548
00549
00550 struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
00551 {
00552 struct ao2_iterator a = {
00553 .c = c,
00554 .flags = flags
00555 };
00556
00557 return a;
00558 }
00559
00560
00561
00562
00563 void * ao2_iterator_next(struct ao2_iterator *a)
00564 {
00565 int lim;
00566 struct bucket_list *p = NULL;
00567 void *ret = NULL;
00568
00569 if (INTERNAL_OBJ(a->c) == NULL)
00570 return NULL;
00571
00572 if (!(a->flags & F_AO2I_DONTLOCK))
00573 ao2_lock(a->c);
00574
00575
00576
00577
00578 if (a->c->version == a->c_version && (p = a->obj) ) {
00579 if ( (p = AST_LIST_NEXT(p, entry)) )
00580 goto found;
00581
00582 a->bucket++;
00583 a->version = 0;
00584 a->obj = NULL;
00585 }
00586
00587 lim = a->c->n_buckets;
00588
00589
00590
00591
00592
00593
00594
00595 for (; a->bucket < lim; a->bucket++, a->version = 0) {
00596
00597 AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
00598 if (p->version > a->version)
00599 goto found;
00600 }
00601 }
00602
00603 found:
00604 if (p) {
00605 a->version = p->version;
00606 a->obj = p;
00607 a->c_version = a->c->version;
00608 ret = EXTERNAL_OBJ(p->astobj);
00609
00610 ao2_ref(ret, 1);
00611 }
00612
00613 if (!(a->flags & F_AO2I_DONTLOCK))
00614 ao2_unlock(a->c);
00615
00616 return ret;
00617 }
00618
00619
00620
00621
00622 static int cd_cb(void *obj, void *arg, int flag)
00623 {
00624 ao2_ref(obj, -1);
00625 return 0;
00626 }
00627
00628 static void container_destruct(void *_c)
00629 {
00630 struct ao2_container *c = _c;
00631 int i;
00632
00633 ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
00634
00635 for (i = 0; i < c->n_buckets; i++) {
00636 struct bucket_list *cur;
00637
00638 while ((cur = AST_LIST_REMOVE_HEAD(&c->buckets[i], entry))) {
00639 ast_free(cur);
00640 }
00641 }
00642
00643 #ifdef AO2_DEBUG
00644 ast_atomic_fetchadd_int(&ao2.total_containers, -1);
00645 #endif
00646 }
00647
00648 #ifdef AO2_DEBUG
00649 static int print_cb(void *obj, void *arg, int flag)
00650 {
00651 int *fd = arg;
00652 char *s = (char *)obj;
00653
00654 ast_cli(*fd, "string <%s>\n", s);
00655 return 0;
00656 }
00657
00658
00659
00660
00661 static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00662 {
00663 switch (cmd) {
00664 case CLI_INIT:
00665 e->command = "astobj2 stats";
00666 e->usage = "Usage: astobj2 stats\n"
00667 " Show astobj2 stats\n";
00668 return NULL;
00669 case CLI_GENERATE:
00670 return NULL;
00671 }
00672 ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
00673 ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
00674 ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
00675 ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
00676 ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
00677 return CLI_SUCCESS;
00678 }
00679
00680
00681
00682
00683 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00684 {
00685 struct ao2_container *c1;
00686 int i, lim;
00687 char *obj;
00688 static int prof_id = -1;
00689 struct ast_cli_args fake_args = { a->fd, 0, NULL };
00690
00691 switch (cmd) {
00692 case CLI_INIT:
00693 e->command = "astobj2 test";
00694 e->usage = "Usage: astobj2 test <num>\n"
00695 " Runs astobj2 test. Creates 'num' objects,\n"
00696 " and test iterators, callbacks and may be other stuff\n";
00697 return NULL;
00698 case CLI_GENERATE:
00699 return NULL;
00700 }
00701
00702 if (a->argc != 3) {
00703 return CLI_SHOWUSAGE;
00704 }
00705
00706 if (prof_id == -1)
00707 prof_id = ast_add_profile("ao2_alloc", 0);
00708
00709 ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
00710 lim = atoi(a->argv[2]);
00711 ast_cli(a->fd, "called astobj_test\n");
00712
00713 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
00714
00715
00716
00717
00718 c1 = ao2_container_alloc(100, NULL , NULL );
00719 ast_cli(a->fd, "container allocated as %p\n", c1);
00720
00721
00722
00723
00724
00725
00726 for (i = 0; i < lim; i++) {
00727 ast_mark(prof_id, 1 );
00728 obj = ao2_alloc(80, NULL);
00729 ast_mark(prof_id, 0 );
00730 ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
00731 sprintf(obj, "-- this is obj %d --", i);
00732 ao2_link(c1, obj);
00733 }
00734 ast_cli(a->fd, "testing callbacks\n");
00735 ao2_callback(c1, 0, print_cb, &a->fd);
00736
00737 ast_cli(a->fd, "testing iterators, remove every second object\n");
00738 {
00739 struct ao2_iterator ai;
00740 int x = 0;
00741
00742 ai = ao2_iterator_init(c1, 0);
00743 while ( (obj = ao2_iterator_next(&ai)) ) {
00744 ast_cli(a->fd, "iterator on <%s>\n", obj);
00745 if (x++ & 1)
00746 ao2_unlink(c1, obj);
00747 ao2_ref(obj, -1);
00748 }
00749 ast_cli(a->fd, "testing iterators again\n");
00750 ai = ao2_iterator_init(c1, 0);
00751 while ( (obj = ao2_iterator_next(&ai)) ) {
00752 ast_cli(a->fd, "iterator on <%s>\n", obj);
00753 ao2_ref(obj, -1);
00754 }
00755 }
00756 ast_cli(a->fd, "testing callbacks again\n");
00757 ao2_callback(c1, 0, print_cb, &a->fd);
00758
00759 ast_verbose("now you should see an error message:\n");
00760 ao2_ref(&i, -1);
00761
00762 ast_cli(a->fd, "destroy container\n");
00763 ao2_ref(c1, -1);
00764 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
00765 return CLI_SUCCESS;
00766 }
00767
00768 static struct ast_cli_entry cli_astobj2[] = {
00769 AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
00770 AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
00771 };
00772 #endif
00773
00774 int astobj2_init(void)
00775 {
00776 #ifdef AO2_DEBUG
00777 ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
00778 #endif
00779
00780 return 0;
00781 }