summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/astobj2.h165
-rw-r--r--include/asterisk/lock.h2
-rw-r--r--main/astobj2.c781
-rw-r--r--main/format.c97
-rw-r--r--main/format_cap.c67
5 files changed, 742 insertions, 370 deletions
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index 49b2a05f2..738b773cb 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -390,12 +390,24 @@ murf
*/
typedef void (*ao2_destructor_fn)(void *);
+/*! \brief Options available when allocating an ao2 object. */
+enum ao2_alloc_opts {
+ /*! The ao2 object has a recursive mutex lock associated with it. */
+ AO2_ALLOC_OPT_LOCK_MUTEX = (0 << 0),
+ /*! The ao2 object has a non-recursive read/write lock associated with it. */
+ AO2_ALLOC_OPT_LOCK_RWLOCK = (1 << 0),
+ /*! The ao2 object has no lock associated with it. */
+ AO2_ALLOC_OPT_LOCK_NOLOCK = (2 << 0),
+ /*! The ao2 object locking option field mask. */
+ AO2_ALLOC_OPT_LOCK_MASK = (3 << 0),
+};
/*!
* \brief Allocate and initialize an object.
*
* \param data_size The sizeof() of the user-defined structure.
* \param destructor_fn The destructor function (can be NULL)
+ * \param options The ao2 object options (See enum ao2_alloc_opts)
* \param debug_msg An ao2 object debug tracing message.
* \return A pointer to user-data.
*
@@ -413,30 +425,45 @@ typedef void (*ao2_destructor_fn)(void *);
#if defined(REF_DEBUG)
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+ __ao2_alloc_debug((data_size), (destructor_fn), (options), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+ __ao2_alloc_debug((data_size), (destructor_fn), (options), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
#define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
- __ao2_alloc_debug((data_size), (destructor_fn), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+ __ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
#define ao2_alloc(data_size, destructor_fn) \
- __ao2_alloc_debug((data_size), (destructor_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+ __ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
#elif defined(__AST_DEBUG_MALLOC)
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+ __ao2_alloc_debug((data_size), (destructor_fn), (options), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+ __ao2_alloc_debug((data_size), (destructor_fn), (options), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
#define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
- __ao2_alloc_debug((data_size), (destructor_fn), (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+ __ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, (debug_msg), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
#define ao2_alloc(data_size, destructor_fn) \
- __ao2_alloc_debug((data_size), (destructor_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+ __ao2_alloc_debug((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
#else
+#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg) \
+ __ao2_alloc((data_size), (destructor_fn), (options))
+#define ao2_alloc_options(data_size, destructor_fn, options) \
+ __ao2_alloc((data_size), (destructor_fn), (options))
+
#define ao2_t_alloc(data_size, destructor_fn, debug_msg) \
- __ao2_alloc((data_size), (destructor_fn))
+ __ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX)
#define ao2_alloc(data_size, destructor_fn) \
- __ao2_alloc((data_size), (destructor_fn))
+ __ao2_alloc((data_size), (destructor_fn), AO2_ALLOC_OPT_LOCK_MUTEX)
#endif
-void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const char *tag,
+void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
const char *file, int line, const char *funcname, int ref_debug);
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn);
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options);
/*! @} */
@@ -476,19 +503,31 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn);
#endif
-int __ao2_ref_debug(void *o, int delta, const char *tag, char *file, int line, const char *funcname);
+int __ao2_ref_debug(void *o, int delta, const char *tag, const char *file, int line, const char *funcname);
int __ao2_ref(void *o, int delta);
/*! @} */
+/*! \brief Which lock to request. */
+enum ao2_lock_req {
+ /*! Request the mutex lock be acquired. */
+ AO2_LOCK_REQ_MUTEX,
+ /*! Request the read lock be acquired. */
+ AO2_LOCK_REQ_RDLOCK,
+ /*! Request the write lock be acquired. */
+ AO2_LOCK_REQ_WRLOCK,
+};
+
/*! \brief
* Lock an object.
*
* \param a A pointer to the object we want to lock.
* \return 0 on success, other values on error.
*/
-int __ao2_lock(void *a, const char *file, const char *func, int line, const char *var);
-#define ao2_lock(a) __ao2_lock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+int __ao2_lock(void *a, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var);
+#define ao2_lock(a) __ao2_lock(a, AO2_LOCK_REQ_MUTEX, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_rdlock(a) __ao2_lock(a, AO2_LOCK_REQ_RDLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_wrlock(a) __ao2_lock(a, AO2_LOCK_REQ_WRLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
/*! \brief
* Unlock an object.
@@ -505,8 +544,10 @@ int __ao2_unlock(void *a, const char *file, const char *func, int line, const ch
* \param a A pointer to the object we want to lock.
* \return 0 on success, other values on error.
*/
-int __ao2_trylock(void *a, const char *file, const char *func, int line, const char *var);
-#define ao2_trylock(a) __ao2_trylock(a, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+int __ao2_trylock(void *a, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var);
+#define ao2_trylock(a) __ao2_trylock(a, AO2_LOCK_REQ_MUTEX, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_tryrdlock(a) __ao2_trylock(a, AO2_LOCK_REQ_RDLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
+#define ao2_trywrlock(a) __ao2_trylock(a, AO2_LOCK_REQ_WRLOCK, __FILE__, __PRETTY_FUNCTION__, __LINE__, #a)
/*!
* \brief Return the mutex lock address of an object
@@ -693,9 +734,17 @@ enum search_flags {
*/
OBJ_CONTINUE = (1 << 4),
/*!
- * \brief By using this flag, the ao2_container being searched will _NOT_
- * be locked. Only use this flag if the ao2_container is being protected
- * by another mechanism other that the internal ao2_lock.
+ * \brief Assume that the ao2_container is already locked.
+ *
+ * \note For ao2_containers that have mutexes, no locking will
+ * be done.
+ *
+ * \note For ao2_containers that have RWLOCKs, the lock will be
+ * promoted to write mode as needed. The lock will be returned
+ * to the original locked state.
+ *
+ * \note Only use this flag if the ao2_container is manually
+ * locked already.
*/
OBJ_NOLOCK = (1 << 5),
/*!
@@ -732,6 +781,7 @@ struct ao2_container;
* We allocate space for a struct astobj_container, struct container
* and the buckets[] array.
*
+ * \param options Container ao2 object options (See enum ao2_alloc_opts)
* \param n_buckets Number of buckets for hash
* \param hash_fn Pointer to a function computing a hash value.
* \param cmp_fn Pointer to a compare function used by ao2_find. (NULL to match everything)
@@ -744,32 +794,47 @@ struct ao2_container;
#if defined(REF_DEBUG)
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+ __ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+ __ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
#define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
- __ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+ __ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
#define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
- __ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+ __ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
#elif defined(__AST_DEBUG_MALLOC)
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+ __ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+ __ao2_container_alloc_debug((options), (n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
#define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
- __ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+ __ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
#define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
- __ao2_container_alloc_debug((n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+ __ao2_container_alloc_debug(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
#else
+#define ao2_t_container_alloc_options(options, n_buckets, hash_fn, cmp_fn, tag) \
+ __ao2_container_alloc((options), (n_buckets), (hash_fn), (cmp_fn))
+#define ao2_container_alloc_options(options, n_buckets, hash_fn, cmp_fn) \
+ __ao2_container_alloc((options), (n_buckets), (hash_fn), (cmp_fn))
+
#define ao2_t_container_alloc(n_buckets, hash_fn, cmp_fn, tag) \
- __ao2_container_alloc((n_buckets), (hash_fn), (cmp_fn))
+ __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn))
#define ao2_container_alloc(n_buckets, hash_fn, cmp_fn) \
- __ao2_container_alloc((n_buckets), (hash_fn), (cmp_fn))
+ __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_MUTEX, (n_buckets), (hash_fn), (cmp_fn))
#endif
-struct ao2_container *__ao2_container_alloc(unsigned int n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn);
-struct ao2_container *__ao2_container_alloc_debug(unsigned int n_buckets,
- ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
- const char *tag, char *file, int line, const char *funcname, int ref_debug);
+struct ao2_container *__ao2_container_alloc(unsigned int options,
+ unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
+struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
+ unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
+ const char *tag, const char *file, int line, const char *funcname, int ref_debug);
/*! \brief
* Returns the number of elements in a container.
@@ -810,7 +875,7 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
* \retval NULL on error.
*/
struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags);
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, char *file, int line, const char *funcname, int ref_debug);
+struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *funcname, int ref_debug);
#if defined(REF_DEBUG)
#define ao2_t_container_clone(orig, flags, tag) __ao2_container_clone_debug(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
@@ -843,6 +908,7 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
*
* \param container The container to operate on.
* \param obj The object to be added.
+ * \param flags search_flags to control linking the object. (OBJ_NOLOCK)
* \param tag used for debugging.
*
* \retval NULL on errors.
@@ -860,20 +926,20 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
#define ao2_t_link(container, obj, tag) __ao2_link_debug((container), (obj), 0, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define ao2_link(container, obj) __ao2_link_debug((container), (obj), 0, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_t_link_nolock(container, obj, tag) __ao2_link_debug((container), (obj), OBJ_NOLOCK, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_link_nolock(container, obj) __ao2_link_debug((container), (obj), OBJ_NOLOCK, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_t_link_flags(container, obj, flags, tag) __ao2_link_debug((container), (obj), (flags), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_link_flags(container, obj, flags) __ao2_link_debug((container), (obj), (flags), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
#define ao2_t_link(container, obj, tag) __ao2_link((container), (obj), 0)
#define ao2_link(container, obj) __ao2_link((container), (obj), 0)
-#define ao2_t_link_nolock(container, obj, tag) __ao2_link((container), (obj), OBJ_NOLOCK)
-#define ao2_link_nolock(container, obj) __ao2_link((container), (obj), OBJ_NOLOCK)
+#define ao2_t_link_flags(container, obj, flags, tag) __ao2_link((container), (obj), (flags))
+#define ao2_link_flags(container, obj, flags) __ao2_link((container), (obj), (flags))
#endif
-void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname);
void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
/*!
@@ -881,6 +947,7 @@ void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
*
* \param container The container to operate on.
* \param obj The object to unlink.
+ * \param flags search_flags to control unlinking the object. (OBJ_NOLOCK)
* \param tag used for debugging.
*
* \retval NULL, always
@@ -898,20 +965,20 @@ void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
#define ao2_t_unlink(container, obj, tag) __ao2_unlink_debug((container), (obj), 0, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define ao2_unlink(container, obj) __ao2_unlink_debug((container), (obj), 0, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_t_unlink_nolock(container, obj, tag) __ao2_unlink_debug((container), (obj), OBJ_NOLOCK, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define ao2_unlink_nolock(container, obj) __ao2_unlink_debug((container), (obj), OBJ_NOLOCK, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_t_unlink_flags(container, obj, flags, tag) __ao2_unlink_debug((container), (obj), (flags), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ao2_unlink_flags(container, obj, flags) __ao2_unlink_debug((container), (obj), (flags), "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
#define ao2_t_unlink(container, obj, tag) __ao2_unlink((container), (obj), 0)
#define ao2_unlink(container, obj) __ao2_unlink((container), (obj), 0)
-#define ao2_t_unlink_nolock(container, obj, tag) __ao2_unlink((container), (obj), OBJ_NOLOCK)
-#define ao2_unlink_nolock(container, obj) __ao2_unlink((container), (obj), OBJ_NOLOCK)
+#define ao2_t_unlink_flags(container, obj, flags, tag) __ao2_unlink((container), (obj), (flags))
+#define ao2_unlink_flags(container, obj, flags) __ao2_unlink((container), (obj), (flags))
#endif
-void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, const char *file, int line, const char *funcname);
void *__ao2_unlink(struct ao2_container *c, void *obj, int flags);
@@ -1010,7 +1077,7 @@ void *__ao2_unlink(struct ao2_container *c, void *obj, int flags);
#endif
void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg, const char *tag, char *file, int line,
+ ao2_callback_fn *cb_fn, void *arg, const char *tag, const 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);
@@ -1048,7 +1115,7 @@ void *__ao2_callback(struct ao2_container *c, enum search_flags flags, ao2_callb
#endif
void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
- ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, char *file,
+ ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
int line, const char *funcname);
void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
ao2_callback_data_fn *cb_fn, void *arg, void *data);
@@ -1073,7 +1140,7 @@ void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
#endif
void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
- const char *tag, char *file, int line, const char *funcname);
+ const char *tag, const char *file, int line, const char *funcname);
void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags);
/*! \brief
@@ -1176,8 +1243,18 @@ struct ao2_iterator {
* of the iterator.
*/
enum ao2_iterator_flags {
- /*! Prevents ao2_iterator_next() from locking the container
- * while retrieving the next object from it.
+ /*!
+ * \brief Assume that the ao2_container is already locked.
+ *
+ * \note For ao2_containers that have mutexes, no locking will
+ * be done.
+ *
+ * \note For ao2_containers that have RWLOCKs, the lock will be
+ * promoted to write mode as needed. The lock will be returned
+ * to the original locked state.
+ *
+ * \note Only use this flag if the ao2_container is manually
+ * locked already.
*/
AO2_ITERATOR_DONTLOCK = (1 << 0),
/*! Indicates that the iterator was dynamically allocated by
@@ -1234,7 +1311,7 @@ void ao2_iterator_destroy(struct ao2_iterator *i);
#endif
-void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, char *file, int line, const char *funcname);
+void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname);
void *__ao2_iterator_next(struct ao2_iterator *a);
/* extra functions */
diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h
index fbcbecccf..943c7ffe6 100644
--- a/include/asterisk/lock.h
+++ b/include/asterisk/lock.h
@@ -352,7 +352,7 @@ int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, in
if (__res2) { \
ast_log(LOG_WARNING, "Could not unlock channel '%s': %s. {{{Originally locked at %s line %d: (%s) '%s'}}} I will NOT try to relock.\n", #chan, strerror(__res2), __filename, __lineno, __func, __mutex_name); \
} else { \
- __ao2_lock(chan, __filename, __func, __lineno, __mutex_name); \
+ __ao2_lock(chan, AO2_LOCK_REQ_MUTEX, __filename, __func, __lineno, __mutex_name); \
} \
} \
} while (0)
diff --git a/main/astobj2.c b/main/astobj2.c
index 62e76dbf6..d0643fe5b 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -34,20 +34,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*!
* astobj2 objects are always preceded by this data structure,
- * which contains a lock, a reference counter,
- * the flags and a pointer to a destructor.
+ * which contains a reference counter,
+ * option flags and a pointer to a destructor.
* The refcount is used to decide when it is time to
* invoke the destructor.
* The magic number is used for consistency check.
- * XXX the lock is not always needed, and its initialization may be
- * expensive. Consider making it external.
*/
struct __priv_data {
- ast_mutex_t lock;
int ref_counter;
ao2_destructor_fn destructor_fn;
- /*! for stats */
+ /*! User data size for stats */
size_t data_size;
+ /*! The ao2 object option flags */
+ uint32_t options;
/*! magic number. This is used to verify that a pointer passed in is a
* valid astobj2 */
uint32_t magic;
@@ -64,9 +63,29 @@ struct astobj2 {
void *user_data[0];
};
-#ifdef AST_DEVMODE
-/* #define AO2_DEBUG 1 */
-#endif
+struct ao2_lock_priv {
+ ast_mutex_t lock;
+};
+
+/* AstObj2 with recursive lock. */
+struct astobj2_lock {
+ struct ao2_lock_priv mutex;
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
+struct ao2_rwlock_priv {
+ ast_rwlock_t lock;
+ /*! Count of the number of threads holding a lock on this object. -1 if it is the write lock. */
+ int num_lockers;
+};
+
+/* AstObj2 with RW lock. */
+struct astobj2_rwlock {
+ struct ao2_rwlock_priv rwlock;
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
#ifdef AO2_DEBUG
struct ao2_stats {
@@ -102,6 +121,12 @@ void ao2_bt(void)
}
#endif
+#define INTERNAL_OBJ_MUTEX(user_data) \
+ ((struct astobj2_lock *) (((char *) (user_data)) - sizeof(struct astobj2_lock)))
+
+#define INTERNAL_OBJ_RWLOCK(user_data) \
+ ((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
+
/*!
* \brief convert from a pointer _p to a user-defined object
*
@@ -119,7 +144,7 @@ static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
if (AO2_MAGIC != (p->priv_data.magic) ) {
ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
- p = NULL;
+ return NULL;
}
return p;
@@ -137,123 +162,269 @@ enum ao2_callback_type {
*/
#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
-/* the underlying functions common to debug and non-debug versions */
-
-static int internal_ao2_ref(void *user_data, const int delta);
-static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn);
-static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func);
-static void *internal_ao2_callback(struct ao2_container *c,
- const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
- const char *tag, char *file, int line, const char *funcname);
-static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_entry **q);
-
-int __ao2_lock(void *user_data, const char *file, const char *func, int line, const char *var)
+int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
{
- struct astobj2 *p = INTERNAL_OBJ(user_data);
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ struct astobj2_lock *obj_mutex;
+ struct astobj2_rwlock *obj_rwlock;
+ int res = 0;
- if (p == NULL)
+ if (obj == NULL) {
return -1;
+ }
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+ res = __ast_pthread_mutex_lock(file, line, func, var, &obj_mutex->mutex.lock);
+#ifdef AO2_DEBUG
+ if (!res) {
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+ }
+#endif
+ break;
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+ obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+ switch (lock_how) {
+ case AO2_LOCK_REQ_MUTEX:
+ case AO2_LOCK_REQ_WRLOCK:
+ res = __ast_rwlock_wrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+ if (!res) {
+ ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+ }
+ break;
+ case AO2_LOCK_REQ_RDLOCK:
+ res = __ast_rwlock_rdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+ if (!res) {
+ ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
#ifdef AO2_DEBUG
- ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
#endif
+ }
+ break;
+ }
+ break;
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+ /* The ao2 object has no lock. */
+ break;
+ default:
+ ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+ user_data);
+ return -1;
+ }
- return __ast_pthread_mutex_lock(file, line, func, var, &p->priv_data.lock);
+ return res;
}
int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
{
- struct astobj2 *p = INTERNAL_OBJ(user_data);
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ struct astobj2_lock *obj_mutex;
+ struct astobj2_rwlock *obj_rwlock;
+ int res = 0;
+ int current_value;
- if (p == NULL)
+ if (obj == NULL) {
return -1;
+ }
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+ res = __ast_pthread_mutex_unlock(file, line, func, var, &obj_mutex->mutex.lock);
#ifdef AO2_DEBUG
- ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+ if (!res) {
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+ }
#endif
-
- return __ast_pthread_mutex_unlock(file, line, func, var, &p->priv_data.lock);
+ break;
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+ obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+
+ current_value = ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1) - 1;
+ if (current_value < 0) {
+ /* It was a WRLOCK that we are unlocking. Fix the count. */
+ ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -current_value);
+ }
+ res = __ast_rwlock_unlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+#ifdef AO2_DEBUG
+ if (!res) {
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+ }
+#endif
+ break;
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+ /* The ao2 object has no lock. */
+ break;
+ default:
+ ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+ user_data);
+ res = -1;
+ break;
+ }
+ return res;
}
-int __ao2_trylock(void *user_data, const char *file, const char *func, int line, const char *var)
+int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
{
- struct astobj2 *p = INTERNAL_OBJ(user_data);
- int ret;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ struct astobj2_lock *obj_mutex;
+ struct astobj2_rwlock *obj_rwlock;
+ int res = 0;
- if (p == NULL)
+ if (obj == NULL) {
return -1;
- ret = __ast_pthread_mutex_trylock(file, line, func, var, &p->priv_data.lock);
+ }
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+ res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock);
#ifdef AO2_DEBUG
- if (!ret)
- ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+ if (!res) {
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+ }
#endif
- return ret;
-}
-
-void *ao2_object_get_lockaddr(void *obj)
-{
- struct astobj2 *p = INTERNAL_OBJ(obj);
+ break;
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+ obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+ switch (lock_how) {
+ case AO2_LOCK_REQ_MUTEX:
+ case AO2_LOCK_REQ_WRLOCK:
+ res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+ if (!res) {
+ ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+ }
+ break;
+ case AO2_LOCK_REQ_RDLOCK:
+ res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
+ if (!res) {
+ ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+ }
+ break;
+ }
+ break;
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+ /* The ao2 object has no lock. */
+ return 0;
+ default:
+ ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
+ user_data);
+ return -1;
+ }
- if (p == NULL)
- return NULL;
- return &p->priv_data.lock;
+ return res;
}
-/*
- * The argument is a pointer to the user portion.
+/*!
+ * \internal
+ * \brief Adjust an object's lock to the requested level.
+ *
+ * \param user_data An ao2 object to adjust lock level.
+ * \param lock_how What level to adjust lock.
+ * \param keep_stronger TRUE if keep original lock level if it is stronger.
+ *
+ * \pre The ao2 object is already locked.
+ *
+ * \details
+ * An ao2 object with a RWLOCK will have its lock level adjusted
+ * to the specified level if it is not already there. An ao2
+ * object with a different type of lock is not affected.
+ *
+ * \return Original lock level.
*/
-
-
-int __ao2_ref_debug(void *user_data, const int delta, const char *tag, char *file, int line, const char *funcname)
+static enum ao2_lock_req adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
{
struct astobj2 *obj = INTERNAL_OBJ(user_data);
-
- if (obj == NULL)
- return -1;
-
- if (delta != 0) {
- FILE *refo = fopen(REF_FILE, "a");
- if (refo) {
- fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
- delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
- fclose(refo);
+ struct astobj2_rwlock *obj_rwlock;
+ enum ao2_lock_req orig_lock;
+
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+ obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+ if (obj_rwlock->rwlock.num_lockers < 0) {
+ orig_lock = AO2_LOCK_REQ_WRLOCK;
+ } else {
+ orig_lock = AO2_LOCK_REQ_RDLOCK;
}
- }
- if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
- FILE *refo = fopen(REF_FILE, "a");
- if (refo) {
- fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
- fclose(refo);
+ switch (lock_how) {
+ case AO2_LOCK_REQ_MUTEX:
+ lock_how = AO2_LOCK_REQ_WRLOCK;
+ /* Fall through */
+ case AO2_LOCK_REQ_WRLOCK:
+ if (lock_how != orig_lock) {
+ /* Switch from read lock to write lock. */
+ ao2_unlock(user_data);
+ ao2_wrlock(user_data);
+ }
+ break;
+ case AO2_LOCK_REQ_RDLOCK:
+ if (!keep_stronger && lock_how != orig_lock) {
+ /* Switch from write lock to read lock. */
+ ao2_unlock(user_data);
+ ao2_rdlock(user_data);
+ }
+ break;
}
+ break;
+ default:
+ ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
+ /* Fall through */
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ orig_lock = AO2_LOCK_REQ_MUTEX;
+ break;
}
- return internal_ao2_ref(user_data, delta);
+
+ return orig_lock;
}
-int __ao2_ref(void *user_data, const int delta)
+void *ao2_object_get_lockaddr(void *user_data)
{
struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ struct astobj2_lock *obj_mutex;
- if (obj == NULL)
- return -1;
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+ return &obj_mutex->mutex.lock;
+ default:
+ break;
+ }
- return internal_ao2_ref(user_data, delta);
+ return NULL;
}
-static int internal_ao2_ref(void *user_data, const int delta)
+static int internal_ao2_ref(void *user_data, int delta, const char *file, int line, const char *funcname)
{
struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ struct astobj2_lock *obj_mutex;
+ struct astobj2_rwlock *obj_rwlock;
int current_value;
int ret;
- if (obj == NULL)
+ if (obj == NULL) {
return -1;
+ }
/* if delta is 0, just return the refcount */
- if (delta == 0)
- return (obj->priv_data.ref_counter);
+ if (delta == 0) {
+ return obj->priv_data.ref_counter;
+ }
/* we modify with an atomic operation the reference counter */
ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
@@ -263,56 +434,169 @@ static int internal_ao2_ref(void *user_data, const int delta)
ast_atomic_fetchadd_int(&ao2.total_refs, delta);
#endif
+ if (0 < current_value) {
+ /* The object still lives. */
+ return ret;
+ }
+
/* this case must never happen */
- if (current_value < 0)
- ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
+ if (current_value < 0) {
+ ast_log(__LOG_ERROR, file, line, funcname,
+ "Invalid refcount %d on ao2 object %p\n", current_value, user_data);
+ }
- if (current_value <= 0) { /* last reference, destroy the object */
- if (obj->priv_data.destructor_fn != NULL) {
- obj->priv_data.destructor_fn(user_data);
- }
+ /* last reference, destroy the object */
+ if (obj->priv_data.destructor_fn != NULL) {
+ obj->priv_data.destructor_fn(user_data);
+ }
- ast_mutex_destroy(&obj->priv_data.lock);
#ifdef AO2_DEBUG
- ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
- ast_atomic_fetchadd_int(&ao2.total_objects, -1);
+ ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
+ ast_atomic_fetchadd_int(&ao2.total_objects, -1);
#endif
- /* for safety, zero-out the astobj2 header and also the
+
+ switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
+ obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
+ ast_mutex_destroy(&obj_mutex->mutex.lock);
+
+ /*
+ * For safety, zero-out the astobj2_lock header and also the
+ * first word of the user-data, which we make sure is always
+ * allocated.
+ */
+ memset(obj_mutex, '\0', sizeof(*obj_mutex) + sizeof(void *) );
+ ast_free(obj_mutex);
+ break;
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+ obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
+ ast_rwlock_destroy(&obj_rwlock->rwlock.lock);
+
+ /*
+ * For safety, zero-out the astobj2_rwlock header and also the
* first word of the user-data, which we make sure is always
- * allocated. */
- memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
+ * allocated.
+ */
+ memset(obj_rwlock, '\0', sizeof(*obj_rwlock) + sizeof(void *) );
+ ast_free(obj_rwlock);
+ break;
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+ /*
+ * For safety, zero-out the astobj2 header and also the first
+ * word of the user-data, which we make sure is always
+ * allocated.
+ */
+ memset(obj, '\0', sizeof(*obj) + sizeof(void *) );
ast_free(obj);
+ break;
+ default:
+ ast_log(__LOG_ERROR, file, line, funcname,
+ "Invalid lock option on ao2 object %p\n", user_data);
+ break;
}
return ret;
}
-/*
- * We always alloc at least the size of a void *,
- * for debugging purposes.
- */
-static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, const char *file, int line, const char *funcname)
+int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *funcname)
+{
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (obj == NULL)
+ return -1;
+
+ if (delta != 0) {
+ FILE *refo = fopen(REF_FILE, "a");
+ if (refo) {
+ fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
+ delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
+ fclose(refo);
+ }
+ }
+ if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
+ FILE *refo = fopen(REF_FILE, "a");
+ if (refo) {
+ fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
+ fclose(refo);
+ }
+ }
+ return internal_ao2_ref(user_data, delta, file, line, funcname);
+}
+
+int __ao2_ref(void *user_data, int delta)
+{
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (obj == NULL)
+ return -1;
+
+ return internal_ao2_ref(user_data, delta, __FILE__, __LINE__, __FUNCTION__);
+}
+
+static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *file, int line, const char *funcname)
{
/* allocation */
struct astobj2 *obj;
+ struct astobj2_lock *obj_mutex;
+ struct astobj2_rwlock *obj_rwlock;
- if (data_size < sizeof(void *))
+ if (data_size < sizeof(void *)) {
+ /*
+ * We always alloc at least the size of a void *,
+ * for debugging purposes.
+ */
data_size = sizeof(void *);
+ }
+ switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
+ case AO2_ALLOC_OPT_LOCK_MUTEX:
#if defined(__AST_DEBUG_MALLOC)
- obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
+ obj_mutex = __ast_calloc(1, sizeof(*obj_mutex) + data_size, file, line, funcname);
#else
- obj = ast_calloc(1, sizeof(*obj) + data_size);
+ obj_mutex = ast_calloc(1, sizeof(*obj_mutex) + data_size);
#endif
+ if (obj_mutex == NULL) {
+ return NULL;
+ }
- if (obj == NULL)
+ ast_mutex_init(&obj_mutex->mutex.lock);
+ obj = (struct astobj2 *) &obj_mutex->priv_data;
+ break;
+ case AO2_ALLOC_OPT_LOCK_RWLOCK:
+#if defined(__AST_DEBUG_MALLOC)
+ obj_rwlock = __ast_calloc(1, sizeof(*obj_rwlock) + data_size, file, line, funcname);
+#else
+ obj_rwlock = ast_calloc(1, sizeof(*obj_rwlock) + data_size);
+#endif
+ if (obj_rwlock == NULL) {
+ return NULL;
+ }
+
+ ast_rwlock_init(&obj_rwlock->rwlock.lock);
+ obj = (struct astobj2 *) &obj_rwlock->priv_data;
+ break;
+ case AO2_ALLOC_OPT_LOCK_NOLOCK:
+#if defined(__AST_DEBUG_MALLOC)
+ obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
+#else
+ obj = ast_calloc(1, sizeof(*obj) + data_size);
+#endif
+ if (obj == NULL) {
+ return NULL;
+ }
+ break;
+ default:
+ /* Invalid option value. */
+ ast_log(__LOG_DEBUG, file, line, funcname, "Invalid lock option requested\n");
return NULL;
+ }
- ast_mutex_init(&obj->priv_data.lock);
- obj->priv_data.magic = AO2_MAGIC;
- obj->priv_data.data_size = data_size;
+ /* Initialize common ao2 values. */
obj->priv_data.ref_counter = 1;
obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
+ obj->priv_data.data_size = data_size;
+ obj->priv_data.options = options;
+ obj->priv_data.magic = AO2_MAGIC;
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_objects, 1);
@@ -324,14 +608,14 @@ static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_f
return EXTERNAL_OBJ(obj);
}
-void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const char *tag,
- const char *file, int line, const char *funcname, int ref_debug)
+void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
+ const char *file, int line, const char *funcname, int ref_debug)
{
/* allocation */
void *obj;
FILE *refo;
- if ((obj = internal_ao2_alloc(data_size, destructor_fn, file, line, funcname)) == NULL) {
+ if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, funcname)) == NULL) {
return NULL;
}
@@ -344,9 +628,9 @@ void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, const
return obj;
}
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options)
{
- return internal_ao2_alloc(data_size, destructor_fn, __FILE__, __LINE__, __FUNCTION__);
+ return internal_ao2_alloc(data_size, destructor_fn, options, __FILE__, __LINE__, __FUNCTION__);
}
@@ -356,6 +640,17 @@ static void container_destruct(void *c);
/* internal callback to destroy a container. */
static void container_destruct_debug(void *c);
+/*!
+ * A structure to create a linked list of entries,
+ * used within a bucket.
+ * XXX \todo this should be private to the container code
+ */
+struct bucket_entry {
+ AST_LIST_ENTRY(bucket_entry) entry;
+ int version;
+ struct astobj2 *astobj;/* pointer to internal data */
+};
+
/* each bucket in the container is a tailq. */
AST_LIST_HEAD_NOLOCK(bucket, bucket_entry);
@@ -410,14 +705,15 @@ static int hash_zero(const void *user_obj, const int flags)
/*
* A container is just an object, after all!
*/
-static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const unsigned int n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn)
+static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c,
+ unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
- if (!c)
+ if (!c) {
return NULL;
+ }
c->version = 1; /* 0 is a reserved value here */
c->n_buckets = hash_fn ? n_buckets : 1;
@@ -431,28 +727,27 @@ static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *
return c;
}
-struct ao2_container *__ao2_container_alloc_debug(unsigned int n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn, const char *tag, char *file, int line,
- const char *funcname, int ref_debug)
+struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
+ unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
+ const char *tag, const char *file, int line, const char *funcname, int ref_debug)
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
- const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+ unsigned int num_buckets = hash_fn ? n_buckets : 1;
size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
- struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname, ref_debug);
+ struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, options, tag, file, line, funcname, ref_debug);
return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
}
-struct ao2_container *__ao2_container_alloc(unsigned int n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn)
+struct ao2_container *__ao2_container_alloc(unsigned int options,
+ unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
-
const unsigned int num_buckets = hash_fn ? n_buckets : 1;
size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
- struct ao2_container *c = __ao2_alloc(container_size, container_destruct);
+ struct ao2_container *c = __ao2_alloc(container_size, container_destruct, options);
return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
}
@@ -465,77 +760,68 @@ int ao2_container_count(struct ao2_container *c)
return c->elements;
}
-/*!
- * A structure to create a linked list of entries,
- * used within a bucket.
- * XXX \todo this should be private to the container code
- */
-struct bucket_entry {
- AST_LIST_ENTRY(bucket_entry) entry;
- int version;
- struct astobj2 *astobj; /* pointer to internal data */
-};
-
/*
* link an object to a container
*/
-
-static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func)
+static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *funcname)
{
int i;
+ enum ao2_lock_req orig_lock;
/* create a new list entry */
struct bucket_entry *p;
struct astobj2 *obj = INTERNAL_OBJ(user_data);
- if (obj == NULL)
+ if (obj == NULL) {
return NULL;
+ }
- if (INTERNAL_OBJ(c) == NULL)
+ if (INTERNAL_OBJ(c) == NULL) {
return NULL;
+ }
p = ast_calloc(1, sizeof(*p));
- if (!p)
+ if (!p) {
return NULL;
+ }
i = abs(c->hash_fn(user_data, OBJ_POINTER));
- if (!(flags & OBJ_NOLOCK)) {
- ao2_lock(c);
+ if (flags & OBJ_NOLOCK) {
+ orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
+ } else {
+ ao2_wrlock(c);
+ orig_lock = AO2_LOCK_REQ_MUTEX;
}
+
i %= c->n_buckets;
p->astobj = obj;
p->version = ast_atomic_fetchadd_int(&c->version, 1);
AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
ast_atomic_fetchadd_int(&c->elements, 1);
- /* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */
- return p;
-}
-
-void *__ao2_link_debug(struct ao2_container *c, void *user_data, int flags, const char *tag, char *file, int line, const char *funcname)
-{
- struct bucket_entry *p = internal_ao2_link(c, user_data, flags, file, line, funcname);
-
- if (p) {
+ if (tag) {
__ao2_ref_debug(user_data, +1, tag, file, line, funcname);
- if (!(flags & OBJ_NOLOCK)) {
- ao2_unlock(c);
- }
+ } else {
+ __ao2_ref(user_data, +1);
}
+
+ if (flags & OBJ_NOLOCK) {
+ adjust_lock(c, orig_lock, 0);
+ } else {
+ ao2_unlock(c);
+ }
+
return p;
}
-void *__ao2_link(struct ao2_container *c, void *user_data, int flags)
+void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname)
{
- struct bucket_entry *p = internal_ao2_link(c, user_data, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ return internal_ao2_link(c, new_obj, flags, tag, file, line, funcname);
+}
- if (p) {
- __ao2_ref(user_data, +1);
- if (!(flags & OBJ_NOLOCK)) {
- ao2_unlock(c);
- }
- }
- return p;
+void *__ao2_link(struct ao2_container *c, void *new_obj, int flags)
+{
+ return internal_ao2_link(c, new_obj, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
}
/*!
@@ -550,14 +836,14 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags)
* Unlink an object from the container
* and destroy the associated * bucket_entry structure.
*/
-void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags, const char *tag,
- char *file, int line, const char *funcname)
+void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
+ const char *tag, const char *file, int line, const char *funcname)
{
- if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
+ if (INTERNAL_OBJ(user_data) == NULL) { /* safety check on the argument */
return NULL;
+ }
flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
-
__ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, funcname);
return NULL;
@@ -565,8 +851,9 @@ void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags, co
void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
{
- if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
+ if (INTERNAL_OBJ(user_data) == NULL) { /* safety check on the argument */
return NULL;
+ }
flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
__ao2_callback(c, flags, ao2_match_by_addr, user_data);
@@ -598,19 +885,21 @@ static int cb_true_data(void *user_data, void *arg, void *data, int flags)
* aren't an excessive load to the system, as the callback should not be
* called as often as, say, the ao2_ref func is called.
*/
-static void *internal_ao2_callback(struct ao2_container *c,
- const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
- const char *tag, char *file, int line, const char *funcname)
+static void *internal_ao2_callback(struct ao2_container *c, 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 *funcname)
{
int i, start, last; /* search boundaries */
+ enum ao2_lock_req orig_lock;
void *ret = NULL;
ao2_callback_fn *cb_default = NULL;
ao2_callback_data_fn *cb_withdata = NULL;
struct ao2_container *multi_container = NULL;
struct ao2_iterator *multi_iterator = NULL;
- if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
+ if (INTERNAL_OBJ(c) == NULL) { /* safety check on the argument */
return NULL;
+ }
/*
* This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
@@ -625,7 +914,8 @@ static void *internal_ao2_callback(struct ao2_container *c,
* is destroyed, the container will be automatically
* destroyed as well.
*/
- if (!(multi_container = __ao2_container_alloc(1, NULL, NULL))) {
+ multi_container = __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+ if (!multi_container) {
return NULL;
}
if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
@@ -657,10 +947,13 @@ static void *internal_ao2_callback(struct ao2_container *c,
* run the hash function. Otherwise, scan the whole container
* (this only for the time being. We need to optimize this.)
*/
- if ((flags & (OBJ_POINTER | OBJ_KEY))) /* we know hash can handle this case */
+ if ((flags & (OBJ_POINTER | OBJ_KEY))) {
+ /* we know hash can handle this case */
start = i = c->hash_fn(arg, flags & (OBJ_POINTER | OBJ_KEY)) % c->n_buckets;
- else /* don't know, let's scan all buckets */
+ } else {
+ /* don't know, let's scan all buckets */
start = i = -1; /* XXX this must be fixed later. */
+ }
/* determine the search boundaries: i..last-1 */
if (i < 0) {
@@ -672,9 +965,20 @@ static void *internal_ao2_callback(struct ao2_container *c,
last = i + 1;
}
-
- if (!(flags & OBJ_NOLOCK)) {
- ao2_lock(c); /* avoid modifications to the content */
+ /* avoid modifications to the content */
+ if (flags & OBJ_NOLOCK) {
+ if (flags & OBJ_UNLINK) {
+ orig_lock = adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
+ } else {
+ orig_lock = adjust_lock(c, AO2_LOCK_REQ_RDLOCK, 1);
+ }
+ } else {
+ orig_lock = AO2_LOCK_REQ_MUTEX;
+ if (flags & OBJ_UNLINK) {
+ ao2_wrlock(c);
+ } else {
+ ao2_rdlock(c);
+ }
}
for (; i < last ; i++) {
@@ -710,7 +1014,7 @@ static void *internal_ao2_callback(struct ao2_container *c,
}
}
- /* If we are in OBJ_MULTIPLE mode and OBJ_NODATE is off,
+ /* If we are in OBJ_MULTIPLE mode and OBJ_NODATA is off,
* link the object into the container that will hold the results.
*/
if (ret && (multi_container != NULL)) {
@@ -764,14 +1068,16 @@ static void *internal_ao2_callback(struct ao2_container *c,
}
}
- if (!(flags & OBJ_NOLOCK)) {
+ if (flags & OBJ_NOLOCK) {
+ adjust_lock(c, orig_lock, 0);
+ } else {
ao2_unlock(c);
}
/* if multi_container was created, we are returning multiple objects */
if (multi_container != NULL) {
*multi_iterator = ao2_iterator_init(multi_container,
- AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
+ AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
ao2_ref(multi_container, -1);
return multi_iterator;
} else {
@@ -780,7 +1086,7 @@ static void *internal_ao2_callback(struct ao2_container *c,
}
void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg, const char *tag, char *file, int line,
+ ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
const char *funcname)
{
return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, funcname);
@@ -792,16 +1098,15 @@ void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL);
}
-void *__ao2_callback_data_debug(struct ao2_container *c,
- enum search_flags flags,
- ao2_callback_data_fn *cb_fn, void *arg, void *data,
- const char *tag, char *file, int line, const char *funcname)
+void *__ao2_callback_data_debug(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 *funcname)
{
return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, funcname);
}
void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
- ao2_callback_data_fn *cb_fn, void *arg, void *data)
+ ao2_callback_data_fn *cb_fn, void *arg, void *data)
{
return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL);
}
@@ -809,7 +1114,8 @@ void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
/*!
* the find function just invokes the default callback with some reasonable flags.
*/
-void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, char *file, int line, const char *funcname)
+void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
+ const char *tag, const char *file, int line, const char *funcname)
{
void *arged = (void *) arg;/* Done to avoid compiler const warning */
@@ -854,26 +1160,39 @@ void ao2_iterator_destroy(struct ao2_iterator *i)
/*
* move to the next element in the container.
*/
-static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_entry **q)
+static void *internal_ao2_iterator_next(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname)
{
int lim;
+ enum ao2_lock_req orig_lock;
struct bucket_entry *p = NULL;
- void *ret = NULL;
-
- *q = NULL;
+ void *ret;
- if (INTERNAL_OBJ(a->c) == NULL)
+ if (INTERNAL_OBJ(a->c) == NULL) {
return NULL;
+ }
- if (!(a->flags & AO2_ITERATOR_DONTLOCK))
- ao2_lock(a->c);
+ if (a->flags & AO2_ITERATOR_DONTLOCK) {
+ if (a->flags & AO2_ITERATOR_UNLINK) {
+ orig_lock = adjust_lock(a->c, AO2_LOCK_REQ_WRLOCK, 1);
+ } else {
+ orig_lock = adjust_lock(a->c, AO2_LOCK_REQ_RDLOCK, 1);
+ }
+ } else {
+ orig_lock = AO2_LOCK_REQ_MUTEX;
+ if (a->flags & AO2_ITERATOR_UNLINK) {
+ ao2_wrlock(a->c);
+ } else {
+ ao2_rdlock(a->c);
+ }
+ }
/* optimization. If the container is unchanged and
* we have a pointer, try follow it
*/
if (a->c->version == a->c_version && (p = a->obj)) {
- if ((p = AST_LIST_NEXT(p, entry)))
+ if ((p = AST_LIST_NEXT(p, entry))) {
goto found;
+ }
/* nope, start from the next bucket */
a->bucket++;
a->version = 0;
@@ -891,8 +1210,9 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *a, struct bucket_en
for (; a->bucket < lim; a->bucket++, a->version = 0) {
/* scan the current bucket */
AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
- if (p->version > a->version)
+ if (p->version > a->version) {
goto found;
+ }
}
}
@@ -913,48 +1233,35 @@ found:
a->version = p->version;
a->obj = p;
a->c_version = a->c->version;
+
/* inc refcount of returned object */
- *q = p;
+ if (tag) {
+ __ao2_ref_debug(ret, 1, tag, file, line, funcname);
+ } else {
+ __ao2_ref(ret, 1);
+ }
}
+ } else {
+ ret = NULL;
+ }
+
+ if (a->flags & AO2_ITERATOR_DONTLOCK) {
+ adjust_lock(a->c, orig_lock, 0);
+ } else {
+ ao2_unlock(a->c);
}
return ret;
}
-void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, char *file, int line, const char *funcname)
+void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname)
{
- struct bucket_entry *p;
- void *ret = NULL;
-
- ret = internal_ao2_iterator_next(a, &p);
-
- if (p) {
- /* inc refcount of returned object */
- __ao2_ref_debug(ret, 1, tag, file, line, funcname);
- }
-
- if (!(a->flags & AO2_ITERATOR_DONTLOCK))
- ao2_unlock(a->c);
-
- return ret;
+ return internal_ao2_iterator_next(a, tag, file, line, funcname);
}
void *__ao2_iterator_next(struct ao2_iterator *a)
{
- struct bucket_entry *p = NULL;
- void *ret = NULL;
-
- ret = internal_ao2_iterator_next(a, &p);
-
- if (p) {
- /* inc refcount of returned object */
- __ao2_ref(ret, 1);
- }
-
- if (!(a->flags & AO2_ITERATOR_DONTLOCK))
- ao2_unlock(a->c);
-
- return ret;
+ return internal_ao2_iterator_next(a, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
}
/* callback for destroying container.
@@ -1037,8 +1344,8 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
int res = 0;
if (!(flags & OBJ_NOLOCK)) {
- ao2_lock(src);
- ao2_lock(dest);
+ ao2_rdlock(src);
+ ao2_wrlock(dest);
}
obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
if (obj) {
@@ -1061,16 +1368,24 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
{
struct ao2_container *clone;
+ struct astobj2 *orig_obj;
+ unsigned int options;
int failed;
+ orig_obj = INTERNAL_OBJ(orig);
+ if (!orig_obj) {
+ return NULL;
+ }
+ options = orig_obj->priv_data.options;
+
/* Create the clone container with the same properties as the original. */
- clone = __ao2_container_alloc(orig->n_buckets, orig->hash_fn, orig->cmp_fn);
+ clone = __ao2_container_alloc(options, orig->n_buckets, orig->hash_fn, orig->cmp_fn);
if (!clone) {
return NULL;
}
if (flags & OBJ_NOLOCK) {
- ao2_lock(clone);
+ ao2_wrlock(clone);
}
failed = ao2_container_dup(clone, orig, flags);
if (flags & OBJ_NOLOCK) {
@@ -1084,20 +1399,28 @@ struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum sea
return clone;
}
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, char *file, int line, const char *funcname, int ref_debug)
+struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *funcname, int ref_debug)
{
struct ao2_container *clone;
+ struct astobj2 *orig_obj;
+ unsigned int options;
int failed;
+ orig_obj = INTERNAL_OBJ(orig);
+ if (!orig_obj) {
+ return NULL;
+ }
+ options = orig_obj->priv_data.options;
+
/* Create the clone container with the same properties as the original. */
- clone = __ao2_container_alloc_debug(orig->n_buckets, orig->hash_fn, orig->cmp_fn, tag,
- file, line, funcname, ref_debug);
+ clone = __ao2_container_alloc_debug(options, orig->n_buckets, orig->hash_fn,
+ orig->cmp_fn, tag, file, line, funcname, ref_debug);
if (!clone) {
return NULL;
}
if (flags & OBJ_NOLOCK) {
- ao2_lock(clone);
+ ao2_wrlock(clone);
}
failed = ao2_container_dup(clone, orig, flags);
if (flags & OBJ_NOLOCK) {
diff --git a/main/format.c b/main/format.c
index 82e3d2628..3af0f15f8 100644
--- a/main/format.c
+++ b/main/format.c
@@ -41,22 +41,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#define FORMAT_CONFIG "codecs.conf"
-/*! This is the container for all the format attribute interfaces.
- * An ao2 container was chosen for fast lookup. */
+/*!
+ * \brief Container for all the format attribute interfaces.
+ * \note This container uses RWLOCKs instead of MUTEX locks. .
+ * \note An ao2 container was chosen for fast lookup.
+ */
static struct ao2_container *interfaces;
-/*! This is the lock used to protect the interfaces container. Yes, ao2_containers
- * do have their own locking, but we need the capability of performing read/write
- * locks on this specific container. */
-static ast_rwlock_t ilock;
-
/*! a wrapper is used put interfaces into the ao2 container. */
struct interface_ao2_wrapper {
enum ast_format_id id;
const struct ast_format_attr_interface *interface;
- /*! a read write lock must be used to protect the wrapper instead
- * of the ao2 lock. */
- ast_rwlock_t wraplock;
};
/*! \brief Format List container, This container is never directly accessed outside
@@ -84,12 +79,6 @@ static int interface_hash_cb(const void *obj, const int flags)
return wrapper->id;
}
-static void interface_destroy_cb(void *obj)
-{
- struct interface_ao2_wrapper *wrapper = obj;
- ast_rwlock_destroy(&wrapper->wraplock);
-}
-
void ast_format_copy(struct ast_format *dst, const struct ast_format *src)
{
memcpy(dst, src, sizeof(struct ast_format));
@@ -107,16 +96,11 @@ int ast_format_get_video_mark(const struct ast_format *format)
static struct interface_ao2_wrapper *find_interface(const struct ast_format *format)
{
- struct interface_ao2_wrapper *wrapper;
struct interface_ao2_wrapper tmp_wrapper = {
.id = format->id,
};
- ast_rwlock_rdlock(&ilock);
- wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK));
- ast_rwlock_unlock(&ilock);
-
- return wrapper;
+ return ao2_find(interfaces, &tmp_wrapper, OBJ_POINTER);
}
static int has_interface(const struct ast_format *format)
@@ -143,16 +127,16 @@ static int format_set_helper(struct ast_format *format, va_list ap)
return -1;
}
- ast_rwlock_rdlock(&wrapper->wraplock);
+ ao2_rdlock(wrapper);
if (!wrapper->interface || !wrapper->interface->format_attr_set) {
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return -1;
}
wrapper->interface->format_attr_set(&format->fattr, ap);
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return 0;
@@ -207,12 +191,12 @@ static int format_isset_helper(const struct ast_format *format, va_list ap)
return -1;
}
- ast_rwlock_rdlock(&wrapper->wraplock);
+ ao2_rdlock(wrapper);
if (!wrapper->interface ||
!wrapper->interface->format_attr_set ||
!wrapper->interface->format_attr_cmp) {
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return -1;
}
@@ -228,7 +212,7 @@ static int format_isset_helper(const struct ast_format *format, va_list ap)
res = (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0;
}
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
@@ -249,21 +233,22 @@ int ast_format_get_value(const struct ast_format *format, int key, void *value)
{
int res = 0;
struct interface_ao2_wrapper *wrapper;
+
if (!(wrapper = find_interface(format))) {
return -1;
}
- ast_rwlock_rdlock(&wrapper->wraplock);
+ ao2_rdlock(wrapper);
if (!wrapper->interface ||
!wrapper->interface->format_attr_get_val) {
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return -1;
}
res = wrapper->interface->format_attr_get_val(&format->fattr, key, value);
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
@@ -281,16 +266,16 @@ static enum ast_format_cmp_res format_cmp_helper(const struct ast_format *format
return res;
}
- ast_rwlock_rdlock(&wrapper->wraplock);
+ ao2_rdlock(wrapper);
if (!wrapper->interface || !wrapper->interface->format_attr_cmp) {
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
}
res = wrapper->interface->format_attr_cmp(&format1->fattr, &format2->fattr);
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
return res;
@@ -318,11 +303,11 @@ static int format_joint_helper(const struct ast_format *format1, const struct as
return res;
}
- ast_rwlock_rdlock(&wrapper->wraplock);
+ ao2_rdlock(wrapper);
if (wrapper->interface && wrapper->interface->format_attr_get_joint) {
res = wrapper->interface->format_attr_get_joint(&format1->fattr, &format2->fattr, &result->fattr);
}
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
@@ -1066,21 +1051,13 @@ init_list_cleanup:
int ast_format_attr_init(void)
{
ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis));
- if (ast_rwlock_init(&ilock)) {
- return -1;
- }
- if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) {
- goto init_cleanup;
+ interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
+ 283, interface_hash_cb, interface_cmp_cb);
+ if (!interfaces) {
+ return -1;
}
return 0;
-
-init_cleanup:
- ast_rwlock_destroy(&ilock);
- if (interfaces) {
- ao2_ref(interfaces, -1);
- }
- return -1;
}
static int custom_celt_format(struct ast_format_list *entry, unsigned int maxbitrate, unsigned int framesize)
@@ -1310,28 +1287,28 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf
* anticipation of adding a new interface and to prevent a
* duplicate from sneaking in between the check and add.
*/
- ast_rwlock_wrlock(&ilock);
+ ao2_wrlock(interfaces);
/* check for duplicates first*/
if ((wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) {
- ast_rwlock_unlock(&ilock);
+ ao2_unlock(interfaces);
ast_log(LOG_WARNING, "Can not register attribute interface for format id %d, interface already exists.\n", interface->id);
ao2_ref(wrapper, -1);
return -1;
}
- if (!(wrapper = ao2_alloc(sizeof(*wrapper), interface_destroy_cb))) {
- ast_rwlock_unlock(&ilock);
+ wrapper = ao2_alloc_options(sizeof(*wrapper), NULL, AO2_ALLOC_OPT_LOCK_RWLOCK);
+ if (!wrapper) {
+ ao2_unlock(interfaces);
return -1;
}
wrapper->interface = interface;
wrapper->id = interface->id;
- ast_rwlock_init(&wrapper->wraplock);
/* The write lock is already held. */
- ao2_link_nolock(interfaces, wrapper);
- ast_rwlock_unlock(&ilock);
+ ao2_link_flags(interfaces, wrapper, OBJ_NOLOCK);
+ ao2_unlock(interfaces);
ao2_ref(wrapper, -1);
@@ -1359,17 +1336,13 @@ int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *inte
.id = interface->id,
};
- /* use the write lock whenever the interface container is modified */
- ast_rwlock_wrlock(&ilock);
- if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK | OBJ_NOLOCK)))) {
- ast_rwlock_unlock(&ilock);
+ if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK)))) {
return -1;
}
- ast_rwlock_unlock(&ilock);
- ast_rwlock_wrlock(&wrapper->wraplock);
+ ao2_wrlock(wrapper);
wrapper->interface = NULL;
- ast_rwlock_unlock(&wrapper->wraplock);
+ ao2_unlock(wrapper);
ao2_ref(wrapper, -1);
diff --git a/main/format_cap.c b/main/format_cap.c
index bd471d4a7..ffec45fe2 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -39,6 +39,7 @@ struct ast_format_cap {
/* The capabilities structure is just an ao2 container of ast_formats */
struct ao2_container *formats;
struct ao2_iterator it;
+ /*! TRUE if the formats container created without a lock. */
int nolock;
};
@@ -69,8 +70,11 @@ static struct ast_format_cap *cap_alloc_helper(int nolock)
if (!cap) {
return NULL;
}
- cap->nolock = nolock ? OBJ_NOLOCK : 0;
- if (!(cap->formats = ao2_container_alloc(283, hash_cb, cmp_cb))) {
+ cap->nolock = nolock;
+ cap->formats = ao2_container_alloc_options(
+ nolock ? AO2_ALLOC_OPT_LOCK_NOLOCK : AO2_ALLOC_OPT_LOCK_MUTEX,
+ 283, hash_cb, cmp_cb);
+ if (!cap->formats) {
ast_free(cap);
return NULL;
}
@@ -109,11 +113,7 @@ void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *for
return;
}
ast_format_copy(fnew, format);
- if (cap->nolock) {
- ao2_link_nolock(cap->formats, fnew);
- } else {
- ao2_link(cap->formats, fnew);
- }
+ ao2_link(cap->formats, fnew);
ao2_ref(fnew, -1);
}
@@ -157,7 +157,7 @@ static int append_cb(void *obj, void *arg, int flag)
void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src)
{
- ao2_callback(src->formats, OBJ_NODATA | src->nolock, append_cb, dst);
+ ao2_callback(src->formats, OBJ_NODATA, append_cb, dst);
}
static int copy_cb(void *obj, void *arg, int flag)
@@ -172,7 +172,7 @@ static int copy_cb(void *obj, void *arg, int flag)
void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src)
{
ast_format_cap_remove_all(dst);
- ao2_callback(src->formats, OBJ_NODATA | src->nolock, copy_cb, dst);
+ ao2_callback(src->formats, OBJ_NODATA, copy_cb, dst);
}
struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
@@ -186,7 +186,7 @@ struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
if (!dst) {
return NULL;
}
- ao2_callback(cap->formats, OBJ_NODATA | cap->nolock, copy_cb, dst);
+ ao2_callback(cap->formats, OBJ_NODATA, copy_cb, dst);
return dst;
}
@@ -209,8 +209,8 @@ static int find_exact_cb(void *obj, void *arg, int flag)
int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
{
struct ast_format *fremove;
- fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK | cap->nolock, find_exact_cb, format);
+ fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK, find_exact_cb, format);
if (fremove) {
ao2_ref(fremove, -1);
return 0;
@@ -249,7 +249,7 @@ int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id
};
ao2_callback(cap->formats,
- OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK,
+ OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
multiple_by_id_cb,
&data);
@@ -271,14 +271,14 @@ static int multiple_by_type_cb(void *obj, void *arg, int flag)
void ast_format_cap_remove_bytype(struct ast_format_cap *cap, enum ast_format_type type)
{
ao2_callback(cap->formats,
- OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | cap->nolock,
+ OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
multiple_by_type_cb,
&type);
}
void ast_format_cap_remove_all(struct ast_format_cap *cap)
{
- ao2_callback(cap->formats, OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
+ ao2_callback(cap->formats, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
}
void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format)
@@ -291,8 +291,8 @@ int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const
{
struct ast_format *f;
struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
- f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
+ f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
if (f) {
ast_format_copy(result, f);
ao2_ref(f, -1);
@@ -310,7 +310,8 @@ int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct a
if (!tmp_cap) {
return 0;
}
- f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
+
+ f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
if (f) {
ao2_ref(f, -1);
return 1;
@@ -345,7 +346,7 @@ int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_i
ast_format_clear(result);
ao2_callback(cap->formats,
- OBJ_MULTIPLE | OBJ_NODATA | cap->nolock,
+ OBJ_MULTIPLE | OBJ_NODATA,
find_best_byid_cb,
&data);
return result->id ? 1 : 0;
@@ -389,11 +390,11 @@ int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast
.joint_cap = NULL,
};
- it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap1->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
- OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+ OBJ_MULTIPLE | OBJ_NODATA,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
@@ -412,7 +413,7 @@ int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast
return 0; /* if they are not the same size, they are not identical */
}
- it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap1->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
if (!ast_format_cap_iscompatible(cap2, tmp)) {
ao2_ref(tmp, -1);
@@ -439,11 +440,11 @@ struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, c
return NULL;
}
- it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap1->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
- OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+ OBJ_MULTIPLE | OBJ_NODATA,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
@@ -469,11 +470,11 @@ static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast
if (!append) {
ast_format_cap_remove_all(result);
}
- it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap1->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
- OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
+ OBJ_MULTIPLE | OBJ_NODATA,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
@@ -505,7 +506,7 @@ struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap,
/* for each format in cap1, see if that format is
* compatible with cap2. If so copy it to the result */
- it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) {
/* copy format */
@@ -529,7 +530,7 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_ty
struct ao2_iterator it;
struct ast_format *tmp;
- it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
if (AST_FORMAT_GET_TYPE(tmp->id) == type) {
ao2_ref(tmp, -1);
@@ -545,18 +546,16 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_ty
void ast_format_cap_iter_start(struct ast_format_cap *cap)
{
- if (!cap->nolock) {
- ao2_lock(cap->formats);
- }
- cap->it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ /* We can unconditionally lock even if the container has no lock. */
+ ao2_lock(cap->formats);
+ cap->it = ao2_iterator_init(cap->formats, AO2_ITERATOR_DONTLOCK);
}
void ast_format_cap_iter_end(struct ast_format_cap *cap)
{
ao2_iterator_destroy(&cap->it);
- if (!cap->nolock) {
- ao2_unlock(cap->formats);
- }
+ /* We can unconditionally unlock even if the container has no lock. */
+ ao2_unlock(cap->formats);
}
int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format)
@@ -614,7 +613,7 @@ uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap)
struct ao2_iterator it;
struct ast_format *tmp;
- it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
+ it = ao2_iterator_init(cap->formats, 0);
while ((tmp = ao2_iterator_next(&it))) {
res |= ast_format_to_old_bitfield(tmp);
ao2_ref(tmp, -1);