AO2_ALLOC_OPT_LOCK_NOLOCK = (2 << 0),
/*! The ao2 object locking option field mask. */
AO2_ALLOC_OPT_LOCK_MASK = (3 << 0),
+ /*!
+ * \internal The ao2 object uses a separate object for locking.
+ *
+ * \note This option is used internally by ao2_alloc_with_lockobj and
+ * should never be passed directly to ao2_alloc.
+ */
+ AO2_ALLOC_OPT_LOCK_OBJ = AO2_ALLOC_OPT_LOCK_MASK,
};
/*!
/*! @} */
+/*!
+ * \since 14.1.0
+ * \brief Allocate and initialize an object with separate locking.
+ *
+ * \param data_size The sizeof() of the user-defined structure.
+ * \param destructor_fn The destructor function (can be NULL)
+ * \param lockobj A separate ao2 object that will provide locking.
+ * \param debug_msg An ao2 object debug tracing message.
+ * \return A pointer to user-data.
+ *
+ * \see \ref ao2_alloc for additional details.
+ *
+ * \note lockobj must be a valid AO2 object.
+ */
+#define ao2_alloc_with_lockobj(data_size, destructor_fn, lockobj, tag) \
+ __ao2_alloc_with_lockobj((data_size), (destructor_fn), (lockobj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj,
+ const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result;
+
/*! \brief
* Reference/unreference an object and return the old refcount.
*
void *user_data[0];
};
+struct ao2_lockobj_priv {
+ void *lock;
+};
+
+/* AstObj2 with locking provided by a separate object. */
+struct astobj2_lockobj {
+ struct ao2_lockobj_priv lockobj;
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
#ifdef AO2_DEBUG
struct ao2_stats ao2;
#endif
#define INTERNAL_OBJ_RWLOCK(user_data) \
((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
+#define INTERNAL_OBJ_LOCKOBJ(user_data) \
+ ((struct astobj2_lockobj *) (((char *) (user_data)) - sizeof(struct astobj2_lockobj)))
+
#define INTERNAL_OBJ(user_data) \
(struct astobj2 *) ((char *) user_data - sizeof(struct astobj2))
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
if (obj == NULL) {
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_lock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
int current_value;
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_unlock(obj_lockobj->lockobj.lock, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
if (obj == NULL) {
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
return 0;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_trylock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
{
struct astobj2 *obj = INTERNAL_OBJ(user_data);
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
enum ao2_lock_req orig_lock;
switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
break;
}
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ orig_lock = __adjust_lock(obj_lockobj->lockobj.lock, lock_how, keep_stronger);
+ break;
default:
ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
/* Fall through */
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int current_value;
int ret;
void *weakproxy = NULL;
case AO2_ALLOC_OPT_LOCK_NOLOCK:
ast_free(obj);
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ ao2_t_ref(obj_lockobj->lockobj.lock, -1, "release lockobj");
+
+ ast_free(obj_lockobj);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func,
"Invalid lock option on ao2 object %p\n", user_data);
}
}
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
- const char *tag, const char *file, int line, const char *func)
+static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
+ void *lockobj, const char *tag, const char *file, int line, const char *func)
{
/* allocation */
struct astobj2 *obj;
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
case AO2_ALLOC_OPT_LOCK_MUTEX:
return NULL;
}
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ lockobj = ao2_t_bump(lockobj, "set lockobj");
+ if (!lockobj) {
+ ast_log(__LOG_ERROR, file, line, func, "AO2_ALLOC_OPT_LOCK_OBJ requires a non-NULL lockobj.\n");
+ return NULL;
+ }
+
+ obj_lockobj = __ast_calloc(1, sizeof(*obj_lockobj) + data_size, file, line, func);
+ if (obj_lockobj == NULL) {
+ ao2_t_ref(lockobj, -1, "release lockobj for failed alloc");
+ return NULL;
+ }
+
+ obj_lockobj->lockobj.lock = lockobj;
+ obj = (struct astobj2 *) &obj_lockobj->priv_data;
+ break;
default:
/* Invalid option value. */
ast_log(__LOG_DEBUG, file, line, func, "Invalid lock option requested\n");
return EXTERNAL_OBJ(obj);
}
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
+ const char *tag, const char *file, int line, const char *func)
+{
+ return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func);
+}
+
+void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj,
+ const char *tag, const char *file, int line, const char *func)
+{
+ return internal_ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_OBJ, lockobj,
+ tag, file, line, func);
+}
+
unsigned int ao2_options_get(void *obj)
{
struct astobj2 *orig_obj;