Fri Jul 24 00:41:45 2009

Asterisk developer's documentation


func_lock.c File Reference

Dialplan mutexes. More...

#include "asterisk.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"

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_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_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_custom_function lock_function
static struct ast_datastore_info lock_info
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 350 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 350 of file func_lock.c.

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

Definition at line 89 of file func_lock.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_trylock(), ast_tvdiff_ms(), ast_tvnow(), chan, lock_frame::channel, lock_frame::count, ast_datastore::data, lock_frame::entries, channel_lock_frame::list, channel_lock_frame::lock_frame, lock_info, LOG_ERROR, lock_frame::mutex, lock_frame::name, and ast_channel::name.

Referenced by lock_read(), and trylock_read().

00090 {
00091    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00092    struct lock_frame *current;
00093    struct channel_lock_frame *clframe = NULL, *save_clframe = NULL;
00094    AST_LIST_HEAD(, channel_lock_frame) *list;
00095    int res, count_channel_locks = 0;
00096 
00097    if (!lock_store) {
00098       ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00099       lock_store = ast_datastore_alloc(&lock_info, NULL);
00100       if (!lock_store) {
00101          ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
00102          return -1;
00103       }
00104 
00105       list = ast_calloc(1, sizeof(*list));
00106       if (!list) {
00107          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
00108          ast_datastore_free(lock_store);
00109          return -1;
00110       }
00111 
00112       lock_store->data = list;
00113       AST_LIST_HEAD_INIT(list);
00114       ast_channel_datastore_add(chan, lock_store);
00115    } else
00116       list = lock_store->data;
00117 
00118    /* Lock already exists? */
00119    AST_LIST_LOCK(&locklist);
00120    AST_LIST_TRAVERSE(&locklist, current, entries) {
00121       if (strcmp(current->name, lockname) == 0) {
00122          break;
00123       }
00124    }
00125 
00126    if (!current) {
00127       if (unloading) {
00128          /* Don't bother */
00129          AST_LIST_UNLOCK(&locklist);
00130          return -1;
00131       }
00132 
00133       /* Create new lock entry */
00134       current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00135       if (!current) {
00136          AST_LIST_UNLOCK(&locklist);
00137          return -1;
00138       }
00139 
00140       strcpy((char *)current + sizeof(*current), lockname);
00141       ast_mutex_init(&current->mutex);
00142       AST_LIST_INSERT_TAIL(&locklist, current, entries);
00143    }
00144    AST_LIST_UNLOCK(&locklist);
00145 
00146    /* Found lock or created one - now find or create the corresponding link in the channel */
00147    AST_LIST_LOCK(list);
00148    AST_LIST_TRAVERSE(list, clframe, list) {
00149       if (clframe->lock_frame == current)
00150          save_clframe = clframe;
00151 
00152       /* Only count mutexes that we currently hold */
00153       if (clframe->lock_frame->channel == chan)
00154          count_channel_locks++;
00155    }
00156 
00157    if (save_clframe) {
00158       clframe = save_clframe;
00159    } else {
00160       if (unloading) {
00161          /* Don't bother */
00162          AST_LIST_UNLOCK(list);
00163          return -1;
00164       }
00165 
00166       clframe = ast_calloc(1, sizeof(*clframe));
00167       if (!clframe) {
00168          ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
00169          AST_LIST_UNLOCK(list);
00170          return -1;
00171       }
00172 
00173       clframe->lock_frame = current;
00174       clframe->channel = chan;
00175       /* Count the lock just created */
00176       count_channel_locks++;
00177       AST_LIST_INSERT_TAIL(list, clframe, list);
00178    }
00179    AST_LIST_UNLOCK(list);
00180 
00181    /* Okay, we have both frames, so now we need to try to lock the mutex. */
00182    if (count_channel_locks > 1) {
00183       struct timeval start = ast_tvnow();
00184       for (;;) {
00185          if ((res = ast_mutex_trylock(&current->mutex)) == 0)
00186             break;
00187          if (ast_tvdiff_ms(ast_tvnow(), start) > 3000)
00188             break; /* bail after 3 seconds of waiting */
00189          usleep(1);
00190       }
00191    } else {
00192       /* If the channel doesn't have any locks so far, then there's no possible deadlock. */
00193       res = try ? ast_mutex_trylock(&current->mutex) : ast_mutex_lock(&current->mutex);
00194    }
00195 
00196    if (res == 0) {
00197       current->count++;
00198       current->channel = chan;
00199    }
00200 
00201    return res;
00202 }

static int load_module ( void   )  [static]

Definition at line 342 of file func_lock.c.

References ast_custom_function_register, lock_function, trylock_function, and unlock_function.

00343 {
00344    int res = ast_custom_function_register(&lock_function);
00345    res |= ast_custom_function_register(&trylock_function);
00346    res |= ast_custom_function_register(&unlock_function);
00347    return res;
00348 }

static void lock_free ( void *  data  )  [static]

Definition at line 68 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, ast_mutex_unlock(), lock_frame::channel, channel_lock_frame::channel, lock_frame::count, channel_lock_frame::list, channel_lock_frame::lock_frame, and lock_frame::mutex.

