Mon Mar 12 2012 21:39:56

Asterisk developer's documentation


func_lock.c File Reference

Dialplan mutexes. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
Include dependency graph for func_lock.c:

Go to the source code of this file.

Data Structures

struct  channel_lock_frame
struct  lock_frame
struct  locklist

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int ast_channel_cmp_cb (void *obj, void *arg, int flags)
static int ast_channel_hash_cb (const void *obj, const int flags)
static int get_lock (struct ast_channel *chan, char *lockname, int try)
static int load_module (void)
static void * lock_broker (void *unused)
static void lock_fixup (void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
static void lock_free (void *data)
static int lock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int trylock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int unload_module (void)
static int unlock_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Dialplan mutexes" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static struct ast_module_infoast_module_info = &__mod_info
static pthread_t broker_tid = AST_PTHREADT_NULL
static struct ast_custom_function lock_function
static struct ast_datastore_info lock_info
static struct locklist locklist
static struct ast_custom_function trylock_function
static int unloading = 0
static struct ast_custom_function unlock_function

Detailed Description

Dialplan mutexes.

Author:
Tilghman Lesher <func_lock_2007@the-tilghman.com>

Definition in file func_lock.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 496 of file func_lock.c.

static void __unreg_module ( void  ) [static]

Definition at line 496 of file func_lock.c.

static int ast_channel_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 211 of file func_lock.c.

References CMP_MATCH, and ast_channel::name.

Referenced by get_lock().

{
   struct ast_channel *chan = obj, *cmp_args = arg;
   return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
}
static int ast_channel_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 205 of file func_lock.c.

References ast_str_case_hash(), and ast_channel::name.

Referenced by get_lock().

{
   const struct ast_channel *chan = obj;
   return ast_str_case_hash(chan->name);
}
static int get_lock ( struct ast_channel chan,
char *  lockname,
int  try 
) [static]

Definition at line 217 of file func_lock.c.

References ao2_container_alloc, ao2_link, ao2_unlink, ast_calloc, ast_channel_cmp_cb(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_hash_cb(), ast_cond_destroy, ast_cond_init, ast_cond_timedwait, ast_datastore_alloc(), ast_datastore_free(), ast_debug, ast_free, AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy, ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, channel_lock_frame::channel, lock_frame::cond, lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, LOG_ERROR, lock_frame::mutex, lock_frame::name, ast_channel::name, lock_frame::owner, and lock_frame::requesters.

Referenced by lock_read(), and trylock_read().

{
   struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
   struct lock_frame *current;
   struct channel_lock_frame *clframe = NULL;
   AST_LIST_HEAD(, channel_lock_frame) *list;
   int res = 0;
   struct timespec three_seconds = { .tv_sec = 3 };

   if (!lock_store) {
      ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
      lock_store = ast_datastore_alloc(&lock_info, NULL);
      if (!lock_store) {
         ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
         return -1;
      }

      list = ast_calloc(1, sizeof(*list));
      if (!list) {
         ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
         ast_datastore_free(lock_store);
         return -1;
      }

      lock_store->data = list;
      AST_LIST_HEAD_INIT(list);
      ast_channel_datastore_add(chan, lock_store);
   } else
      list = lock_store->data;

   /* Lock already exists? */
   AST_LIST_LOCK(&locklist);
   AST_LIST_TRAVERSE(&locklist, current, entries) {
      if (strcmp(current->name, lockname) == 0) {
         break;
      }
   }

   if (!current) {
      if (unloading) {
         /* Don't bother */
         AST_LIST_UNLOCK(&locklist);
         return -1;
      }

      /* Create new lock entry */
      current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
      if (!current) {
         AST_LIST_UNLOCK(&locklist);
         return -1;
      }

      strcpy(current->name, lockname); /* SAFE */
      if ((res = ast_mutex_init(&current->mutex))) {
         ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
         ast_free(current);
         AST_LIST_UNLOCK(&locklist);
         return -1;
      }
      if ((res = ast_cond_init(&current->cond, NULL))) {
         ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
         ast_mutex_destroy(&current->mutex);
         ast_free(current);
         AST_LIST_UNLOCK(&locklist);
         return -1;
      }
      if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
         ast_mutex_destroy(&current->mutex);
         ast_cond_destroy(&current->cond);
         ast_free(current);
         AST_LIST_UNLOCK(&locklist);
         return -1;
      }
      AST_LIST_INSERT_TAIL(&locklist, current, entries);
   }
   AST_LIST_UNLOCK(&locklist);

   /* Found lock or created one - now find or create the corresponding link in the channel */
   AST_LIST_LOCK(list);
   AST_LIST_TRAVERSE(list, clframe, list) {
      if (clframe->lock_frame == current) {
         break;
      }
   }

   if (!clframe) {
      if (unloading) {
         /* Don't bother */
         AST_LIST_UNLOCK(list);
         return -1;
      }

      if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
         ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
         AST_LIST_UNLOCK(list);
         return -1;
      }

      clframe->lock_frame = current;
      clframe->channel = chan;
      AST_LIST_INSERT_TAIL(list, clframe, list);
   }
   AST_LIST_UNLOCK(list);

   /* If we already own the lock, then we're being called recursively.
    * Keep track of how many times that is, because we need to unlock
    * the same amount, before we'll release this one.
    */
   if (current->owner == chan) {
      current->count++;
      return 0;
   }

   /* Okay, we have both frames, so now we need to try to lock.
    *
    * Locking order: always lock locklist first.  We need the
    * locklist lock because the broker thread counts whether
    * there are requesters with the locklist lock held, and we
    * need to hold it, so that when we send our signal, below,
    * to wake up the broker thread, it definitely will see that
    * a requester exists at that point in time.  Otherwise, we
    * could add to the requesters after it has already seen that
    * that lock is unoccupied and wait forever for another signal.
    */
   AST_LIST_LOCK(&locklist);
   ast_mutex_lock(&current->mutex);
   /* Add to requester list */
   ao2_link(current->requesters, chan);
   pthread_kill(broker_tid, SIGURG);
   AST_LIST_UNLOCK(&locklist);

   if ((!current->owner) ||
      (!try && !(res = ast_cond_timedwait(&current->cond, &current->mutex, &three_seconds)))) {
      res = 0;
      current->owner = chan;
      current->count++;
   } else {
      res = -1;
   }
   /* Remove from requester list */
   ao2_unlink(current->requesters, chan);
   ast_mutex_unlock(&current->mutex);

   return res;
}
static void* lock_broker ( void *  unused) [static]

