Thu Apr 28 2011 17:13:55

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 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 null_cmp_cb (void *obj, void *arg, int flags)
static int null_hash_cb (const void *obj, const int flags)
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
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
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
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 494 of file func_lock.c.

static void __unreg_module ( void  ) [static]

Definition at line 494 of file func_lock.c.

static int get_lock ( struct ast_channel chan,
char *  lockname,
int  try 
) [static]

Definition at line 211 of file func_lock.c.

References ao2_alloc, ao2_container_alloc, ao2_link, ao2_ref, ao2_unlink, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), 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(), chan, 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, null_cmp_cb(), null_hash_cb(), 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, *link;
   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(7, null_hash_cb, null_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;
   }

   /* Link is just an empty flag, used to check whether more than one channel
    * is contending for the lock. */
   if (!(link = ao2_alloc(sizeof(*link), NULL))) {
      return -1;
   }

   /* 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, link);
   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, link);
   ao2_ref(link, -1);
   ast_mutex_unlock(&current->mutex);

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

Definition at line 165 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 143 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 125 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 408 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 null_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 206 of file func_lock.c.

References CMP_MATCH.

Referenced by get_lock().

{
   return obj == arg ? CMP_MATCH : 0;
}
static int null_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 201 of file func_lock.c.

Referenced by get_lock().

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

Definition at line 421 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 449 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 364 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 __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 494 of file func_lock.c.

Definition at line 494 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 96 of file func_lock.c.

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

Definition at line 434 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 98 of file func_lock.c.

Referenced by dummy_start().

Initial value:
 {
   .name = "TRYLOCK",
   .read = trylock_read,
}

Definition at line 439 of file func_lock.c.

int unloading = 0 [static]

Definition at line 95 of file func_lock.c.

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

Definition at line 444 of file func_lock.c.