summaryrefslogtreecommitdiff
path: root/include/asterisk/astobj2.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/asterisk/astobj2.h')
-rw-r--r--include/asterisk/astobj2.h436
1 files changed, 411 insertions, 25 deletions
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index b02b6cba8..5f9776004 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -64,12 +64,7 @@ On return from ao2_alloc():
ao2_ref(o, -1)
causing the destructor to be called (and then memory freed) when
- the refcount goes to 0. This is also available as ao2_unref(o),
- and returns NULL as a convenience, so you can do things like
-
- o = ao2_unref(o);
-
- and clean the original pointer to prevent errors.
+ the refcount goes to 0.
- ao2_ref(o, +1) can be used to modify the refcount on the
object in case we want to pass it around.
@@ -121,6 +116,8 @@ Once done, we can link an object to a container with
The function returns NULL in case of errors (and the object
is not inserted in the container). Other values mean success
(we are not supposed to use the value as a pointer to anything).
+Linking an object to a container increases its refcount by 1
+automatically.
\note While an object o is in a container, we expect that
my_hash_fn(o) will always return the same value. The function
@@ -137,6 +134,245 @@ list. However there is no ordering among elements.
*/
+/*
+\note DEBUGGING REF COUNTS BIBLE:
+An interface to help debug refcounting is provided
+in this package. It is dependent on the REF_DEBUG macro being
+defined in a source file, before the #include of astobj2.h,
+and in using variants of the normal ao2_xxxx functions
+that are named ao2_t_xxxx instead, with an extra argument, a string,
+that will be printed out into /tmp/refs when the refcount for an
+object is changed.
+
+ these ao2_t_xxxx variants are provided:
+
+ao2_t_alloc(arg1, arg2, arg3)
+ao2_t_ref(arg1,arg2,arg3)
+ao2_t_container_alloc(arg1,arg2,arg3,arg4)
+ao2_t_link(arg1, arg2, arg3)
+ao2_t_unlink(arg1, arg2, arg3)
+ao2_t_callback(arg1,arg2,arg3,arg4,arg5)
+ao2_t_find(arg1,arg2,arg3,arg4)
+ao2_t_iterator_next(arg1, arg2)
+
+If you study each argument list, you will see that these functions all have
+one extra argument that their ao2_xxx counterpart. The last argument in
+each case is supposed to be a string pointer, a "tag", that should contain
+enough of an explanation, that you can pair operations that increment the
+ref count, with operations that are meant to decrement the refcount.
+
+Each of these calls will generate at least one line of output in /tmp/refs.
+These lines look like this:
+...
+0x8756f00 =1 chan_sip.c:22240:load_module (allocate users)
+0x86e3408 =1 chan_sip.c:22241:load_module (allocate peers)
+0x86dd380 =1 chan_sip.c:22242:load_module (allocate peers_by_ip)
+0x822d020 =1 chan_sip.c:22243:load_module (allocate dialogs)
+0x8930fd8 =1 chan_sip.c:20025:build_peer (allocate a peer struct)
+0x8930fd8 +1 chan_sip.c:21467:reload_config (link peer into peer table) [@1]
+0x8930fd8 -1 chan_sip.c:2370:unref_peer (unref_peer: from reload_config) [@2]
+0x89318b0 =1 chan_sip.c:20025:build_peer (allocate a peer struct)
+0x89318b0 +1 chan_sip.c:21467:reload_config (link peer into peer table) [@1]
+0x89318b0 -1 chan_sip.c:2370:unref_peer (unref_peer: from reload_config) [@2]
+0x8930218 =1 chan_sip.c:20025:build_peer (allocate a peer struct)
+0x8930218 +1 chan_sip.c:21539:reload_config (link peer into peers table) [@1]
+0x868c040 -1 chan_sip.c:2424:dialog_unlink_all (unset the relatedpeer->call field in tandem with relatedpeer field itself) [@2]
+0x868c040 -1 chan_sip.c:2443:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time) [@1]
+0x868c040 **call destructor** chan_sip.c:2443:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time)
+0x8cc07e8 -1 chan_sip.c:2370:unref_peer (unsetting a dialog relatedpeer field in sip_destroy) [@3]
+0x8cc07e8 +1 chan_sip.c:3876:find_peer (ao2_find in peers table) [@2]
+0x8cc07e8 -1 chan_sip.c:2370:unref_peer (unref_peer, from sip_devicestate, release ref from find_peer) [@3]
+...
+
+The first column is the object address.
+The second column reflects how the operation affected the ref count
+ for that object. Creation sets the ref count to 1 (=1).
+ increment or decrement and amount are specified (-1/+1).
+The remainder of the line specifies where in the file the call was made,
+ and the function name, and the tag supplied in the function call.
+
+The **call destructor** is specified when the the destroy routine is
+run for an object. It does not affect the ref count, but is important
+in debugging, because it is possible to have the astobj2 system run it
+multiple times on the same object, commonly fatal to asterisk.
+
+Sometimes you have some helper functions to do object ref/unref
+operations. Using these normally hides the place where these
+functions were called. To get the location where these functions
+were called to appear in /tmp/refs, you can do this sort of thing:
+
+#ifdef REF_DEBUG
+#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+static struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+{
+ if (p)
+ ao2_ref_debug(p, 1, tag, file, line, func);
+ else
+ ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
+ return p;
+}
+
+static struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
+{
+ if (p)
+ ao2_ref_debug(p, -1, tag, file, line, func);
+ return NULL;
+}
+#else
+static struct sip_pvt *dialog_ref(struct sip_pvt *p, char *tag)
+{
+ if (p)
+ ao2_ref(p, 1);
+ else
+ ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
+ return p;
+}
+
+static struct sip_pvt *dialog_unref(struct sip_pvt *p, char *tag)
+{
+ if (p)
+ ao2_ref(p, -1);
+ return NULL;
+}
+#endif
+
+In the above code, note that the "normal" helper funcs call ao2_ref() as
+normal, and the "helper" functions call ao2_ref_debug directly with the
+file, function, and line number info provided. You might find this
+well worth the effort to help track these function calls in the code.
+
+To find out why objects are not destroyed (a common bug), you can
+edit the source file to use the ao2_t_* variants, add the #define REF_DEBUG 1
+before the #include "asterisk/astobj2.h" line, and add a descriptive
+tag to each call. Recompile, and run Asterisk, exit asterisk with
+"stop gracefully", which should result in every object being destroyed.
+Then, you can "sort -k 1 /tmp/refs > x1" to get a sorted list of
+all the objects, or you can use "util/refcounter" to scan the file
+for you and output any problems it finds.
+
+The above may seem astronomically more work than it is worth to debug
+reference counts, which may be true in "simple" situations, but for
+more complex situations, it is easily worth 100 times this effort to
+help find problems.
+
+To debug, pair all calls so that each call that increments the
+refcount is paired with a corresponding call that decrements the
+count for the same reason. Hopefully, you will be left with one
+or more unpaired calls. This is where you start your search!
+
+For instance, here is an example of this for a dialog object in
+chan_sip, that was not getting destroyed, after I moved the lines around
+to pair operations:
+
+ 0x83787a0 =1 chan_sip.c:5733:sip_alloc (allocate a dialog(pvt) struct)
+ 0x83787a0 -1 chan_sip.c:19173:sip_poke_peer (unref dialog at end of sip_poke_peer, obtained from sip_alloc, just before it goes out of scope) [@4]
+
+ 0x83787a0 +1 chan_sip.c:5854:sip_alloc (link pvt into dialogs table) [@1]
+ 0x83787a0 -1 chan_sip.c:19150:sip_poke_peer (About to change the callid -- remove the old name) [@3]
+ 0x83787a0 +1 chan_sip.c:19152:sip_poke_peer (Linking in under new name) [@2]
+ 0x83787a0 -1 chan_sip.c:2399:dialog_unlink_all (unlinking dialog via ao2_unlink) [@5]
+
+ 0x83787a0 +1 chan_sip.c:19130:sip_poke_peer (copy sip alloc from p to peer->call) [@2]
+
+
+ 0x83787a0 +1 chan_sip.c:2996:__sip_reliable_xmit (__sip_reliable_xmit: setting pkt->owner) [@3]
+ 0x83787a0 -1 chan_sip.c:2425:dialog_unlink_all (remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy) [@4]
+
+ 0x83787a0 +1 chan_sip.c:22356:unload_module (iterate thru dialogs) [@4]
+ 0x83787a0 -1 chan_sip.c:22359:unload_module (toss dialog ptr from iterator_next) [@5]
+
+
+ 0x83787a0 +1 chan_sip.c:22373:unload_module (iterate thru dialogs) [@3]
+ 0x83787a0 -1 chan_sip.c:22375:unload_module (throw away iterator result) [@2]
+
+ 0x83787a0 +1 chan_sip.c:2397:dialog_unlink_all (Let's bump the count in the unlink so it doesn't accidentally become dead before we are done) [@4]
+ 0x83787a0 -1 chan_sip.c:2436:dialog_unlink_all (Let's unbump the count in the unlink so the poor pvt can disappear if it is time) [@3]
+
+As you can see, only one unbalanced operation is in the list, a ref count increment when
+the peer->call was set, but no corresponding decrement was made...
+
+Hopefully this helps you narrow your search and find those bugs.
+
+THE ART OF REFERENCE COUNTING
+(by Steve Murphy)
+SOME TIPS for complicated code, and ref counting:
+
+1. Theoretically, passing a refcounted object pointer into a function
+call is an act of copying the reference, and could be refcounted.
+But, upon examination, this sort of refcounting will explode the amount
+of code you have to enter, and for no tangible benefit, beyond
+creating more possible failure points/bugs. It will even
+complicate your code and make debugging harder, slow down your program
+doing useless increments and decrements of the ref counts.
+
+2. It is better to track places where a ref counted pointer
+is copied into a structure or stored. Make sure to decrement the refcount
+of any previous pointer that might have been there, if setting
+this field might erase a previous pointer. ao2_find and iterate_next
+internally increment the ref count when they return a pointer, so
+you need to decrement the count before the pointer goes out of scope.
+
+3. Any time you decrement a ref count, it may be possible that the
+object will be destroyed (freed) immediately by that call. If you
+are destroying a series of fields in a refcounted object, and
+any of the unref calls might possibly result in immediate destruction,
+you can first increment the count to prevent such behavior, then
+after the last test, decrement the pointer to allow the object
+to be destroyed, if the refcount would be zero.
+
+Example:
+
+ dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
+
+ ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
+
+ *//* Unlink us from the owner (channel) if we have one *//*
+ if (dialog->owner) {
+ if (lockowner)
+ ast_channel_lock(dialog->owner);
+ ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
+ dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
+ if (lockowner)
+ ast_channel_unlock(dialog->owner);
+ }
+ if (dialog->registry) {
+ if (dialog->registry->call == dialog)
+ dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
+ dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
+ }
+ ...
+ dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
+
+In the above code, the ao2_t_unlink could end up destroying the dialog
+object; if this happens, then the subsequent usages of the dialog
+pointer could result in a core dump. So, we 'bump' the
+count upwards before beginning, and then decrementing the count when
+we are finished. This is analogous to 'locking' or 'protecting' operations
+for a short while.
+
+4. One of the most insidious problems I've run into when converting
+code to do ref counted automatic destruction, is in the destruction
+routines. Where a "destroy" routine had previously been called to
+get rid of an object in non-refcounted code, the new regime demands
+that you tear that "destroy" routine into two pieces, one that will
+tear down the links and 'unref' them, and the other to actually free
+and reset fields. A destroy routine that does any reference deletion
+for its own object, will never be called. Another insidious problem
+occurs in mutually referenced structures. As an example, a dialog contains
+a pointer to a peer, and a peer contains a pointer to a dialog. Watch
+out that the destruction of one doesn't depend on the destruction of the
+other, as in this case a dependency loop will result in neither being
+destroyed!
+
+Given the above, you should be ready to do a good job!
+
+murf
+
+*/
+
+
+
/*! \brief
* Typedef for an object destructor. This is called just before freeing
* the memory for the object. It is passed a pointer to the user-defined
@@ -160,7 +396,22 @@ typedef void (*ao2_destructor_fn)(void *);
* - the returned pointer cannot be free()'d or realloc()'ed;
* rather, we just call ao2_ref(o, -1);
*/
-void *ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
+
+#ifdef REF_DEBUG
+
+
+#define ao2_t_alloc(arg1, arg2, arg3) _ao2_alloc_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_alloc(arg1, arg2) _ao2_alloc_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#else
+
+#define ao2_t_alloc(arg1,arg2,arg3) _ao2_alloc((arg1), (arg2))
+#define ao2_alloc(arg1,arg2) _ao2_alloc((arg1), (arg2))
+
+#endif
+void *_ao2_alloc_debug(const size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname);
+void *_ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
+
/*! \brief
* Reference/unreference an object and return the old refcount.
@@ -182,12 +433,21 @@ void *ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
* can go away is when we release our reference, and it is
* the last one in existence.
*/
-int ao2_ref(void *o, int delta);
+
+#ifdef REF_DEBUG
+#define ao2_t_ref(arg1,arg2,arg3) _ao2_ref_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_ref(arg1,arg2) _ao2_ref_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_ref(arg1,arg2,arg3) _ao2_ref((arg1), (arg2))
+#define ao2_ref(arg1,arg2) _ao2_ref((arg1), (arg2))
+#endif
+int _ao2_ref_debug(void *o, int delta, char *tag, char *file, int line, const char *funcname);
+int _ao2_ref(void *o, int delta);
/*! \brief
* Lock an object.
*
- * \param a A pointer to the object we want lock.
+ * \param a A pointer to the object we want to lock.
* \return 0 on success, other values on error.
*/
int ao2_lock(void *a);
@@ -200,8 +460,28 @@ int ao2_lock(void *a);
*/
int ao2_unlock(void *a);
-/*!
+/*! \brief
+ * Try locking-- (don't block if fail)
+ *
+ * \param a A pointer to the object we want to lock.
+ * \return 0 on success, other values on error.
+ */
+int ao2_trylock(void *a);
+
+/*! \brief
+ * Return the lock address of an object
*
+ * \param a A pointer to the object we want.
+ * \return the address of the lock, else NULL.
+ *
+ * This function comes in handy mainly for debugging locking
+ * situations, where the locking trace code reports the
+ * lock address, this allows you to correlate against
+ * object address, to match objects to reported locks.
+ */
+void *ao2_object_get_lockaddr(void *obj);
+
+/*!
\page AstObj2_Containers AstObj2 Containers
Containers are data structures meant to store several objects,
@@ -217,17 +497,44 @@ Operations on container include:
- c = \b ao2_container_alloc(size, cmp_fn, hash_fn)
allocate a container with desired size and default compare
and hash function
+ -The compare function returns an int, which
+ can be 0 for not found, CMP_STOP to stop end a traversal,
+ or CMP_MATCH if they are equal
+ -The hash function returns an int. The hash function
+ takes two argument, the object pointer and a flags field,
- \b ao2_find(c, arg, flags)
returns zero or more element matching a given criteria
- (specified as arg). Flags indicate how many results we
- want (only one or all matching entries), and whether we
- should unlink the object from the container.
+ (specified as arg). 'c' is the container pointer. Flags
+ can be:
+ OBJ_UNLINK - to remove the object, once found, from the container.
+ OBJ_NODATA - don't return the object if found (no ref count change)
+ OBJ_MULTIPLE - don't stop at first match (not implemented)
+ OBJ_POINTER - if set, 'arg' is an object pointer, and a hashtable
+ search will be done. If not, a traversal is done.
- \b ao2_callback(c, flags, fn, arg)
apply fn(obj, arg) to all objects in the container.
Similar to find. fn() can tell when to stop, and
do anything with the object including unlinking it.
+ - c is the container;
+ - flags can be
+ OBJ_UNLINK - to remove the object, once found, from the container.
+ OBJ_NODATA - don't return the object if found (no ref count change)
+ OBJ_MULTIPLE - don't stop at first match (not implemented)
+ OBJ_POINTER - if set, 'arg' is an object pointer, and a hashtable
+ search will be done. If not, a traversal is done through
+ all the hashtable 'buckets'..
+ - fn is a func that returns int, and takes 3 args:
+ (void *obj, void *arg, int flags);
+ obj is an object
+ arg is the same as arg passed into ao2_callback
+ flags is the same as flags passed into ao2_callback
+ fn returns:
+ 0: no match, keep going
+ CMP_STOP: stop search, no match
+ CMP_MATCH: This object is matched.
+
Note that the entire operation is run with the container
locked, so noone else can change its content while we work on it.
However, we pay this with the fact that doing
@@ -343,8 +650,19 @@ struct ao2_container;
*
* destructor is set implicitly.
*/
-struct ao2_container *ao2_container_alloc(const uint n_buckets,
- ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
+
+#ifdef REF_DEBUG
+#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) _ao2_container_alloc_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_container_alloc(arg1,arg2,arg3) _ao2_container_alloc_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_container_alloc(arg1,arg2,arg3,arg4) _ao2_container_alloc((arg1), (arg2), (arg3))
+#define ao2_container_alloc(arg1,arg2,arg3) _ao2_container_alloc((arg1), (arg2), (arg3))
+#endif
+struct ao2_container *_ao2_container_alloc(const uint n_buckets,
+ ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
+struct ao2_container *_ao2_container_alloc_debug(const uint n_buckets,
+ ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
+ char *tag, char *file, int line, const char *funcname);
/*! \brief
* Returns the number of elements in a container.
@@ -376,7 +694,16 @@ int ao2_container_count(struct ao2_container *c);
* \note This function automatically increases the reference count to account
* for the reference that the container now holds to the object.
*/
-void *ao2_link(struct ao2_container *c, void *newobj);
+#ifdef REF_DEBUG
+
+#define ao2_t_link(arg1, arg2, arg3) _ao2_link_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_link(arg1, arg2) _ao2_link_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_link(arg1, arg2, arg3) _ao2_link((arg1), (arg2))
+#define ao2_link(arg1, arg2) _ao2_link((arg1), (arg2))
+#endif
+void *_ao2_link_debug(struct ao2_container *c, void *new_obj, char *tag, char *file, int line, const char *funcname);
+void *_ao2_link(struct ao2_container *c, void *newobj);
/*!
* \brief Remove an object from the container
@@ -391,9 +718,19 @@ void *ao2_link(struct ao2_container *c, void *newobj);
* be called.
*
* \note If the object gets unlinked from the container, the container's
- * reference to the object will be automatically released.
+ * reference to the object will be automatically released. (The
+ * refcount will be decremented).
*/
-void *ao2_unlink(struct ao2_container *c, void *obj);
+#ifdef REF_DEBUG
+#define ao2_t_unlink(arg1, arg2, arg3) _ao2_unlink_debug((arg1), (arg2), (arg3), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_unlink(arg1, arg2) _ao2_unlink_debug((arg1), (arg2), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_unlink(arg1, arg2, arg3) _ao2_unlink((arg1), (arg2))
+#define ao2_unlink(arg1, arg2) _ao2_unlink((arg1), (arg2))
+#endif
+void *_ao2_unlink_debug(struct ao2_container *c, void *obj, char *tag, char *file, int line, const char *funcname);
+void *_ao2_unlink(struct ao2_container *c, void *obj);
+
/*! \brief Used as return value if the flag OBJ_MULTIPLE is set */
struct ao2_list {
@@ -407,10 +744,30 @@ struct ao2_list {
* in a container, as described below.
*
* \param c A pointer to the container to operate on.
- * \param arg passed to the callback.
* \param flags A set of flags specifying the operation to perform,
partially used by the container code, but also passed to
the callback.
+ - If OBJ_NODATA is set, ao2_callback will return NULL. No refcounts
+ of any of the traversed objects will be incremented.
+ On the converse, if it is NOT set (the default), The ref count
+ of each object for which CMP_MATCH was set will be incremented,
+ and you will have no way of knowing which those are, until
+ the multiple-object-return functionality is implemented.
+ - If OBJ_POINTER is set, the traversed items will be restricted
+ to the objects in the bucket that the object key hashes to.
+ * \param cb_fn A function pointer, that will be called on all
+ objects, to see if they match. This function returns CMP_MATCH
+ if the object is matches the criteria; CMP_STOP if the traversal
+ should immediately stop, or both (via bitwise ORing), if you find a
+ match and want to end the traversal, and 0 if the object is not a match,
+ but the traversal should continue. This is the function that is applied
+ to each object traversed. It's arguments are:
+ (void *obj, void *arg, int flags), where:
+ obj is an object
+ arg is the same as arg passed into ao2_callback
+ flags is the same as flags passed into ao2_callback (flags are
+ also used by ao2_callback).
+ * \param arg passed to the callback.
* \return A pointer to the object found/marked,
* a pointer to a list of objects matching comparison function,
* NULL if not found.
@@ -459,14 +816,32 @@ struct ao2_list {
* \note When the returned object is no longer in use, ao2_ref() should
* be used to free the additional reference possibly created by this function.
*/
-void *ao2_callback(struct ao2_container *c,
- enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg);
+#ifdef REF_DEBUG
+#define ao2_t_callback(arg1,arg2,arg3,arg4,arg5) _ao2_callback_debug((arg1), (arg2), (arg3), (arg4), (arg5), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_callback(arg1,arg2,arg3,arg4) _ao2_callback_debug((arg1), (arg2), (arg3), (arg4), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_callback(arg1,arg2,arg3,arg4,arg5) _ao2_callback((arg1), (arg2), (arg3), (arg4))
+#define ao2_callback(arg1,arg2,arg3,arg4) _ao2_callback((arg1), (arg2), (arg3), (arg4))
+#endif
+void *_ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg, char *tag,
+ char *file, int line, const char *funcname);
+void *_ao2_callback(struct ao2_container *c,
+ enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg);
/*! ao2_find() is a short hand for ao2_callback(c, flags, c->cmp_fn, arg)
* XXX possibly change order of arguments ?
*/
-void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags);
+#ifdef REF_DEBUG
+#define ao2_t_find(arg1,arg2,arg3,arg4) _ao2_find_debug((arg1), (arg2), (arg3), (arg4), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_find(arg1,arg2,arg3) _ao2_find_debug((arg1), (arg2), (arg3), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_find(arg1,arg2,arg3,arg4) _ao2_find((arg1), (arg2), (arg3))
+#define ao2_find(arg1,arg2,arg3) _ao2_find((arg1), (arg2), (arg3))
+#endif
+void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname);
+void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags);
/*! \brief
*
@@ -559,9 +934,20 @@ struct ao2_iterator {
uint version;
};
-struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags);
+/* the flags field can contain F_AO2I_DONTLOCK, which will prevent
+ ao2_iterator_next calls from locking the container while it
+ searches for the next pointer */
-void *ao2_iterator_next(struct ao2_iterator *a);
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags);
+#ifdef REF_DEBUG
+#define ao2_t_iterator_next(arg1, arg2) _ao2_iterator_next_debug((arg1), (arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_iterator_next(arg1) _ao2_iterator_next_debug((arg1), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#else
+#define ao2_t_iterator_next(arg1, arg2) _ao2_iterator_next((arg1))
+#define ao2_iterator_next(arg1) _ao2_iterator_next((arg1))
+#endif
+void *_ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname);
+void *_ao2_iterator_next(struct ao2_iterator *a);
/* extra functions */
void ao2_bt(void); /* backtrace */