astobj2.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. /*
  2. * astobj2 - replacement containers for asterisk data structures.
  3. *
  4. * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
  5. *
  6. * See http://www.asterisk.org for more information about
  7. * the Asterisk project. Please do not directly contact
  8. * any of the maintainers of this project for assistance;
  9. * the project provides a web site, mailing lists and IRC
  10. * channels for your use.
  11. *
  12. * This program is free software, distributed under the terms of
  13. * the GNU General Public License Version 2. See the LICENSE file
  14. * at the top of the source tree.
  15. */
  16. /*! \file
  17. *
  18. * \brief Functions implementing astobj2 objects.
  19. *
  20. * \author Richard Mudgett <rmudgett@digium.com>
  21. */
  22. /*** MODULEINFO
  23. <support_level>core</support_level>
  24. ***/
  25. /* This reduces the size of lock structures within astobj2 objects when
  26. * DEBUG_THREADS is not defined. */
  27. #define DEBUG_THREADS_LOOSE_ABI
  28. #include "asterisk.h"
  29. #include "asterisk/_private.h"
  30. #include "asterisk/astobj2.h"
  31. #include "astobj2_private.h"
  32. #include "astobj2_container_private.h"
  33. #include "asterisk/cli.h"
  34. #include "asterisk/paths.h"
  35. /* Use ast_log_safe in place of ast_log. */
  36. #define ast_log ast_log_safe
  37. static FILE *ref_log;
  38. /*!
  39. * astobj2 objects are always preceded by this data structure,
  40. * which contains a reference counter,
  41. * option flags and a pointer to a destructor.
  42. * The refcount is used to decide when it is time to
  43. * invoke the destructor.
  44. * The magic number is used for consistency check.
  45. */
  46. struct __priv_data {
  47. ao2_destructor_fn destructor_fn;
  48. /*! This field is used for astobj2 and ao2_weakproxy objects to reference each other */
  49. void *weakptr;
  50. #if defined(AO2_DEBUG)
  51. /*! User data size for stats */
  52. size_t data_size;
  53. #endif
  54. /*! Number of references held for this object */
  55. int32_t ref_counter;
  56. /*!
  57. * \brief The ao2 object option flags.
  58. *
  59. * \note This field is constant after object creation. It shares
  60. * a uint32_t with \p lockused and \p magic.
  61. */
  62. uint32_t options:3;
  63. /*!
  64. * \brief Set to 1 when the lock is used if refdebug is enabled.
  65. *
  66. * \note This bit-field may be modified after object creation. It
  67. * shares a uint32_t with \ref options and \ref magic.
  68. */
  69. uint32_t lockused:1;
  70. /*!
  71. * \brief Magic number.
  72. *
  73. * This is used to verify that a pointer is a valid astobj2 or ao2_weak
  74. * reference.
  75. *
  76. * \note This field is constant after object creation. It shares
  77. * a uint32_t with \p options and \p lockused.
  78. *
  79. * \warning Stealing bits for any additional writable fields would cause
  80. * reentrancy issues if using bitfields. If any additional
  81. * writable bits are required in the future we will need to put
  82. * all bitfields into a single 'uint32_t flags' field and use
  83. * atomic operations from \file lock.h to perform writes.
  84. */
  85. uint32_t magic:28;
  86. };
  87. #define AO2_MAGIC 0xa70b123
  88. #define AO2_WEAK 0xa70b122
  89. #define IS_AO2_MAGIC_BAD(p) (AO2_MAGIC != (p->priv_data.magic | 1))
  90. /*!
  91. * What an astobj2 object looks like: fixed-size private data
  92. * followed by variable-size user data.
  93. */
  94. struct astobj2 {
  95. struct __priv_data priv_data;
  96. void *user_data[0];
  97. };
  98. struct ao2_weakproxy_notification {
  99. ao2_weakproxy_notification_cb cb;
  100. void *data;
  101. AST_LIST_ENTRY(ao2_weakproxy_notification) list;
  102. };
  103. struct ao2_lock_priv {
  104. ast_mutex_t lock;
  105. };
  106. /* AstObj2 with recursive lock. */
  107. struct astobj2_lock {
  108. struct ao2_lock_priv mutex;
  109. struct __priv_data priv_data;
  110. void *user_data[0];
  111. };
  112. struct ao2_rwlock_priv {
  113. ast_rwlock_t lock;
  114. /*! Count of the number of threads holding a lock on this object. -1 if it is the write lock. */
  115. int num_lockers;
  116. };
  117. /* AstObj2 with RW lock. */
  118. struct astobj2_rwlock {
  119. struct ao2_rwlock_priv rwlock;
  120. struct __priv_data priv_data;
  121. void *user_data[0];
  122. };
  123. struct ao2_lockobj_priv {
  124. void *lock;
  125. };
  126. /* AstObj2 with locking provided by a separate object. */
  127. struct astobj2_lockobj {
  128. struct ao2_lockobj_priv lockobj;
  129. struct __priv_data priv_data;
  130. void *user_data[0];
  131. };
  132. #ifdef AO2_DEBUG
  133. struct ao2_stats ao2;
  134. #endif
  135. #define INTERNAL_OBJ_MUTEX(user_data) \
  136. ((struct astobj2_lock *) (((char *) (user_data)) - sizeof(struct astobj2_lock)))
  137. #define INTERNAL_OBJ_RWLOCK(user_data) \
  138. ((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
  139. #define INTERNAL_OBJ_LOCKOBJ(user_data) \
  140. ((struct astobj2_lockobj *) (((char *) (user_data)) - sizeof(struct astobj2_lockobj)))
  141. #define INTERNAL_OBJ(user_data) \
  142. (struct astobj2 *) ((char *) user_data - sizeof(struct astobj2))
  143. /*!
  144. * \brief convert from a pointer _p to a user-defined object
  145. *
  146. * \return the pointer to the astobj2 structure
  147. */
  148. #define __INTERNAL_OBJ_CHECK(user_data, file, line, func) \
  149. ({ \
  150. struct astobj2 *p ## __LINE__; \
  151. if (!user_data \
  152. || !(p ## __LINE__ = INTERNAL_OBJ(user_data)) \
  153. || IS_AO2_MAGIC_BAD(p ## __LINE__)) { \
  154. log_bad_ao2(user_data, file, line, func); \
  155. p ## __LINE__ = NULL; \
  156. } \
  157. (p ## __LINE__); \
  158. })
  159. #define INTERNAL_OBJ_CHECK(user_data) \
  160. __INTERNAL_OBJ_CHECK(user_data, __FILE__, __LINE__, __PRETTY_FUNCTION__)
  161. /*!
  162. * \brief convert from a pointer _p to an astobj2 object
  163. *
  164. * \return the pointer to the user-defined portion.
  165. */
  166. #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
  167. int internal_is_ao2_object(void *user_data)
  168. {
  169. struct astobj2 *p;
  170. if (!user_data) {
  171. return 0;
  172. }
  173. p = INTERNAL_OBJ(user_data);
  174. return !p || IS_AO2_MAGIC_BAD(p) ? 0 : 1;
  175. }
  176. void log_bad_ao2(void *user_data, const char *file, int line, const char *func)
  177. {
  178. struct astobj2 *p;
  179. char bad_magic[100];
  180. if (!user_data) {
  181. __ast_assert_failed(0, "user_data is NULL", file, line, func);
  182. return;
  183. }
  184. p = INTERNAL_OBJ(user_data);
  185. snprintf(bad_magic, sizeof(bad_magic), "bad magic number 0x%x for object %p",
  186. p->priv_data.magic, user_data);
  187. __ast_assert_failed(0, bad_magic, file, line, func);
  188. }
  189. int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
  190. {
  191. struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
  192. struct astobj2_lock *obj_mutex;
  193. struct astobj2_rwlock *obj_rwlock;
  194. struct astobj2_lockobj *obj_lockobj;
  195. int res = 0;
  196. if (obj == NULL) {
  197. return -1;
  198. }
  199. if (ref_log) {
  200. obj->priv_data.lockused = 1;
  201. }
  202. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  203. case AO2_ALLOC_OPT_LOCK_MUTEX:
  204. obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
  205. res = __ast_pthread_mutex_lock(file, line, func, var, &obj_mutex->mutex.lock);
  206. #ifdef AO2_DEBUG
  207. if (!res) {
  208. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  209. }
  210. #endif
  211. break;
  212. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  213. obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
  214. switch (lock_how) {
  215. case AO2_LOCK_REQ_MUTEX:
  216. case AO2_LOCK_REQ_WRLOCK:
  217. res = __ast_rwlock_wrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
  218. if (!res) {
  219. ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
  220. #ifdef AO2_DEBUG
  221. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  222. #endif
  223. }
  224. break;
  225. case AO2_LOCK_REQ_RDLOCK:
  226. res = __ast_rwlock_rdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
  227. if (!res) {
  228. ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
  229. #ifdef AO2_DEBUG
  230. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  231. #endif
  232. }
  233. break;
  234. }
  235. break;
  236. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  237. /* The ao2 object has no lock. */
  238. break;
  239. case AO2_ALLOC_OPT_LOCK_OBJ:
  240. obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
  241. res = __ao2_lock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
  242. break;
  243. default:
  244. ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
  245. user_data);
  246. return -1;
  247. }
  248. return res;
  249. }
  250. int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
  251. {
  252. struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
  253. struct astobj2_lock *obj_mutex;
  254. struct astobj2_rwlock *obj_rwlock;
  255. struct astobj2_lockobj *obj_lockobj;
  256. int res = 0;
  257. int current_value;
  258. if (obj == NULL) {
  259. return -1;
  260. }
  261. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  262. case AO2_ALLOC_OPT_LOCK_MUTEX:
  263. obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
  264. res = __ast_pthread_mutex_unlock(file, line, func, var, &obj_mutex->mutex.lock);
  265. #ifdef AO2_DEBUG
  266. if (!res) {
  267. ast_atomic_fetchadd_int(&ao2.total_locked, -1);
  268. }
  269. #endif
  270. break;
  271. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  272. obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
  273. current_value = ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1) - 1;
  274. if (current_value < 0) {
  275. /* It was a WRLOCK that we are unlocking. Fix the count. */
  276. ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -current_value);
  277. }
  278. res = __ast_rwlock_unlock(file, line, func, &obj_rwlock->rwlock.lock, var);
  279. #ifdef AO2_DEBUG
  280. if (!res) {
  281. ast_atomic_fetchadd_int(&ao2.total_locked, -1);
  282. }
  283. #endif
  284. break;
  285. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  286. /* The ao2 object has no lock. */
  287. break;
  288. case AO2_ALLOC_OPT_LOCK_OBJ:
  289. obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
  290. res = __ao2_unlock(obj_lockobj->lockobj.lock, file, func, line, var);
  291. break;
  292. default:
  293. ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
  294. user_data);
  295. res = -1;
  296. break;
  297. }
  298. return res;
  299. }
  300. int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
  301. {
  302. struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
  303. struct astobj2_lock *obj_mutex;
  304. struct astobj2_rwlock *obj_rwlock;
  305. struct astobj2_lockobj *obj_lockobj;
  306. int res = 0;
  307. if (obj == NULL) {
  308. return -1;
  309. }
  310. if (ref_log) {
  311. obj->priv_data.lockused = 1;
  312. }
  313. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  314. case AO2_ALLOC_OPT_LOCK_MUTEX:
  315. obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
  316. res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock);
  317. #ifdef AO2_DEBUG
  318. if (!res) {
  319. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  320. }
  321. #endif
  322. break;
  323. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  324. obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
  325. switch (lock_how) {
  326. case AO2_LOCK_REQ_MUTEX:
  327. case AO2_LOCK_REQ_WRLOCK:
  328. res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
  329. if (!res) {
  330. ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
  331. #ifdef AO2_DEBUG
  332. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  333. #endif
  334. }
  335. break;
  336. case AO2_LOCK_REQ_RDLOCK:
  337. res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
  338. if (!res) {
  339. ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
  340. #ifdef AO2_DEBUG
  341. ast_atomic_fetchadd_int(&ao2.total_locked, 1);
  342. #endif
  343. }
  344. break;
  345. }
  346. break;
  347. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  348. /* The ao2 object has no lock. */
  349. return 0;
  350. case AO2_ALLOC_OPT_LOCK_OBJ:
  351. obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
  352. res = __ao2_trylock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
  353. break;
  354. default:
  355. ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
  356. user_data);
  357. return -1;
  358. }
  359. return res;
  360. }
  361. /*!
  362. * \internal
  363. * \brief Adjust an object's lock to the requested level.
  364. *
  365. * \param user_data An ao2 object to adjust lock level.
  366. * \param lock_how What level to adjust lock.
  367. * \param keep_stronger TRUE if keep original lock level if it is stronger.
  368. *
  369. * \pre The ao2 object is already locked.
  370. *
  371. * \details
  372. * An ao2 object with a RWLOCK will have its lock level adjusted
  373. * to the specified level if it is not already there. An ao2
  374. * object with a different type of lock is not affected.
  375. *
  376. * \return Original lock level.
  377. */
  378. enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
  379. {
  380. struct astobj2 *obj = INTERNAL_OBJ(user_data);
  381. struct astobj2_rwlock *obj_rwlock;
  382. struct astobj2_lockobj *obj_lockobj;
  383. enum ao2_lock_req orig_lock;
  384. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  385. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  386. obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
  387. if (obj_rwlock->rwlock.num_lockers < 0) {
  388. orig_lock = AO2_LOCK_REQ_WRLOCK;
  389. } else {
  390. orig_lock = AO2_LOCK_REQ_RDLOCK;
  391. }
  392. switch (lock_how) {
  393. case AO2_LOCK_REQ_MUTEX:
  394. lock_how = AO2_LOCK_REQ_WRLOCK;
  395. /* Fall through */
  396. case AO2_LOCK_REQ_WRLOCK:
  397. if (lock_how != orig_lock) {
  398. /* Switch from read lock to write lock. */
  399. ao2_unlock(user_data);
  400. ao2_wrlock(user_data);
  401. }
  402. break;
  403. case AO2_LOCK_REQ_RDLOCK:
  404. if (!keep_stronger && lock_how != orig_lock) {
  405. /* Switch from write lock to read lock. */
  406. ao2_unlock(user_data);
  407. ao2_rdlock(user_data);
  408. }
  409. break;
  410. }
  411. break;
  412. case AO2_ALLOC_OPT_LOCK_OBJ:
  413. obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
  414. orig_lock = __adjust_lock(obj_lockobj->lockobj.lock, lock_how, keep_stronger);
  415. break;
  416. default:
  417. ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
  418. /* Fall through */
  419. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  420. case AO2_ALLOC_OPT_LOCK_MUTEX:
  421. orig_lock = AO2_LOCK_REQ_MUTEX;
  422. break;
  423. }
  424. return orig_lock;
  425. }
  426. void *ao2_object_get_lockaddr(void *user_data)
  427. {
  428. struct astobj2 *obj;
  429. struct astobj2_lock *obj_mutex;
  430. obj = INTERNAL_OBJ_CHECK(user_data);
  431. if (obj == NULL) {
  432. return NULL;
  433. }
  434. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  435. case AO2_ALLOC_OPT_LOCK_MUTEX:
  436. obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
  437. return &obj_mutex->mutex.lock;
  438. default:
  439. break;
  440. }
  441. return NULL;
  442. }
  443. int __ao2_ref(void *user_data, int delta,
  444. const char *tag, const char *file, int line, const char *func)
  445. {
  446. struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
  447. struct astobj2_lock *obj_mutex;
  448. struct astobj2_rwlock *obj_rwlock;
  449. struct astobj2_lockobj *obj_lockobj;
  450. int32_t current_value;
  451. int32_t ret;
  452. uint32_t privdataoptions;
  453. struct ao2_weakproxy *weakproxy = NULL;
  454. const char *lock_state;
  455. if (obj == NULL) {
  456. if (ref_log && user_data) {
  457. fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%s\n",
  458. user_data, delta, ast_get_tid(), file, line, func, tag ?: "");
  459. fflush(ref_log);
  460. }
  461. return -1;
  462. }
  463. /* if delta is 0, just return the refcount */
  464. if (delta == 0) {
  465. return obj->priv_data.ref_counter;
  466. }
  467. if (delta < 0 && obj->priv_data.magic == AO2_MAGIC && (weakproxy = obj->priv_data.weakptr)) {
  468. ao2_lock(weakproxy);
  469. }
  470. /* we modify with an atomic operation the reference counter */
  471. ret = ast_atomic_fetch_add(&obj->priv_data.ref_counter, delta, __ATOMIC_RELAXED);
  472. current_value = ret + delta;
  473. #ifdef AO2_DEBUG
  474. ast_atomic_fetchadd_int(&ao2.total_refs, delta);
  475. #endif
  476. if (weakproxy) {
  477. struct ao2_weakproxy cbs;
  478. if (current_value == 1) {
  479. /* The only remaining reference is the one owned by the weak object */
  480. struct astobj2 *internal_weakproxy;
  481. internal_weakproxy = INTERNAL_OBJ_CHECK(weakproxy);
  482. /* Unlink the obj from the weak proxy */
  483. internal_weakproxy->priv_data.weakptr = NULL;
  484. obj->priv_data.weakptr = NULL;
  485. /* transfer list to local copy so callbacks are run with weakproxy unlocked. */
  486. cbs.destroyed_cb = weakproxy->destroyed_cb;
  487. AST_LIST_HEAD_INIT_NOLOCK(&weakproxy->destroyed_cb);
  488. /* weak is already unlinked from obj so this won't recurse */
  489. ao2_ref(user_data, -1);
  490. }
  491. ao2_unlock(weakproxy);
  492. if (current_value == 1) {
  493. struct ao2_weakproxy_notification *destroyed_cb;
  494. /* Notify the subscribers that weakproxy now points to NULL. */
  495. while ((destroyed_cb = AST_LIST_REMOVE_HEAD(&cbs.destroyed_cb, list))) {
  496. destroyed_cb->cb(weakproxy, destroyed_cb->data);
  497. ast_free(destroyed_cb);
  498. }
  499. ao2_ref(weakproxy, -1);
  500. }
  501. }
  502. if (0 < current_value) {
  503. /* The object still lives. */
  504. #define EXCESSIVE_REF_COUNT 100000
  505. if (EXCESSIVE_REF_COUNT <= current_value && ret < EXCESSIVE_REF_COUNT) {
  506. char excessive_ref_buf[100];
  507. /* We just reached or went over the excessive ref count trigger */
  508. snprintf(excessive_ref_buf, sizeof(excessive_ref_buf),
  509. "Excessive refcount %d reached on ao2 object %p",
  510. (int)current_value, user_data);
  511. ast_log(__LOG_ERROR, file, line, func, "%s\n", excessive_ref_buf);
  512. __ast_assert_failed(0, excessive_ref_buf, file, line, func);
  513. }
  514. if (ref_log && !(obj->priv_data.options & AO2_ALLOC_OPT_NO_REF_DEBUG)) {
  515. fprintf(ref_log, "%p,%s%d,%d,%s,%d,%s,%d,%s\n", user_data,
  516. (delta < 0 ? "" : "+"), delta, ast_get_tid(),
  517. file, line, func, (int)ret, tag ?: "");
  518. fflush(ref_log);
  519. }
  520. return ret;
  521. }
  522. /* this case must never happen */
  523. if (current_value < 0) {
  524. ast_log(__LOG_ERROR, file, line, func,
  525. "Invalid refcount %d on ao2 object %p\n", (int)current_value, user_data);
  526. if (ref_log) {
  527. /* Log to ref_log even if AO2_ALLOC_OPT_NO_REF_DEBUG */
  528. fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%s\n",
  529. user_data, delta, ast_get_tid(), file, line, func, tag ?: "");
  530. fflush(ref_log);
  531. }
  532. ast_assert(0);
  533. /* stop here even if assert doesn't DO_CRASH */
  534. return -1;
  535. }
  536. /* last reference, destroy the object */
  537. if (obj->priv_data.destructor_fn != NULL) {
  538. obj->priv_data.destructor_fn(user_data);
  539. }
  540. #ifdef AO2_DEBUG
  541. ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
  542. ast_atomic_fetchadd_int(&ao2.total_objects, -1);
  543. #endif
  544. /* In case someone uses an object after it's been freed */
  545. obj->priv_data.magic = 0;
  546. /* Save the options locally so the ref_log print at the end doesn't access freed data */
  547. privdataoptions = obj->priv_data.options;
  548. switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
  549. case AO2_ALLOC_OPT_LOCK_MUTEX:
  550. obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
  551. lock_state = obj->priv_data.lockused ? "used" : "unused";
  552. ast_mutex_destroy(&obj_mutex->mutex.lock);
  553. ast_free(obj_mutex);
  554. break;
  555. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  556. obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
  557. lock_state = obj->priv_data.lockused ? "used" : "unused";
  558. ast_rwlock_destroy(&obj_rwlock->rwlock.lock);
  559. ast_free(obj_rwlock);
  560. break;
  561. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  562. lock_state = "none";
  563. ast_free(obj);
  564. break;
  565. case AO2_ALLOC_OPT_LOCK_OBJ:
  566. obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
  567. lock_state = obj->priv_data.lockused ? "used" : "unused";
  568. ao2_t_ref(obj_lockobj->lockobj.lock, -1, "release lockobj");
  569. ast_free(obj_lockobj);
  570. break;
  571. default:
  572. ast_log(__LOG_ERROR, file, line, func,
  573. "Invalid lock option on ao2 object %p\n", user_data);
  574. lock_state = "invalid";
  575. break;
  576. }
  577. if (ref_log && !(privdataoptions & AO2_ALLOC_OPT_NO_REF_DEBUG)) {
  578. fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**destructor**lock-state:%s**,%s\n",
  579. user_data, delta, ast_get_tid(), file, line, func, lock_state, tag ?: "");
  580. fflush(ref_log);
  581. }
  582. return ret;
  583. }
  584. void __ao2_cleanup_debug(void *obj, const char *tag, const char *file, int line, const char *function)
  585. {
  586. if (obj) {
  587. __ao2_ref(obj, -1, tag, file, line, function);
  588. }
  589. }
  590. void __ao2_cleanup(void *obj)
  591. {
  592. if (obj) {
  593. ao2_ref(obj, -1);
  594. }
  595. }
  596. static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
  597. void *lockobj, const char *tag, const char *file, int line, const char *func)
  598. {
  599. /* allocation */
  600. struct astobj2 *obj;
  601. struct astobj2_lock *obj_mutex;
  602. struct astobj2_rwlock *obj_rwlock;
  603. struct astobj2_lockobj *obj_lockobj;
  604. size_t overhead;
  605. switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
  606. case AO2_ALLOC_OPT_LOCK_MUTEX:
  607. overhead = sizeof(*obj_mutex);
  608. obj_mutex = __ast_calloc(1, overhead + data_size, file, line, func);
  609. if (obj_mutex == NULL) {
  610. return NULL;
  611. }
  612. ast_mutex_init(&obj_mutex->mutex.lock);
  613. obj = (struct astobj2 *) &obj_mutex->priv_data;
  614. break;
  615. case AO2_ALLOC_OPT_LOCK_RWLOCK:
  616. overhead = sizeof(*obj_rwlock);
  617. obj_rwlock = __ast_calloc(1, overhead + data_size, file, line, func);
  618. if (obj_rwlock == NULL) {
  619. return NULL;
  620. }
  621. ast_rwlock_init(&obj_rwlock->rwlock.lock);
  622. obj = (struct astobj2 *) &obj_rwlock->priv_data;
  623. break;
  624. case AO2_ALLOC_OPT_LOCK_NOLOCK:
  625. overhead = sizeof(*obj);
  626. obj = __ast_calloc(1, overhead + data_size, file, line, func);
  627. if (obj == NULL) {
  628. return NULL;
  629. }
  630. break;
  631. case AO2_ALLOC_OPT_LOCK_OBJ:
  632. lockobj = ao2_t_bump(lockobj, "set lockobj");
  633. if (!lockobj) {
  634. ast_log(__LOG_ERROR, file, line, func, "AO2_ALLOC_OPT_LOCK_OBJ requires a non-NULL lockobj.\n");
  635. return NULL;
  636. }
  637. overhead = sizeof(*obj_lockobj);
  638. obj_lockobj = __ast_calloc(1, overhead + data_size, file, line, func);
  639. if (obj_lockobj == NULL) {
  640. ao2_t_ref(lockobj, -1, "release lockobj for failed alloc");
  641. return NULL;
  642. }
  643. obj_lockobj->lockobj.lock = lockobj;
  644. obj = (struct astobj2 *) &obj_lockobj->priv_data;
  645. break;
  646. default:
  647. /* Invalid option value. */
  648. ast_log(__LOG_DEBUG, file, line, func, "Invalid lock option requested\n");
  649. return NULL;
  650. }
  651. /* Initialize common ao2 values. */
  652. obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
  653. obj->priv_data.ref_counter = 1;
  654. obj->priv_data.options = options;
  655. obj->priv_data.magic = AO2_MAGIC;
  656. #ifdef AO2_DEBUG
  657. obj->priv_data.data_size = data_size;
  658. ast_atomic_fetchadd_int(&ao2.total_objects, 1);
  659. ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
  660. ast_atomic_fetchadd_int(&ao2.total_refs, 1);
  661. #endif
  662. if (ref_log && !(obj->priv_data.options & AO2_ALLOC_OPT_NO_REF_DEBUG)) {
  663. fprintf(ref_log, "%p,+1,%d,%s,%d,%s,**constructor**%zu**%zu**,%s\n",
  664. EXTERNAL_OBJ(obj), ast_get_tid(), file, line, func, overhead, data_size, tag ?: "");
  665. fflush(ref_log);
  666. }
  667. /* return a pointer to the user data */
  668. return EXTERNAL_OBJ(obj);
  669. }
  670. void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
  671. const char *tag, const char *file, int line, const char *func)
  672. {
  673. return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func);
  674. }
  675. void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj,
  676. const char *tag, const char *file, int line, const char *func)
  677. {
  678. return internal_ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_OBJ, lockobj,
  679. tag, file, line, func);
  680. }
  681. unsigned int ao2_options_get(void *obj)
  682. {
  683. struct astobj2 *orig_obj;
  684. orig_obj = INTERNAL_OBJ_CHECK(obj);
  685. if (!orig_obj) {
  686. return 0;
  687. }
  688. return orig_obj->priv_data.options;
  689. }
  690. void *__ao2_weakproxy_alloc(size_t data_size, ao2_destructor_fn destructor_fn,
  691. const char *tag, const char *file, int line, const char *func)
  692. {
  693. struct ao2_weakproxy *weakproxy;
  694. if (data_size < sizeof(*weakproxy)) {
  695. ast_assert(0);
  696. ast_log(LOG_ERROR, "Requested data_size smaller than minimum.\n");
  697. return NULL;
  698. }
  699. weakproxy = __ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_MUTEX,
  700. tag, file, line, func);
  701. if (weakproxy) {
  702. struct astobj2 *weakproxy_internal;
  703. /* Just created weakproxy, no need to check if it's valid. */
  704. weakproxy_internal = INTERNAL_OBJ(weakproxy);
  705. weakproxy_internal->priv_data.magic = AO2_WEAK;
  706. }
  707. return weakproxy;
  708. }
  709. int __ao2_weakproxy_set_object(void *weakproxy, void *obj, int flags,
  710. const char *tag, const char *file, int line, const char *func)
  711. {
  712. struct astobj2 *weakproxy_internal = __INTERNAL_OBJ_CHECK(weakproxy, file, line, func);
  713. struct astobj2 *obj_internal = __INTERNAL_OBJ_CHECK(obj, file, line, func);
  714. int ret = -1;
  715. if (!weakproxy_internal
  716. || weakproxy_internal->priv_data.magic != AO2_WEAK) {
  717. return -1;
  718. }
  719. if (!obj_internal
  720. || obj_internal->priv_data.weakptr
  721. || obj_internal->priv_data.magic != AO2_MAGIC) {
  722. return -1;
  723. }
  724. if (!(flags & OBJ_NOLOCK)) {
  725. ao2_lock(weakproxy);
  726. }
  727. if (!weakproxy_internal->priv_data.weakptr) {
  728. __ao2_ref(obj, +1, tag, file, line, func);
  729. __ao2_ref(weakproxy, +1, tag, file, line, func);
  730. weakproxy_internal->priv_data.weakptr = obj;
  731. obj_internal->priv_data.weakptr = weakproxy;
  732. ret = 0;
  733. }
  734. if (!(flags & OBJ_NOLOCK)) {
  735. ao2_unlock(weakproxy);
  736. /* It is possible for obj to be accessed now. It's allowed
  737. * for weakproxy to already be in a container. Another thread
  738. * could have been waiting for a lock on weakproxy to retrieve
  739. * the object.
  740. */
  741. }
  742. return ret;
  743. }
  744. int __ao2_weakproxy_ref_object(void *weakproxy, int delta, int flags,
  745. const char *tag, const char *file, int line, const char *func)
  746. {
  747. struct astobj2 *internal = __INTERNAL_OBJ_CHECK(weakproxy, file, line, func);
  748. int ret = -1;
  749. if (!internal || internal->priv_data.magic != AO2_WEAK) {
  750. /* This method is meant to be run on weakproxy objects! */
  751. return -2;
  752. }
  753. /* We have a weak object, grab lock. */
  754. if (!(flags & OBJ_NOLOCK)) {
  755. ao2_lock(weakproxy);
  756. }
  757. if (internal->priv_data.weakptr) {
  758. ret = __ao2_ref(internal->priv_data.weakptr, delta, tag, file, line, func);
  759. }
  760. if (!(flags & OBJ_NOLOCK)) {
  761. ao2_unlock(weakproxy);
  762. }
  763. return ret;
  764. }
  765. void *__ao2_weakproxy_get_object(void *weakproxy, int flags,
  766. const char *tag, const char *file, int line, const char *func)
  767. {
  768. struct astobj2 *internal = __INTERNAL_OBJ_CHECK(weakproxy, file, line, func);
  769. void *obj;
  770. if (!internal || internal->priv_data.magic != AO2_WEAK) {
  771. /* This method is meant to be run on weakproxy objects! */
  772. return NULL;
  773. }
  774. /* We have a weak object, grab reference to object within lock */
  775. if (!(flags & OBJ_NOLOCK)) {
  776. ao2_lock(weakproxy);
  777. }
  778. obj = internal->priv_data.weakptr;
  779. if (obj) {
  780. __ao2_ref(obj, +1, tag, file, line, func);
  781. }
  782. if (!(flags & OBJ_NOLOCK)) {
  783. ao2_unlock(weakproxy);
  784. }
  785. return obj;
  786. }
  787. void *__ao2_get_weakproxy(void *obj, const char *tag, const char *file, int line, const char *func)
  788. {
  789. struct astobj2 *obj_internal = __INTERNAL_OBJ_CHECK(obj, file, line, func);
  790. if (!obj_internal || obj_internal->priv_data.magic != AO2_MAGIC) {
  791. /* This method is meant to be run on normal ao2 objects! */
  792. return NULL;
  793. }
  794. if (!obj_internal->priv_data.weakptr) {
  795. return NULL;
  796. }
  797. __ao2_ref(obj_internal->priv_data.weakptr, +1, tag, file, line, func);
  798. return obj_internal->priv_data.weakptr;
  799. }
  800. int ao2_weakproxy_subscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, void *data, int flags)
  801. {
  802. struct astobj2 *weakproxy_internal = INTERNAL_OBJ_CHECK(weakproxy);
  803. int ret = -1;
  804. int hasobj;
  805. if (!weakproxy_internal || weakproxy_internal->priv_data.magic != AO2_WEAK) {
  806. return -1;
  807. }
  808. if (!(flags & OBJ_NOLOCK)) {
  809. ao2_lock(weakproxy);
  810. }
  811. hasobj = weakproxy_internal->priv_data.weakptr != NULL;
  812. if (hasobj) {
  813. struct ao2_weakproxy *weak = weakproxy;
  814. struct ao2_weakproxy_notification *sub = ast_calloc(1, sizeof(*sub));
  815. if (sub) {
  816. sub->cb = cb;
  817. sub->data = data;
  818. AST_LIST_INSERT_HEAD(&weak->destroyed_cb, sub, list);
  819. ret = 0;
  820. }
  821. }
  822. if (!(flags & OBJ_NOLOCK)) {
  823. ao2_unlock(weakproxy);
  824. }
  825. if (!hasobj) {
  826. cb(weakproxy, data);
  827. ret = 0;
  828. }
  829. return ret;
  830. }
  831. int ao2_weakproxy_unsubscribe(void *weakproxy, ao2_weakproxy_notification_cb destroyed_cb, void *data, int flags)
  832. {
  833. struct astobj2 *internal_weakproxy = INTERNAL_OBJ_CHECK(weakproxy);
  834. struct ao2_weakproxy *weak;
  835. struct ao2_weakproxy_notification *sub;
  836. int ret = 0;
  837. if (!internal_weakproxy || internal_weakproxy->priv_data.magic != AO2_WEAK || !destroyed_cb) {
  838. return -1;
  839. }
  840. if (!(flags & OBJ_NOLOCK)) {
  841. ao2_lock(weakproxy);
  842. }
  843. weak = weakproxy;
  844. AST_LIST_TRAVERSE_SAFE_BEGIN(&weak->destroyed_cb, sub, list) {
  845. if (sub->cb == destroyed_cb && sub->data == data) {
  846. AST_LIST_REMOVE_CURRENT(list);
  847. ast_free(sub);
  848. ret++;
  849. if (!(flags & OBJ_MULTIPLE)) {
  850. break;
  851. }
  852. }
  853. }
  854. AST_LIST_TRAVERSE_SAFE_END;
  855. if (!(flags & OBJ_NOLOCK)) {
  856. ao2_unlock(weakproxy);
  857. }
  858. return ret;
  859. }
  860. #ifdef AO2_DEBUG
  861. static int print_cb(void *obj, void *arg, int flag)
  862. {
  863. struct ast_cli_args *a = (struct ast_cli_args *) arg;
  864. char *s = (char *)obj;
  865. ast_cli(a->fd, "string <%s>\n", s);
  866. return 0;
  867. }
  868. /*
  869. * Print stats
  870. */
  871. static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  872. {
  873. switch (cmd) {
  874. case CLI_INIT:
  875. e->command = "astobj2 show stats";
  876. e->usage = "Usage: astobj2 show stats\n"
  877. " Show astobj2 show stats\n";
  878. return NULL;
  879. case CLI_GENERATE:
  880. return NULL;
  881. }
  882. ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
  883. ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
  884. ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
  885. ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
  886. ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
  887. return CLI_SUCCESS;
  888. }
  889. /*
  890. * This is testing code for astobj
  891. */
  892. static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  893. {
  894. struct ao2_container *c1;
  895. struct ao2_container *c2;
  896. int i, lim;
  897. char *obj;
  898. static int prof_id = -1;
  899. struct ast_cli_args fake_args = { a->fd, 0, NULL };
  900. switch (cmd) {
  901. case CLI_INIT:
  902. e->command = "astobj2 test";
  903. e->usage = "Usage: astobj2 test <num>\n"
  904. " Runs astobj2 test. Creates 'num' objects,\n"
  905. " and test iterators, callbacks and maybe other stuff\n";
  906. return NULL;
  907. case CLI_GENERATE:
  908. return NULL;
  909. }
  910. if (a->argc != 3) {
  911. return CLI_SHOWUSAGE;
  912. }
  913. if (prof_id == -1) {
  914. prof_id = ast_add_profile("ao2_alloc", 0);
  915. }
  916. ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
  917. lim = atoi(a->argv[2]);
  918. ast_cli(a->fd, "called astobj_test\n");
  919. handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
  920. /*
  921. * Allocate a list container.
  922. */
  923. c1 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL /* no sort */,
  924. NULL /* no callback */, "test");
  925. ast_cli(a->fd, "container allocated as %p\n", c1);
  926. /*
  927. * fill the container with objects.
  928. * ao2_alloc() gives us a reference which we pass to the
  929. * container when we do the insert.
  930. */
  931. for (i = 0; i < lim; i++) {
  932. ast_mark(prof_id, 1 /* start */);
  933. obj = ao2_t_alloc(80, NULL,"test");
  934. ast_mark(prof_id, 0 /* stop */);
  935. ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
  936. sprintf(obj, "-- this is obj %d --", i);
  937. ao2_link(c1, obj);
  938. /* At this point, the refcount on obj is 2 due to the allocation
  939. * and linking. We can go ahead and reduce the refcount by 1
  940. * right here so that when the container is unreffed later, the
  941. * objects will be freed
  942. */
  943. ao2_t_ref(obj, -1, "test");
  944. }
  945. ast_cli(a->fd, "testing callbacks\n");
  946. ao2_t_callback(c1, 0, print_cb, a, "test callback");
  947. ast_cli(a->fd, "testing container cloning\n");
  948. c2 = ao2_container_clone(c1, 0);
  949. if (ao2_container_count(c1) != ao2_container_count(c2)) {
  950. ast_cli(a->fd, "Cloned container does not have the same number of objects!\n");
  951. }
  952. ao2_t_callback(c2, 0, print_cb, a, "test callback");
  953. ast_cli(a->fd, "testing iterators, remove every second object\n");
  954. {
  955. struct ao2_iterator ai;
  956. int x = 0;
  957. ai = ao2_iterator_init(c1, 0);
  958. while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
  959. ast_cli(a->fd, "iterator on <%s>\n", obj);
  960. if (x++ & 1)
  961. ao2_t_unlink(c1, obj,"test");
  962. ao2_t_ref(obj, -1,"test");
  963. }
  964. ao2_iterator_destroy(&ai);
  965. ast_cli(a->fd, "testing iterators again\n");
  966. ai = ao2_iterator_init(c1, 0);
  967. while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
  968. ast_cli(a->fd, "iterator on <%s>\n", obj);
  969. ao2_t_ref(obj, -1,"test");
  970. }
  971. ao2_iterator_destroy(&ai);
  972. }
  973. ast_cli(a->fd, "testing callbacks again\n");
  974. ao2_t_callback(c1, 0, print_cb, a, "test callback");
  975. ast_verbose("now you should see an error and possible assertion failure messages:\n");
  976. ao2_t_ref(&i, -1, ""); /* i is not a valid object so we print an error here */
  977. ast_cli(a->fd, "destroy container\n");
  978. ao2_t_ref(c1, -1, ""); /* destroy container */
  979. ao2_t_ref(c2, -1, ""); /* destroy container */
  980. handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
  981. return CLI_SUCCESS;
  982. }
  983. #endif /* AO2_DEBUG */
  984. #if defined(AO2_DEBUG)
  985. static struct ast_cli_entry cli_astobj2[] = {
  986. AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
  987. AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
  988. };
  989. #endif /* AO2_DEBUG */
  990. static void astobj2_cleanup(void)
  991. {
  992. #if defined(AO2_DEBUG)
  993. ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
  994. #endif
  995. if (ast_opt_ref_debug) {
  996. fclose(ref_log);
  997. ref_log = NULL;
  998. }
  999. }
  1000. int astobj2_init(void)
  1001. {
  1002. char ref_filename[1024];
  1003. if (ast_opt_ref_debug) {
  1004. snprintf(ref_filename, sizeof(ref_filename), "%s/refs", ast_config_AST_LOG_DIR);
  1005. ref_log = fopen(ref_filename, "w");
  1006. if (!ref_log) {
  1007. ast_log(LOG_ERROR, "Could not open ref debug log file: %s\n", ref_filename);
  1008. }
  1009. }
  1010. ast_register_cleanup(astobj2_cleanup);
  1011. if (container_init() != 0) {
  1012. fclose(ref_log);
  1013. ref_log = NULL;
  1014. return -1;
  1015. }
  1016. #if defined(AO2_DEBUG)
  1017. ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
  1018. #endif /* defined(AO2_DEBUG) */
  1019. return 0;
  1020. }