Asterisk - The Open Source Telephony Project  18.5.0
astobj2_container.c
Go to the documentation of this file.
1 /* astobj2 - replacement containers for asterisk data structures.
2  *
3  * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
4  *
5  * See http://www.asterisk.org for more information about
6  * the Asterisk project. Please do not directly contact
7  * any of the maintainers of this project for assistance;
8  * the project provides a web site, mailing lists and IRC
9  * channels for your use.
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License Version 2. See the LICENSE file
13  * at the top of the source tree.
14  */
15 
16 /*! \file
17  *
18  * \brief Functions implementing astobj2 objects.
19  *
20  * \author Richard Mudgett <[email protected]>
21  */
22 
23 #include "asterisk.h"
24 
25 #include "asterisk/_private.h"
26 #include "asterisk/astobj2.h"
27 #include "astobj2_private.h"
29 #include "asterisk/cli.h"
30 
31 /*!
32  * return the number of elements in the container
33  */
35 {
36  return ast_atomic_fetchadd_int(&c->elements, 0);
37 }
38 
40  const char *tag, const char *file, int line, const char *func)
41 {
42  struct ao2_container *container = node->my_container;
43 
44  if (container == NULL && (flags & AO2_UNLINK_NODE_DEC_COUNT)) {
45  return 0;
46  }
47 
48  if ((flags & AO2_UNLINK_NODE_UNLINK_OBJECT)
49  && !(flags & AO2_UNLINK_NODE_NOUNREF_OBJECT)) {
50  __ao2_ref(node->obj, -1, tag ?: "Remove obj from container", file, line, func);
51  }
52 
53  node->obj = NULL;
54 
55  if (flags & AO2_UNLINK_NODE_DEC_COUNT) {
56  ast_atomic_fetchadd_int(&container->elements, -1);
57 #if defined(AO2_DEBUG)
58  {
59  int empty = container->nodes - container->elements;
60 
61  if (container->max_empty_nodes < empty) {
62  container->max_empty_nodes = empty;
63  }
64  if (container->v_table->unlink_stat) {
65  container->v_table->unlink_stat(container, node);
66  }
67  }
68 #endif /* defined(AO2_DEBUG) */
69  }
70 
71  if (flags & AO2_UNLINK_NODE_UNREF_NODE) {
72  /* Remove node from container */
73  ao2_ref(node, -1);
74  }
75 
76  return 1;
77 }
78 
79 /*!
80  * \internal
81  * \brief Link an object into this container. (internal)
82  *
83  * \param self Container to operate upon.
84  * \param obj_new Object to insert into the container.
85  * \param flags search_flags to control linking the object. (OBJ_NOLOCK)
86  * \param tag used for debugging.
87  * \param file Debug file name invoked from
88  * \param line Debug line invoked from
89  * \param func Debug function name invoked from
90  *
91  * \retval 0 on errors.
92  * \retval 1 on success.
93  */
94 int __ao2_link(struct ao2_container *self, void *obj_new, int flags,
95  const char *tag, const char *file, int line, const char *func)
96 {
97  int res;
98  enum ao2_lock_req orig_lock;
99  struct ao2_container_node *node;
100 
101  if (!__is_ao2_object(obj_new, file, line, func)
102  || !__is_ao2_object(self, file, line, func)) {
103  return 0;
104  }
105 
106  if (!self->v_table || !self->v_table->new_node || !self->v_table->insert) {
107  /* Sanity checks. */
108  __ast_assert_failed(0, "invalid container v_table", file, line, func);
109  return 0;
110  }
111 
112  if (flags & OBJ_NOLOCK) {
113  orig_lock = __adjust_lock(self, AO2_LOCK_REQ_WRLOCK, 1);
114  } else {
115  ao2_wrlock(self);
116  orig_lock = AO2_LOCK_REQ_MUTEX;
117  }
118 
119  res = 0;
120  node = self->v_table->new_node(self, obj_new, tag, file, line, func);
121  if (node) {
122 #if defined(AO2_DEBUG)
123  if (ao2_container_check(self, OBJ_NOLOCK)) {
124  ast_log(LOG_ERROR, "Container integrity failed before insert.\n");
125  }
126 #endif /* defined(AO2_DEBUG) */
127 
128  /* Insert the new node. */
129  switch (self->v_table->insert(self, node)) {
131  node->is_linked = 1;
132  ast_atomic_fetchadd_int(&self->elements, 1);
133 #if defined(AO2_DEBUG)
134  AO2_DEVMODE_STAT(++self->nodes);
135  if (self->v_table->link_stat) {
136  self->v_table->link_stat(self, node);
137  }
138 #endif /* defined(AO2_DEBUG) */
139  /* Fall through */
141 #if defined(AO2_DEBUG)
142  if (ao2_container_check(self, OBJ_NOLOCK)) {
143  ast_log(LOG_ERROR, "Container integrity failed after insert or replace.\n");
144  }
145 #endif /* defined(AO2_DEBUG) */
146  res = 1;
147  break;
149  ao2_ref(node, -1);
150  break;
151  }
152  }
153 
154  if (flags & OBJ_NOLOCK) {
155  __adjust_lock(self, orig_lock, 0);
156  } else {
157  ao2_unlock(self);
158  }
159 
160  return res;
161 }
162 
163 /*!
164  * \brief another convenience function is a callback that matches on address
165  */
166 int ao2_match_by_addr(void *user_data, void *arg, int flags)
167 {
168  return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
169 }
170 
171 /*
172  * Unlink an object from the container
173  * and destroy the associated * bucket_entry structure.
174  */
175 void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags,
176  const char *tag, const char *file, int line, const char *func)
177 {
178  if (!__is_ao2_object(user_data, file, line, func)) {
179  /* Sanity checks. */
180  return NULL;
181  }
182 
183  flags &= ~OBJ_SEARCH_MASK;
184  flags |= (OBJ_UNLINK | OBJ_SEARCH_OBJECT | OBJ_NODATA);
185  __ao2_callback(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
186 
187  return NULL;
188 }
189 
190 /*!
191  * \brief special callback that matches all
192  */
193 static int cb_true(void *user_data, void *arg, int flags)
194 {
195  return CMP_MATCH;
196 }
197 
198 /*!
199  * \brief similar to cb_true, but is an ao2_callback_data_fn instead
200  */
201 static int cb_true_data(void *user_data, void *arg, void *data, int flags)
202 {
203  return CMP_MATCH;
204 }
205 
206 /*!
207  * \internal
208  * \brief Traverse the container. (internal)
209  *
210  * \param self Container to operate upon.
211  * \param flags search_flags to control traversing the container
212  * \param cb_fn Comparison callback function.
213  * \param arg Comparison callback arg parameter.
214  * \param data Data comparison callback data parameter.
215  * \param type Type of comparison callback cb_fn.
216  * \param tag used for debugging.
217  * \param file Debug file name invoked from
218  * \param line Debug line invoked from
219  * \param func Debug function name invoked from
220  *
221  * \retval NULL on failure or no matching object found.
222  *
223  * \retval object found if OBJ_MULTIPLE is not set in the flags
224  * parameter.
225  *
226  * \retval ao2_iterator pointer if OBJ_MULTIPLE is set in the
227  * flags parameter. The iterator must be destroyed with
228  * ao2_iterator_destroy() when the caller no longer needs it.
229  */
230 static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags flags,
231  void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
232  const char *tag, const char *file, int line, const char *func)
233 {
234  void *ret;
235  ao2_callback_fn *cb_default = NULL;
236  ao2_callback_data_fn *cb_withdata = NULL;
237  struct ao2_container_node *node;
238  void *traversal_state;
239 
240  enum ao2_lock_req orig_lock;
241  struct ao2_container *multi_container = NULL;
242  struct ao2_iterator *multi_iterator = NULL;
243 
244  if (!__is_ao2_object(self, file, line, func)) {
245  return NULL;
246  }
247 
248  if (!self->v_table
249  || !self->v_table->traverse_first || !self->v_table->traverse_next) {
250  /* Sanity checks. */
251  __ast_assert_failed(0, "invalid container v_table", file, line, func);
252  return NULL;
253  }
254 
255  /*
256  * This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
257  * turned off. This if statement checks for the special condition
258  * where multiple items may need to be returned.
259  */
260  if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
261  /* we need to return an ao2_iterator with the results,
262  * as there could be more than one. the iterator will
263  * hold the only reference to a container that has all the
264  * matching objects linked into it, so when the iterator
265  * is destroyed, the container will be automatically
266  * destroyed as well.
267  */
269  NULL, "OBJ_MULTIPLE return container creation");
270  if (!multi_container) {
271  return NULL;
272  }
273  if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
274  ao2_t_ref(multi_container, -1, "OBJ_MULTIPLE interator creation failed.");
275  return NULL;
276  }
277  }
278 
279  if (!cb_fn) {
280  /* Match everything if no callback match function provided. */
281  if (type == AO2_CALLBACK_WITH_DATA) {
282  cb_withdata = cb_true_data;
283  } else {
284  cb_default = cb_true;
285  }
286  } else {
287  /*
288  * We do this here to avoid the per object casting penalty (even
289  * though that is probably optimized away anyway).
290  */
291  if (type == AO2_CALLBACK_WITH_DATA) {
292  cb_withdata = cb_fn;
293  } else {
294  cb_default = cb_fn;
295  }
296  }
297 
298  /* avoid modifications to the content */
299  if (flags & OBJ_NOLOCK) {
300  if (flags & OBJ_UNLINK) {
301  orig_lock = __adjust_lock(self, AO2_LOCK_REQ_WRLOCK, 1);
302  } else {
303  orig_lock = __adjust_lock(self, AO2_LOCK_REQ_RDLOCK, 1);
304  }
305  } else {
306  orig_lock = AO2_LOCK_REQ_MUTEX;
307  if (flags & OBJ_UNLINK) {
308  ao2_wrlock(self);
309  } else {
310  ao2_rdlock(self);
311  }
312  }
313 
314  /* Create a buffer for the traversal state. */
315  traversal_state = alloca(AO2_TRAVERSAL_STATE_SIZE);
316 
317  ret = NULL;
318  for (node = self->v_table->traverse_first(self, flags, arg, traversal_state);
319  node;
320  node = self->v_table->traverse_next(self, traversal_state, node)) {
321  int match;
322 
323  /* Visit the current node. */
324  match = (CMP_MATCH | CMP_STOP);
325  if (type == AO2_CALLBACK_WITH_DATA) {
326  match &= cb_withdata(node->obj, arg, data, flags);
327  } else {
328  match &= cb_default(node->obj, arg, flags);
329  }
330  if (match == 0) {
331  /* no match, no stop, continue */
332  continue;
333  }
334  if (match == CMP_STOP) {
335  /* no match but stop, we are done */
336  break;
337  }
338 
339  /*
340  * CMP_MATCH is set here
341  *
342  * we found the object, performing operations according to flags
343  */
344  if (node->obj) {
345  /* The object is still in the container. */
346  if (!(flags & OBJ_NODATA)) {
347  /*
348  * We are returning the object, record the value. It is
349  * important to handle this case before the unlink.
350  */
351  if (multi_container) {
352  /*
353  * Link the object into the container that will hold the
354  * results.
355  */
356  __ao2_link(multi_container, node->obj, flags, tag, file, line, func);
357  } else {
358  ret = node->obj;
359  /* Returning a single object. */
360  if (!(flags & OBJ_UNLINK)) {
361  /*
362  * Bump the ref count since we are not going to unlink and
363  * transfer the container's object ref to the returned object.
364  */
365  __ao2_ref(ret, 1, tag ?: "Traversal found object", file, line, func);
366  }
367  }
368  }
369 
370  if (flags & OBJ_UNLINK) {
372  if (multi_container || (flags & OBJ_NODATA)) {
374  }
375  __container_unlink_node_debug(node, ulflag, tag, file, line, func);
376  }
377  }
378 
379  if ((match & CMP_STOP) || !(flags & OBJ_MULTIPLE)) {
380  /* We found our only (or last) match, so we are done */
381  break;
382  }
383  }
384  if (self->v_table->traverse_cleanup) {
385  self->v_table->traverse_cleanup(traversal_state);
386  }
387  if (node) {
388  /* Unref the node from self->v_table->traverse_first/traverse_next() */
389  ao2_ref(node, -1);
390  }
391 
392  if (flags & OBJ_NOLOCK) {
393  __adjust_lock(self, orig_lock, 0);
394  } else {
395  ao2_unlock(self);
396  }
397 
398  /* if multi_container was created, we are returning multiple objects */
399  if (multi_container) {
400  *multi_iterator = ao2_iterator_init(multi_container,
402  ao2_t_ref(multi_container, -1,
403  "OBJ_MULTIPLE for multiple objects traversal complete.");
404  return multi_iterator;
405  } else {
406  return ret;
407  }
408 }
409 
411  ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
412  const char *func)
413 {
414  return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, tag, file, line, func);
415 }
416 
418  ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
419  int line, const char *func)
420 {
421  return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, tag, file, line, func);
422 }
423 
424 /*!
425  * the find function just invokes the default callback with some reasonable flags.
426  */
427 void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags,
428  const char *tag, const char *file, int line, const char *func)
429 {
430  void *arged = (void *) arg;/* Done to avoid compiler const warning */
431 
432  if (!c) {
433  /* Sanity checks. */
434  ast_assert(0);
435  return NULL;
436  }
437  return __ao2_callback(c, flags, c->cmp_fn, arged, tag, file, line, func);
438 }
439 
440 void *__ao2_weakproxy_find(struct ao2_container *c, const void *arg, enum search_flags flags,
441  const char *tag, const char *file, int line, const char *func)
442 {
443  void *proxy;
444  void *obj = NULL;
445  enum ao2_lock_req orig_lock;
446 
447  ast_assert(!!c);
448  ast_assert(flags & OBJ_SEARCH_MASK);
449  ast_assert(!(flags & ~(OBJ_SEARCH_MASK | OBJ_NOLOCK)));
450 
451  if (flags & OBJ_NOLOCK) {
452  orig_lock = __adjust_lock(c, AO2_LOCK_REQ_RDLOCK, 1);
453  } else {
454  orig_lock = AO2_LOCK_REQ_RDLOCK;
455  ao2_rdlock(c);
456  }
457 
458  while ((proxy = ao2_find(c, arg, flags | OBJ_NOLOCK))) {
459  obj = __ao2_weakproxy_get_object(proxy, 0, tag ?: __PRETTY_FUNCTION__, file, line, func);
460 
461  if (obj) {
462  ao2_ref(proxy, -1);
463  break;
464  }
465 
466  /* Upgrade to a write lock */
468  ao2_unlink_flags(c, proxy, OBJ_NOLOCK);
469  ao2_ref(proxy, -1);
470  }
471 
472  if (flags & OBJ_NOLOCK) {
473  /* We'll keep any upgraded lock */
474  __adjust_lock(c, orig_lock, 1);
475  } else {
476  ao2_unlock(c);
477  }
478 
479  return obj;
480 }
481 
482 /*!
483  * initialize an iterator so we start from the first object
484  */
486 {
487  struct ao2_iterator a = {
488  .c = c,
489  .flags = flags
490  };
491 
492  ao2_t_ref(c, +1, "Init iterator with container.");
493 
494  return a;
495 }
496 
498 {
499  if (!is_ao2_object(iter->c)) {
500  /* Sanity check. */
501  return;
502  }
503 
504  /* Release the last container node reference if we have one. */
505  if (iter->last_node) {
506  enum ao2_lock_req orig_lock;
507 
508  /*
509  * Do a read lock in case the container node unref does not
510  * destroy the node. If the container node is destroyed then
511  * the lock will be upgraded to a write lock.
512  */
513  if (iter->flags & AO2_ITERATOR_DONTLOCK) {
514  orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
515  } else {
516  orig_lock = AO2_LOCK_REQ_MUTEX;
517  ao2_rdlock(iter->c);
518  }
519 
520  ao2_ref(iter->last_node, -1);
521  iter->last_node = NULL;
522 
523  if (iter->flags & AO2_ITERATOR_DONTLOCK) {
524  __adjust_lock(iter->c, orig_lock, 0);
525  } else {
526  ao2_unlock(iter->c);
527  }
528  }
529 
530  /* The iteration is no longer complete. */
531  iter->complete = 0;
532 }
533 
535 {
536  /* Release any last container node reference. */
537  ao2_iterator_restart(iter);
538 
539  /* Release the iterated container reference. */
540  ao2_t_ref(iter->c, -1, "Unref iterator in ao2_iterator_destroy");
541  iter->c = NULL;
542 
543  /* Free the malloced iterator. */
544  if (iter->flags & AO2_ITERATOR_MALLOCD) {
545  ast_free(iter);
546  }
547 }
548 
550 {
551  if (iter) {
552  ao2_iterator_destroy(iter);
553  }
554 }
555 
557  const char *tag, const char *file, int line, const char *func)
558 {
559  enum ao2_lock_req orig_lock;
560  struct ao2_container_node *node;
561  void *ret;
562 
563  if (!__is_ao2_object(iter->c, file, line, func)) {
564  return NULL;
565  }
566 
567  if (!iter->c->v_table || !iter->c->v_table->iterator_next) {
568  /* Sanity checks. */
569  __ast_assert_failed(0, "invalid iterator container v_table", file, line, func);
570  return NULL;
571  }
572 
573  if (iter->complete) {
574  /* Don't return any more objects. */
575  return NULL;
576  }
577 
578  if (iter->flags & AO2_ITERATOR_DONTLOCK) {
579  if (iter->flags & AO2_ITERATOR_UNLINK) {
580  orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_WRLOCK, 1);
581  } else {
582  orig_lock = __adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
583  }
584  } else {
585  orig_lock = AO2_LOCK_REQ_MUTEX;
586  if (iter->flags & AO2_ITERATOR_UNLINK) {
587  ao2_wrlock(iter->c);
588  } else {
589  ao2_rdlock(iter->c);
590  }
591  }
592 
593  node = iter->c->v_table->iterator_next(iter->c, iter->last_node, iter->flags);
594  if (node) {
595  ret = node->obj;
596 
597  if (iter->flags & AO2_ITERATOR_UNLINK) {
598  /* Transfer the object ref from the container to the returned object. */
599  __container_unlink_node_debug(node, AO2_UNLINK_NODE_DEC_COUNT, tag, file, line, func);
600 
601  /* Transfer the container's node ref to the iterator. */
602  } else {
603  /* Bump ref of returned object */
604  __ao2_ref(ret, +1, tag ?: "Next iterator object.", file, line, func);
605 
606  /* Bump the container's node ref for the iterator. */
607  ao2_ref(node, +1);
608  }
609  } else {
610  /* The iteration has completed. */
611  iter->complete = 1;
612  ret = NULL;
613  }
614 
615  /* Replace the iterator's node */
616  if (iter->last_node) {
617  ao2_ref(iter->last_node, -1);
618  }
619  iter->last_node = node;
620 
621  if (iter->flags & AO2_ITERATOR_DONTLOCK) {
622  __adjust_lock(iter->c, orig_lock, 0);
623  } else {
624  ao2_unlock(iter->c);
625  }
626 
627  return ret;
628 }
629 
631 {
632  return ao2_container_count(iter->c);
633 }
634 
635 void container_destruct(void *_c)
636 {
637  struct ao2_container *c = _c;
638 
639  /* Unlink any stored objects in the container. */
640  c->destroying = 1;
642  "container_destruct called");
643 
644  /* Perform any extra container cleanup. */
645  if (c->v_table && c->v_table->destroy) {
646  c->v_table->destroy(c);
647  }
648 
649 #if defined(AO2_DEBUG)
650  ast_atomic_fetchadd_int(&ao2.total_containers, -1);
651 #endif
652 }
653 
654 /*!
655  * \internal
656  * \brief Put obj into the arg container.
657  * \since 11.0
658  *
659  * \param obj pointer to the (user-defined part) of an object.
660  * \param arg callback argument from ao2_callback()
661  * \param flags flags from ao2_callback()
662  *
663  * \retval 0 on success.
664  * \retval CMP_STOP|CMP_MATCH on error.
665  */
666 static int dup_obj_cb(void *obj, void *arg, int flags)
667 {
668  struct ao2_container *dest = arg;
669 
670  return ao2_link_flags(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
671 }
672 
673 int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
674 {
675  void *obj;
676  int res = 0;
677 
678  if (!(flags & OBJ_NOLOCK)) {
679  ao2_rdlock(src);
680  ao2_wrlock(dest);
681  }
682  obj = ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
683  if (obj) {
684  /* Failed to put this obj into the dest container. */
685  ao2_t_ref(obj, -1, "Failed to put this object into the dest container.");
686 
687  /* Remove all items from the dest container. */
688  ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
689  NULL);
690  res = -1;
691  }
692  if (!(flags & OBJ_NOLOCK)) {
693  ao2_unlock(dest);
694  ao2_unlock(src);
695  }
696 
697  return res;
698 }
699 
700 /*!
701  * \brief Copy obj associated with a weakproxy into the arg container.
702  *
703  * \param proxy pointer to the weakproxy.
704  * \param arg callback argument from ao2_callback()
705  * \param flags flags from ao2_callback()
706  *
707  * \retval 0 on success.
708  * \retval CMP_STOP|CMP_MATCH on error.
709  */
710 static int dup_weakproxy_cb(void *proxy, void *arg, int flags)
711 {
712  void *obj = ao2_weakproxy_get_object(proxy, 0);
713  struct ao2_container *dest = arg;
714  int ret;
715 
716  if (!obj) {
717  return 0;
718  }
719 
720  ret = ao2_link_flags(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
721  ao2_ref(obj, -1);
722 
723  return ret;
724 }
725 
727 {
728  void *obj;
729  int res = 0;
730 
731  if (!(flags & OBJ_NOLOCK)) {
732  ao2_rdlock(src);
733  ao2_wrlock(dest);
734  }
735  obj = ao2_callback(src, OBJ_NOLOCK, dup_weakproxy_cb, dest);
736  if (obj) {
737  /* Failed to put this obj into the dest container. */
738  ao2_t_ref(obj, -1, "Failed to put this object into the dest container.");
739 
740  /* Remove all items from the dest container. */
741  ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
742  NULL);
743  res = -1;
744  }
745  if (!(flags & OBJ_NOLOCK)) {
746  ao2_unlock(dest);
747  ao2_unlock(src);
748  }
749 
750  return res;
751 }
752 
753 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
754 {
755  struct ao2_container *clone;
756  int failed;
757 
758  /* Create the clone container with the same properties as the original. */
759  if (!__is_ao2_object(orig, file, line, func)) {
760  return NULL;
761  }
762 
763  if (!orig->v_table || !orig->v_table->alloc_empty_clone) {
764  /* Sanity checks. */
765  __ast_assert_failed(0, "invalid container v_table", file, line, func);
766  return NULL;
767  }
768 
769  clone = orig->v_table->alloc_empty_clone(orig, tag, file, line, func);
770  if (!clone) {
771  return NULL;
772  }
773 
774  /* This test is correct. clone must be locked before calling
775  * ao2_container_dup when the OBJ_NOLOCK flag is set, otherwise
776  * we could have errors in __adjust_lock. */
777  if (flags & OBJ_NOLOCK) {
778  ao2_wrlock(clone);
779  }
780  failed = ao2_container_dup(clone, orig, flags);
781  if (flags & OBJ_NOLOCK) {
782  ao2_unlock(clone);
783  }
784  if (failed) {
785  /* Object copy into the clone container failed. */
786  __ao2_ref(clone, -1, tag ?: "Clone creation failed", file, line, func);
787  clone = NULL;
788  }
789  return clone;
790 }
791 
792 void ao2_container_dump(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt, ao2_prnt_obj_fn *prnt_obj)
793 {
794  if (!is_ao2_object(self) || !self->v_table) {
795  prnt(where, "Invalid container\n");
796  ast_assert(0);
797  return;
798  }
799 
800  if (!(flags & OBJ_NOLOCK)) {
801  ao2_rdlock(self);
802  }
803  if (name) {
804  prnt(where, "Container name: %s\n", name);
805  }
806 #if defined(AO2_DEBUG)
807  if (self->v_table->dump) {
808  self->v_table->dump(self, where, prnt, prnt_obj);
809  } else
810 #endif /* defined(AO2_DEBUG) */
811  {
812  prnt(where, "Container dump not available.\n");
813  }
814  if (!(flags & OBJ_NOLOCK)) {
815  ao2_unlock(self);
816  }
817 }
818 
819 void ao2_container_stats(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt)
820 {
821  if (!is_ao2_object(self) || !self->v_table) {
822  prnt(where, "Invalid container\n");
823  ast_assert(0);
824  return;
825  }
826 
827  if (!(flags & OBJ_NOLOCK)) {
828  ao2_rdlock(self);
829  }
830  if (name) {
831  prnt(where, "Container name: %s\n", name);
832  }
833  prnt(where, "Number of objects: %d\n", self->elements);
834 #if defined(AO2_DEBUG)
835  prnt(where, "Number of nodes: %d\n", self->nodes);
836  prnt(where, "Number of empty nodes: %d\n", self->nodes - self->elements);
837  /*
838  * XXX
839  * If the max_empty_nodes count gets out of single digits you
840  * likely have a code path where ao2_iterator_destroy() is not
841  * called.
842  *
843  * Empty nodes do not harm the container but they do make
844  * container operations less efficient.
845  */
846  prnt(where, "Maximum empty nodes: %d\n", self->max_empty_nodes);
847  if (self->v_table->stats) {
848  self->v_table->stats(self, where, prnt);
849  }
850 #endif /* defined(AO2_DEBUG) */
851  if (!(flags & OBJ_NOLOCK)) {
852  ao2_unlock(self);
853  }
854 }
855 
856 int ao2_container_check(struct ao2_container *self, enum search_flags flags)
857 {
858  int res = 0;
859 
860  if (!is_ao2_object(self) || !self->v_table) {
861  /* Sanity checks. */
862  ast_assert(0);
863  return -1;
864  }
865 #if defined(AO2_DEBUG)
866  if (!self->v_table->integrity) {
867  /* No ingetrigy check available. Assume container is ok. */
868  return 0;
869  }
870 
871  if (!(flags & OBJ_NOLOCK)) {
872  ao2_rdlock(self);
873  }
874  res = self->v_table->integrity(self);
875  if (!(flags & OBJ_NOLOCK)) {
876  ao2_unlock(self);
877  }
878 #endif /* defined(AO2_DEBUG) */
879  return res;
880 }
881 
882 #if defined(AO2_DEBUG)
883 static struct ao2_container *reg_containers;
884 
885 struct ao2_reg_container {
886  /*! Registered container pointer. */
887  struct ao2_container *registered;
888  /*! Callback function to print the given object's key. (NULL if not available) */
889  ao2_prnt_obj_fn *prnt_obj;
890  /*! Name container registered under. */
891  char name[1];
892 };
893 
894 struct ao2_reg_partial_key {
895  /*! Length of partial key match. */
896  int len;
897  /*! Registration partial key name. */
898  const char *name;
899 };
900 
901 struct ao2_reg_match {
902  /*! The nth match to find. */
903  int find_nth;
904  /*! Count of the matches already found. */
905  int count;
906 };
907 #endif /* defined(AO2_DEBUG) */
908 
909 #if defined(AO2_DEBUG)
910 static int ao2_reg_sort_cb(const void *obj_left, const void *obj_right, int flags)
911 {
912  const struct ao2_reg_container *reg_left = obj_left;
913  int cmp;
914 
915  switch (flags & OBJ_SEARCH_MASK) {
916  case OBJ_SEARCH_OBJECT:
917  {
918  const struct ao2_reg_container *reg_right = obj_right;
919 
920  cmp = strcasecmp(reg_left->name, reg_right->name);
921  }
922  break;
923  case OBJ_SEARCH_KEY:
924  {
925  const char *name = obj_right;
926 
927  cmp = strcasecmp(reg_left->name, name);
928  }
929  break;
931  {
932  const struct ao2_reg_partial_key *partial_key = obj_right;
933 
934  cmp = strncasecmp(reg_left->name, partial_key->name, partial_key->len);
935  }
936  break;
937  default:
938  /* Sort can only work on something with a full or partial key. */
939  ast_assert(0);
940  cmp = 0;
941  break;
942  }
943  return cmp;
944 }
945 #endif /* defined(AO2_DEBUG) */
946 
947 #if defined(AO2_DEBUG)
948 static void ao2_reg_destructor(void *v_doomed)
949 {
950  struct ao2_reg_container *doomed = v_doomed;
951 
952  if (doomed->registered) {
953  ao2_t_ref(doomed->registered, -1, "Releasing registered container.");
954  }
955 }
956 #endif /* defined(AO2_DEBUG) */
957 
958 int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
959 {
960  int res = 0;
961 #if defined(AO2_DEBUG)
962  struct ao2_reg_container *reg;
963 
964  reg = ao2_t_alloc_options(sizeof(*reg) + strlen(name), ao2_reg_destructor,
965  AO2_ALLOC_OPT_LOCK_NOLOCK, "Container registration object.");
966  if (!reg) {
967  return -1;
968  }
969 
970  /* Fill in registered entry */
971  ao2_t_ref(self, +1, "Registering container.");
972  reg->registered = self;
973  reg->prnt_obj = prnt_obj;
974  strcpy(reg->name, name);/* safe */
975 
976  if (!ao2_t_link(reg_containers, reg, "Save registration object.")) {
977  res = -1;
978  }
979 
980  ao2_t_ref(reg, -1, "Done registering container.");
981 #endif /* defined(AO2_DEBUG) */
982  return res;
983 }
984 
986 {
987 #if defined(AO2_DEBUG)
988  ao2_t_find(reg_containers, name, OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY,
989  "Unregister container");
990 #endif /* defined(AO2_DEBUG) */
991 }
992 
993 #if defined(AO2_DEBUG)
994 static int ao2_complete_reg_cb(void *obj, void *arg, int flags)
995 {
996  struct ao2_reg_container *reg = obj;
997 
998  if (ast_cli_completion_add(ast_strdup(reg->name))) {
999  return CMP_STOP;
1000  }
1001 
1002  return 0;
1003 }
1004 #endif /* defined(AO2_DEBUG) */
1005 
1006 #if defined(AO2_DEBUG)
1007 static char *complete_container_names(struct ast_cli_args *a)
1008 {
1009  struct ao2_reg_partial_key partial_key;
1010 
1011  if (a->pos != 3) {
1012  return NULL;
1013  }
1014 
1015  partial_key.len = strlen(a->word);
1016  partial_key.name = a->word;
1017  ao2_callback(reg_containers, partial_key.len ? OBJ_SEARCH_PARTIAL_KEY : 0,
1018  ao2_complete_reg_cb, &partial_key);
1019 
1020  return NULL;
1021 }
1022 #endif /* defined(AO2_DEBUG) */
1023 
1024 #if defined(AO2_DEBUG)
1025 AST_THREADSTORAGE(ao2_out_buf);
1026 
1027 /*!
1028  * \brief Print CLI output.
1029  * \since 12.0.0
1030  *
1031  * \param where User data pointer needed to determine where to put output.
1032  * \param fmt printf type format string.
1033  *
1034  * \return Nothing
1035  */
1036 static void cli_output(void *where, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
1037 static void cli_output(void *where, const char *fmt, ...)
1038 {
1039  int res;
1040  struct ast_str *buf;
1041  va_list ap;
1042 
1043  buf = ast_str_thread_get(&ao2_out_buf, 256);
1044  if (!buf) {
1045  return;
1046  }
1047 
1048  va_start(ap, fmt);
1049  res = ast_str_set_va(&buf, 0, fmt, ap);
1050  va_end(ap);
1051 
1052  if (res != AST_DYNSTR_BUILD_FAILED) {
1053  ast_cli(*(int *) where, "%s", ast_str_buffer(buf));
1054  }
1055 }
1056 #endif /* defined(AO2_DEBUG) */
1057 
1058 #if defined(AO2_DEBUG)
1059 /*! \brief Show container contents - CLI command */
1060 static char *handle_cli_astobj2_container_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1061 {
1062  const char *name;
1063  struct ao2_reg_container *reg;
1064 
1065  switch (cmd) {
1066  case CLI_INIT:
1067  e->command = "astobj2 container dump";
1068  e->usage =
1069  "Usage: astobj2 container dump <name>\n"
1070  " Show contents of the container <name>.\n";
1071  return NULL;
1072  case CLI_GENERATE:
1073  return complete_container_names(a);
1074  }
1075 
1076  if (a->argc != 4) {
1077  return CLI_SHOWUSAGE;
1078  }
1079 
1080  name = a->argv[3];
1081  reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
1082  if (reg) {
1083  ao2_container_dump(reg->registered, 0, name, (void *) &a->fd, cli_output,
1084  reg->prnt_obj);
1085  ao2_t_ref(reg, -1, "Done with registered container object.");
1086  } else {
1087  ast_cli(a->fd, "Container '%s' not found.\n", name);
1088  }
1089 
1090  return CLI_SUCCESS;
1091 }
1092 #endif /* defined(AO2_DEBUG) */
1093 
1094 #if defined(AO2_DEBUG)
1095 /*! \brief Show container statistics - CLI command */
1096 static char *handle_cli_astobj2_container_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1097 {
1098  const char *name;
1099  struct ao2_reg_container *reg;
1100 
1101  switch (cmd) {
1102  case CLI_INIT:
1103  e->command = "astobj2 container stats";
1104  e->usage =
1105  "Usage: astobj2 container stats <name>\n"
1106  " Show statistics about the specified container <name>.\n";
1107  return NULL;
1108  case CLI_GENERATE:
1109  return complete_container_names(a);
1110  }
1111 
1112  if (a->argc != 4) {
1113  return CLI_SHOWUSAGE;
1114  }
1115 
1116  name = a->argv[3];
1117  reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
1118  if (reg) {
1119  ao2_container_stats(reg->registered, 0, name, (void *) &a->fd, cli_output);
1120  ao2_t_ref(reg, -1, "Done with registered container object.");
1121  } else {
1122  ast_cli(a->fd, "Container '%s' not found.\n", name);
1123  }
1124 
1125  return CLI_SUCCESS;
1126 }
1127 #endif /* defined(AO2_DEBUG) */
1128 
1129 #if defined(AO2_DEBUG)
1130 /*! \brief Show container check results - CLI command */
1131 static char *handle_cli_astobj2_container_check(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1132 {
1133  const char *name;
1134  struct ao2_reg_container *reg;
1135 
1136  switch (cmd) {
1137  case CLI_INIT:
1138  e->command = "astobj2 container check";
1139  e->usage =
1140  "Usage: astobj2 container check <name>\n"
1141  " Perform a container integrity check on <name>.\n";
1142  return NULL;
1143  case CLI_GENERATE:
1144  return complete_container_names(a);
1145  }
1146 
1147  if (a->argc != 4) {
1148  return CLI_SHOWUSAGE;
1149  }
1150 
1151  name = a->argv[3];
1152  reg = ao2_t_find(reg_containers, name, OBJ_SEARCH_KEY, "Find registered container");
1153  if (reg) {
1154  ast_cli(a->fd, "Container check of '%s': %s.\n", name,
1155  ao2_container_check(reg->registered, 0) ? "failed" : "OK");
1156  ao2_t_ref(reg, -1, "Done with registered container object.");
1157  } else {
1158  ast_cli(a->fd, "Container '%s' not found.\n", name);
1159  }
1160 
1161  return CLI_SUCCESS;
1162 }
1163 #endif /* defined(AO2_DEBUG) */
1164 
1165 #if defined(AO2_DEBUG)
1166 static struct ast_cli_entry cli_astobj2[] = {
1167  AST_CLI_DEFINE(handle_cli_astobj2_container_dump, "Show container contents"),
1168  AST_CLI_DEFINE(handle_cli_astobj2_container_stats, "Show container statistics"),
1169  AST_CLI_DEFINE(handle_cli_astobj2_container_check, "Perform a container integrity check"),
1170 };
1171 #endif /* defined(AO2_DEBUG) */
1172 
1173 #if defined(AO2_DEBUG)
1174 static void container_cleanup(void)
1175 {
1176  ao2_t_ref(reg_containers, -1, "Releasing container registration container");
1177  reg_containers = NULL;
1178 
1179  ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
1180 }
1181 #endif /* defined(AO2_DEBUG) */
1182 
1184 {
1185 #if defined(AO2_DEBUG)
1187  AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_reg_sort_cb, NULL,
1188  "Container registration container.");
1189  if (!reg_containers) {
1190  return -1;
1191  }
1192 
1193  ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
1194  ast_register_cleanup(container_cleanup);
1195 #endif /* defined(AO2_DEBUG) */
1196 
1197  return 0;
1198 }
#define ao2_t_ref(o, delta, tag)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:463
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:84
static const char type[]
Definition: chan_ooh323.c:109
Definition: test_heap.c:38
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
int ao2_iterator_count(struct ao2_iterator *iter)
Get a count of the iterated container objects.
int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flags, const char *tag, const char *file, int line, const char *func)
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
void * last_node
Definition: astobj2.h:1845
#define __is_ao2_object(user_data, file, line, func)
ao2_container_destroy_fn destroy
void * __ao2_iterator_next(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
ao2_lock_req
Which lock to request.
Definition: astobj2.h:701
static int cb_true_data(void *user_data, void *arg, void *data, int flags)
similar to cb_true, but is an ao2_callback_data_fn instead
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
void container_destruct(void *_c)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
Create an iterator for a container.
Common, private definitions for astobj2.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void * __ao2_unlink(struct ao2_container *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *func)
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
int __ao2_link(struct ao2_container *self, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
void * __ao2_callback(struct ao2_container *c, enum search_flags flags, ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line, const char *func)
void ao2_iterator_restart(struct ao2_iterator *iter)
Restart an iteration.
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
Definition: strings.h:982
static int dup_obj_cb(void *obj, void *arg, int flags)
Definition: cli.h:152
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
#define ao2_t_link(container, obj, tag)
Add an object to a container.
Definition: astobj2.h:1547
#define ao2_t_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn, tag)
Allocate and initialize a list container.
Definition: astobj2.h:1333
void ao2_iterator_cleanup(struct ao2_iterator *iter)
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_assert(a)
Definition: utils.h:695
#define ao2_link_flags(container, obj, flags)
Definition: astobj2.h:1572
#define ao2_unlock(a)
Definition: astobj2.h:730
static struct test_val c
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2315
#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg)
Allocate and initialize an object.
Definition: astobj2.h:404
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
int ao2_container_check(struct ao2_container *self, enum search_flags flags)
Perform an integrity check on the specified container.
search_flags
Flags passed to ao2_callback_fn(), ao2_hash_fn(), and ao2_sort_fn() to modify behaviour.
Definition: astobj2.h:1038
#define NULL
Definition: resample.c:96
static int cb_true(void *user_data, void *arg, int flags)
special callback that matches all
#define ao2_wrlock(a)
Definition: astobj2.h:720
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
int complete
Definition: astobj2.h:1847
int() ao2_callback_data_fn(void *obj, void *arg, void *data, int flags)
Type of a generic callback function.
Definition: astobj2.h:1248
struct ao2_container * c
Definition: astobj2.h:1843
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
void() ao2_prnt_fn(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1442
Generic container node.
#define is_ao2_object(user_data)
int ao2_match_by_addr(void *user_data, void *arg, int flags)
another convenience function is a callback that matches on address
int __ao2_ref(void *o, int delta, const char *tag, const char *file, int line, const char *func)
Definition: astobj2.c:498
int() ao2_callback_fn(void *obj, void *arg, int flags)
Type of a generic callback function.
Definition: astobj2.h:1230
#define ast_log
Definition: astobj2.c:42
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1120
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
const int fd
Definition: cli.h:159
#define ao2_weakproxy_get_object(weakproxy, flags)
Definition: astobj2.h:625
#define ao2_ref(o, delta)
Definition: astobj2.h:464
const struct ao2_container_methods * v_table
void * __ao2_weakproxy_get_object(void *weakproxy, int flags, const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result
Get the object associated with weakproxy.
Definition: astobj2.c:886
#define AO2_DEVMODE_STAT(stat)
struct ao2_container * container
Definition: res_fax.c:502
void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
Definition: main/utils.c:2564
void ao2_container_dump(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt, ao2_prnt_obj_fn *prnt_obj)
Display contents of the specified container.
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
Register a container for CLI stats and integrity check.
const char *const * argv
Definition: cli.h:161
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static void * internal_ao2_traverse(struct ao2_container *self, enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, const char *tag, const char *file, int line, const char *func)
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define CLI_SHOWUSAGE
Definition: cli.h:45
ao2_iterator_next_fn iterator_next
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ao2_rdlock(a)
Definition: astobj2.h:719
int ao2_container_dup_weakproxy_objs(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy object references associated with src container weakproxies into the dest container.
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1714
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
void * __ao2_weakproxy_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
unsigned int destroying
TRUE if the container is being destroyed.
void ao2_container_unregister(const char *name)
Unregister a container for CLI stats and integrity check.
static int dup_weakproxy_cb(void *proxy, void *arg, int flags)
Copy obj associated with a weakproxy into the arg container.
const char * word
Definition: cli.h:163
Prototypes for public functions only of internal interest,.
#define AO2_TRAVERSAL_STATE_SIZE
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
void ao2_container_stats(struct ao2_container *self, enum search_flags flags, const char *name, void *where, ao2_prnt_fn *prnt)
Display statistics of the specified container.
Assume that the ao2_container is already locked.
Definition: astobj2.h:1872
The arg parameter is an object of the same type.
Definition: astobj2.h:1091
Replace objects with duplicate keys in container.
Definition: astobj2.h:1215
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
Standard Command Line Interface.
struct ao2_container * __ao2_container_clone(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
Create a clone/copy of the given container.
const int pos
Definition: cli.h:164
Common, private definitions for astobj2 containers.
enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
Definition: astobj2.c:425
#define ao2_t_find(container, arg, flags, tag)
Definition: astobj2.h:1754
#define ao2_unlink_flags(container, obj, flags)
Definition: astobj2.h:1622
Generic container type.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
Search option field mask.
Definition: astobj2.h:1076
void() ao2_prnt_obj_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
Print object key.
Definition: astobj2.h:1454
struct ao2_container * my_container
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
static snd_pcm_format_t format
Definition: chan_alsa.c:102
ao2_container_alloc_empty_clone_fn alloc_empty_clone
Create an empty copy of this container.
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
void * __ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
ao2_callback_fn * cmp_fn
void * __ao2_callback_data(struct ao2_container *c, enum search_flags flags, ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file, int line, const char *func)
static struct test_val a
int container_init(void)