diff options
-rw-r--r-- | include/asterisk/astobj2.h | 165 | ||||
-rw-r--r-- | include/asterisk/lock.h | 2 | ||||
-rw-r--r-- | main/astobj2.c | 781 | ||||
-rw-r--r-- | main/format.c | 97 | ||||
-rw-r--r-- | main/format_cap.c | 67 |
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); |