Add scoped locks to Asterisk.
authorMark Michelson <mmichelson@digium.com>
Tue, 21 Aug 2012 19:04:32 +0000 (19:04 +0000)
committerMark Michelson <mmichelson@digium.com>
Tue, 21 Aug 2012 19:04:32 +0000 (19:04 +0000)
With the SCOPED_LOCK macro, you can create a variable
that locks a specific lock and unlocks the lock when the
variable goes out of scope. This is useful for situations
where many breaks, continues, returns, or other interruptions
would require separate unlock statements. With a scoped lock,
these aren't necessary.

There are specializations for mutexes, read locks, write locks,
ao2 locks, ao2 read locks, ao2 write locks, and channel locks.
Each of these is a SCOPED_LOCK at heart though.

Review: https://reviewboard.asterisk.org/r/2060

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@371582 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/lock.h
main/config.c

index 943c7ff..573fc0c 100644 (file)
@@ -549,6 +549,82 @@ static void  __attribute__((destructor)) fini_##rwlock(void) \
 #define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock, AST_RWLOCK_INIT_VALUE, 1)
 #define AST_RWLOCK_DEFINE_STATIC_NOTRACKING(rwlock) __AST_RWLOCK_DEFINE(static, rwlock, AST_RWLOCK_INIT_VALUE_NOTRACKING, 0)
 
