summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2012-02-28 18:15:34 +0000
committerRichard Mudgett <rmudgett@digium.com>2012-02-28 18:15:34 +0000
commit2e834f7d36be6b7fd2531f4e4ac3dcbb050a2bdc (patch)
tree7a9ebd0ea02ff83ef870b1fa9377e5d334a57aea /main
parentbd7e9dae6663ab7f53213916fc001105b7e8bd94 (diff)
Astobj2 locking enhancement.
Add the ability to specify what kind of locking an ao2 object has when it is allocated. The locking could be one of: MUTEX, RWLOCK, or none. New API: ao2_t_alloc_options() ao2_alloc_options() ao2_t_container_alloc_options() ao2_container_alloc_options() ao2_rdlock() ao2_wrlock() ao2_tryrdlock() ao2_trywrlock() The OBJ_NOLOCK and AO2_ITERATOR_DONTLOCK flags have a slight meaning change. They no longer mean that the object is protected by an external mechanism. They mean the lock associated with the object has already been manually obtained by one of the ao2_lock calls. This change is necessary for RWLOCK support since they are not reentrant. Also an operation on an ao2 container may require promoting a read lock to a write lock by releasing the already held read lock to re-acquire as a write lock. Replaced API calls: ao2_t_link_nolock() ao2_link_nolock() ao2_t_unlink_nolock() ao2_unlink_nolock() with the respective ao2_t_link_flags() ao2_link_flags() ao2_t_unlink_flags() ao2_unlink_flags() API calls to be more flexible and to allow an anticipated enhancement to control linking duplicate objects into a container. The changes to format.c and format_cap.c are taking advantange of the new ao2 locking options to simplify the use of the format capabilities containers. Review: https://reviewboard.asterisk.org/r/1554/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@357272 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/astobj2.c781
-rw-r--r--main/format.c97
-rw-r--r--main/format_cap.c67
3 files changed, 620 insertions, 325 deletions
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);