Definition at line 169 of file func_lock.c.

References ao2_container_count(), ast_cond_signal, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock, ast_mutex_unlock, lock_frame::cond, lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.

Referenced by load_module().

{
   struct lock_frame *frame;
   struct timespec forever = { 1000000, 0 };
   for (;;) {
      int found_requester = 0;

      /* Test for cancel outside of the lock */
      pthread_testcancel();
      AST_LIST_LOCK(&locklist);

      AST_LIST_TRAVERSE(&locklist, frame, entries) {
         if (ao2_container_count(frame->requesters)) {
            found_requester++;
            ast_mutex_lock(&frame->mutex);
            if (!frame->owner) {
               ast_cond_signal(&frame->cond);
            }
            ast_mutex_unlock(&frame->mutex);
         }
      }

      AST_LIST_UNLOCK(&locklist);
      pthread_testcancel();

      /* If there are no requesters, then wait for a signal */
      if (!found_requester) {
         nanosleep(&forever, NULL);
      } else {
         sched_yield();
      }
   }
   /* Not reached */
   return NULL;
}
static void lock_fixup ( void *  data,
struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Definition at line 147 of file func_lock.c.

References ast_channel_datastore_find(), AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, channel_lock_frame::channel, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, and lock_frame::owner.

{
   struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
   AST_LIST_HEAD(, channel_lock_frame) *list;
   struct channel_lock_frame *clframe = NULL;

   if (!lock_store) {
      return;
   }
   list = lock_store->data;

   AST_LIST_LOCK(list);
   AST_LIST_TRAVERSE(list, clframe, list) {
      if (clframe->lock_frame->owner == oldchan) {
         clframe->lock_frame->owner = newchan;
      }
      /* We don't move requesters, because the thread stack is different */
      clframe->channel = newchan;
   }
   AST_LIST_UNLOCK(list);
}
static void lock_free ( void *  data) [static]

Definition at line 129 of file func_lock.c.

References ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, channel_lock_frame::channel, lock_frame::count, channel_lock_frame::lock_frame, and lock_frame::owner.

{
   AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
   struct channel_lock_frame *clframe;
   AST_LIST_LOCK(oldlist);
   while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
      /* Only unlock if we own the lock */
      if (clframe->channel == clframe->lock_frame->owner) {
         clframe->lock_frame->count = 0;
         clframe->lock_frame->owner = NULL;
      }
      ast_free(clframe);
   }
   AST_LIST_UNLOCK(oldlist);
   AST_LIST_HEAD_DESTROY(oldlist);
   ast_free(oldlist);
}
static int lock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 407 of file func_lock.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().

