summaryrefslogtreecommitdiff
path: root/main/astobj2.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/astobj2.c')
-rw-r--r--main/astobj2.c781
1 files changed, 552 insertions, 229 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) {