+/*!
+ * \brief Scoped Locks
+ *
+ * Scoped locks provide a way to use RAII locks. In other words,
+ * declaration of a scoped lock will automatically define and lock
+ * the lock. When the lock goes out of scope, it will automatically
+ * be unlocked.
+ *
+ * \example
+ * int some_function(struct ast_channel *chan)
+ * {
+ *     SCOPED_LOCK(lock, chan, ast_channel_lock, ast_channel_unlock);
+ *
+ *     if (!strcmp(ast_channel_name(chan, "foo")) {
+ *         return 0;
+ *     }
+ *
+ *     return -1;
+ * }
+ *
+ * In the above example, neither return path requires explicit unlocking
+ * of the channel.
+ *
+ * \note
+ * Care should be taken when using SCOPED_LOCKS in conjunction with ao2 objects.
+ * ao2 objects should be unlocked before they are unreffed. Since SCOPED_LOCK runs
+ * once the variable goes out of scope, this can easily lead to situations where the
+ * variable gets unlocked after it is unreffed.
+ *
+ * \param varname The unique name to give to the scoped lock. You are not likely to reference
+ * this outside of the SCOPED_LOCK invocation.
+ * \param lock The variable to lock. This can be anything that can be passed to a locking
+ * or unlocking function.
+ * \param lockfunc The function to call to lock the lock
+ * \param unlockfunc The function to call to unlock the lock
+ */
+#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc) \
+       auto void _dtor_ ## varname (typeof((lock)) * v); \
+       auto void _dtor_ ## varname (typeof((lock)) * v) { unlockfunc(*v); } \
+       typeof((lock)) varname __attribute__((cleanup(_dtor_ ## varname))) = lock; lockfunc((lock))
+
+/*!
+ * \brief scoped lock specialization for mutexes
+ */
+#define SCOPED_MUTEX(varname, lock) SCOPED_LOCK(varname, (lock), ast_mutex_lock, ast_mutex_unlock)
+
+/*!
+ * \brief scoped lock specialization for read locks
+ */
+#define SCOPED_RDLOCK(varname, lock) SCOPED_LOCK(varname, (lock), ast_rwlock_rdlock, ast_rwlock_unlock)
+
+/*!
+ * \brief scoped lock specialization for write locks
+ */
+#define SCOPED_WRLOCK(varname, lock) SCOPED_LOCK(varname, (lock), ast_rwlock_wrlock, ast_rwlock_unlock)
+
+/*!
+ * \brief scoped lock specialization for ao2 mutexes.
+ */
+#define SCOPED_AO2LOCK(varname, obj) SCOPED_LOCK(varname, (obj), ao2_lock, ao2_unlock)
+
+/*!
+ * \brief scoped lock specialization for ao2 read locks.
+ */
+#define SCOPED_AO2RDLOCK(varname, obj) SCOPED_LOCK(varname, (obj), ao2_rdlock, ao2_unlock)
+
+/*!
+ * \brief scoped lock specialization for ao2 write locks.
+ */
+#define SCOPED_AO2WRLOCK(varname, obj) SCOPED_LOCK(varname, (obj), ao2_wrlock, ao2_unlock)
+
+/*!
+ * \brief scoped lock specialization for channels.
+ */
+#define SCOPED_CHANNELLOCK(varname, chan) SCOPED_LOCK(varname, (chan), ast_channel_lock, ast_channel_unlock)
+
 #ifndef __CYGWIN__     /* temporary disabled for cygwin */
 #define pthread_mutex_t                use_ast_mutex_t_instead_of_pthread_mutex_t
 #define pthread_cond_t         use_ast_cond_t_instead_of_pthread_cond_t
index 336f51e..bf91963 100644 (file)
@@ -2194,15 +2194,13 @@ static void clear_config_maps(void)
 {
        struct ast_config_map *map;
 
-       ast_mutex_lock(&config_lock);
+       SCOPED_MUTEX(lock, &config_lock);
 
        while (config_maps) {
                map = config_maps;
                config_maps = config_maps->next;
                ast_free(map);
        }
-
-       ast_mutex_unlock(&config_lock);
 }
 
 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
@@ -2325,7 +2323,7 @@ int ast_config_engine_register(struct ast_config_engine *new)
 {
        struct ast_config_engine *ptr;
 
-       ast_mutex_lock(&config_lock);
+       SCOPED_MUTEX(lock, &config_lock);
 
        if (!config_engine_list) {
                config_engine_list = new;
@@ -2334,7 +2332,6 @@ int ast_config_engine_register(struct ast_config_engine *new)
                ptr->next = new;
        }
 
-       ast_mutex_unlock(&config_lock);
        ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
 
        return 1;
@@ -2344,7 +2341,7 @@ int ast_config_engine_deregister(struct ast_config_engine *del)
 {
        struct ast_config_engine *ptr, *last=NULL;
 
-       ast_mutex_lock(&config_lock);
+       SCOPED_MUTEX(lock, &config_lock);
 
        for (ptr = config_engine_list; ptr; ptr=ptr->next) {
                if (ptr == del) {
@@ -2357,25 +2354,20 @@ int ast_config_engine_deregister(struct ast_config_engine *del)
                last = ptr;
        }
 
-       ast_mutex_unlock(&config_lock);
-
        return 0;
 }
 
 int ast_realtime_is_mapping_defined(const char *family)
 {
        struct ast_config_map *map;
-       ast_mutex_lock(&config_lock);
+       SCOPED_MUTEX(lock, &config_lock);
 
        for (map = config_maps; map; map = map->next) {
                if (!strcasecmp(family, map->name)) {
-                       ast_mutex_unlock(&config_lock);
                        return 1;
                }
        }
 
-       ast_mutex_unlock(&config_lock);
-
        return 0;
 }
 
@@ -2385,7 +2377,7 @@ static struct ast_config_engine *find_engine(const char *family, int priority, c
        struct ast_config_engine *eng, *ret = NULL;
        struct ast_config_map *map;
 
-       ast_mutex_lock(&config_lock);
+       SCOPED_MUTEX(lock, &config_lock);
 
        for (map = config_maps; map; map = map->next) {
                if (!strcasecmp(family, map->name) && (priority == map->priority)) {
@@ -2405,8 +2397,6 @@ static struct ast_config_engine *find_engine(const char *family, int priority, c
                }
        }
 
-       ast_mutex_unlock(&config_lock);
-
        /* if we found a mapping, but the engine is not available, then issue a warning */
        if (map && !ret)
                ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
@@ -3028,24 +3018,24 @@ static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int c
                return NULL;
        }
 
-       ast_mutex_lock(&config_lock);
+       {
+               SCOPED_MUTEX(lock, &config_lock);
 
-       if (!config_engine_list) {
-               ast_cli(a->fd, "No config mappings found.\n");
-       } else {
-               for (eng = config_engine_list; eng; eng = eng->next) {
-                       ast_cli(a->fd, "Config Engine: %s\n", eng->name);
-                       for (map = config_maps; map; map = map->next) {
-                               if (!strcasecmp(map->driver, eng->name)) {
-                                       ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
-                                                       map->table ? map->table : map->name);
+               if (!config_engine_list) {
+                       ast_cli(a->fd, "No config mappings found.\n");
+               } else {
+                       for (eng = config_engine_list; eng; eng = eng->next) {
+                               ast_cli(a->fd, "Config Engine: %s\n", eng->name);
+                               for (map = config_maps; map; map = map->next) {
+                                       if (!strcasecmp(map->driver, eng->name)) {
+                                               ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
+                                                               map->table ? map->table : map->name);
+                                       }
                                }
                        }
                }
        }
 
-       ast_mutex_unlock(&config_lock);
-
        return CLI_SUCCESS;
 }