{
   if (chan)
      ast_autoservice_start(chan);

   ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);

   if (chan)
      ast_autoservice_stop(chan);

   return 0;
}
static int trylock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 420 of file func_lock.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().

{
   if (chan)
      ast_autoservice_start(chan);

   ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);

   if (chan)
      ast_autoservice_stop(chan);

   return 0;
}
static int unload_module ( void  ) [static]

Definition at line 451 of file func_lock.c.

References ao2_container_count(), ao2_ref, ast_custom_function_unregister(), ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_destroy, lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.

{
   struct lock_frame *current;

   /* Module flag */
   unloading = 1;

   AST_LIST_LOCK(&locklist);
   while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
      /* If any locks are currently in use, then we cannot unload this module */
      if (current->owner || ao2_container_count(current->requesters)) {
         /* Put it back */
         AST_LIST_INSERT_HEAD(&locklist, current, entries);
         AST_LIST_UNLOCK(&locklist);
         unloading = 0;
         return -1;
      }
      ast_mutex_destroy(&current->mutex);
      ao2_ref(current->requesters, -1);
      ast_free(current);
   }

   /* No locks left, unregister functions */
   ast_custom_function_unregister(&lock_function);
   ast_custom_function_unregister(&trylock_function);
   ast_custom_function_unregister(&unlock_function);

   pthread_cancel(broker_tid);
   pthread_kill(broker_tid, SIGURG);
   pthread_join(broker_tid, NULL);

   AST_LIST_UNLOCK(&locklist);

   return 0;
}
static int unlock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 363 of file func_lock.c.

References ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, LOG_WARNING, lock_frame::name, and lock_frame::owner.

{
   struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
   struct channel_lock_frame *clframe;
   AST_LIST_HEAD(, channel_lock_frame) *list;

   if (!lock_store) {
      ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
      ast_copy_string(buf, "0", len);
      return 0;
   }

   if (!(list = lock_store->data)) {
      ast_debug(1, "This should NEVER happen\n");
      ast_copy_string(buf, "0", len);
      return 0;
   }

   /* Find item in the channel list */
   AST_LIST_LOCK(list);
   AST_LIST_TRAVERSE(list, clframe, list) {
      if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
         break;
      }
   }
   /* We never destroy anything until channel destruction, which will never
    * happen while this routine is executing, so we don't need to hold the
    * lock beyond this point. */
   AST_LIST_UNLOCK(list);

   if (!clframe) {
      /* We didn't have this lock in the first place */
      ast_copy_string(buf, "0", len);
      return 0;
   }

   if (--clframe->lock_frame->count == 0) {
      clframe->lock_frame->owner = NULL;
   }

   ast_copy_string(buf, "1", len);
   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Dialplan mutexes" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 496 of file func_lock.c.

Definition at line 496 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 100 of file func_lock.c.

Initial value:
 {
   .name = "LOCK",
   .read = lock_read,
   .read_max = 2,
}

Definition at line 433 of file func_lock.c.

struct ast_datastore_info lock_info [static]
Initial value:
 {
   .type = "MUTEX",
   .destroy = lock_free,
   .chan_fixup = lock_fixup,
}

Definition at line 102 of file func_lock.c.

Referenced by dummy_start().

struct locklist locklist [static]
Initial value:
 {
   .name = "TRYLOCK",
   .read = trylock_read,
   .read_max = 2,
}

Definition at line 439 of file func_lock.c.

int unloading = 0 [static]

Definition at line 99 of file func_lock.c.

Initial value:
 {
   .name = "UNLOCK",
   .read = unlock_read,
   .read_max = 2,
}

Definition at line 445 of file func_lock.c.