00069 {
00070    AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00071    struct channel_lock_frame *clframe;
00072    AST_LIST_LOCK(oldlist);
00073    while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00074       /* Only unlock if we own the lock */
00075       if (clframe->channel == clframe->lock_frame->channel) {
00076          clframe->lock_frame->channel = NULL;
00077          while (clframe->lock_frame->count > 0) {
00078             clframe->lock_frame->count--;
00079             ast_mutex_unlock(&clframe->lock_frame->mutex);
00080          }
00081       }
00082       ast_free(clframe);
00083    }
00084    AST_LIST_UNLOCK(oldlist);
00085    AST_LIST_HEAD_DESTROY(oldlist);
00086    ast_free(oldlist);
00087 }

static int lock_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 251 of file func_lock.c.

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

00252 {  
00253    if (chan)
00254       ast_autoservice_start(chan);
00255 
00256    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00257 
00258    if (chan)
00259       ast_autoservice_stop(chan);
00260 
00261    return 0;
00262 }

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

Definition at line 264 of file func_lock.c.

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

00265 {
00266    if (chan)
00267       ast_autoservice_start(chan);
00268 
00269    ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00270 
00271    if (chan)
00272       ast_autoservice_stop(chan);
00273 
00274    return 0;
00275 }

static int unload_module ( void   )  [static]

Definition at line 312 of file func_lock.c.

References 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::channel, lock_frame::entries, lock_function, lock_frame::mutex, trylock_function, and unlock_function.

00313 {
00314    struct lock_frame *current;
00315 
00316    /* Module flag */
00317    unloading = 1;
00318 
00319    AST_LIST_LOCK(&locklist);
00320    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00321       /* If any locks are currently in use, then we cannot unload this module */
00322       if (current->channel) {
00323          /* Put it back */
00324          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00325          AST_LIST_UNLOCK(&locklist);
00326          unloading = 0;
00327          return -1;
00328       }
00329       ast_mutex_destroy(&current->mutex);
00330       ast_free(current);
00331    }
00332 
00333    /* No locks left, unregister functions */
00334    ast_custom_function_unregister(&lock_function);
00335    ast_custom_function_unregister(&trylock_function);
00336    ast_custom_function_unregister(&unlock_function);
00337 
00338    AST_LIST_UNLOCK(&locklist);
00339    return 0;
00340 }

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

Definition at line 204 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(), ast_mutex_unlock(), chan, lock_frame::channel, lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, lock_info, LOG_WARNING, lock_frame::mutex, and lock_frame::name.

00205 {
00206    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00207    struct channel_lock_frame *clframe;
00208    AST_LIST_HEAD(, channel_lock_frame) *list;
00209 
00210    if (!lock_store) {
00211       ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
00212       ast_copy_string(buf, "0", len);
00213       return 0;
00214    }
00215 
00216    if (!(list = lock_store->data)) {
00217       ast_debug(1, "This should NEVER happen\n");
00218       ast_copy_string(buf, "0", len);
00219       return 0;
00220    }
00221 
00222    /* Find item in the channel list */
00223    AST_LIST_LOCK(list);
00224    AST_LIST_TRAVERSE(list, clframe, list) {
00225       if (clframe->lock_frame && clframe->lock_frame->channel == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00226          break;
00227       }
00228    }
00229    /* We never destroy anything until channel destruction, which will never
00230     * happen while this routine is executing, so we don't need to hold the
00231     * lock beyond this point. */
00232    AST_LIST_UNLOCK(list);
00233 
00234    if (!clframe) {
00235       /* We didn't have this lock in the first place */
00236       ast_copy_string(buf, "0", len);
00237       return 0;
00238    }
00239 
00240    /* Decrement before we release, because if a channel is waiting on the
00241     * mutex, there's otherwise a race to alter count. */
00242    clframe->lock_frame->count--;
00243    /* If we get another lock, this one shouldn't count against us for deadlock avoidance. */
00244    clframe->lock_frame->channel = NULL;
00245    ast_mutex_unlock(&clframe->lock_frame->mutex);
00246 
00247    ast_copy_string(buf, "1", len);
00248    return 0;
00249 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static]

Definition at line 350 of file func_lock.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 350 of file func_lock.c.

struct ast_custom_function lock_function [static]

Definition at line 277 of file func_lock.c.

Referenced by load_module(), and unload_module().

struct ast_datastore_info lock_info [static]

Initial value:

 {
   .type = "MUTEX",
   .destroy = lock_free,
}

Definition at line 45 of file func_lock.c.

Referenced by dummy_start(), get_lock(), and unlock_read().

struct ast_custom_function trylock_function [static]

Definition at line 290 of file func_lock.c.

Referenced by load_module(), and unload_module().

int unloading = 0 [static]

Definition at line 43 of file func_lock.c.

struct ast_custom_function unlock_function [static]

Definition at line 301 of file func_lock.c.

Referenced by load_module(), and unload_module().


Generated on Fri Jul 24 00:41:45 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7