Mon Mar 19 11:30:48 2012

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"

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 trylock)
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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 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 507 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 507 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 ast_channels_init(), and get_lock().

00212 {
00213    struct ast_channel *chan = obj, *cmp_args = arg;
00214    return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
00215 }

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 ast_channels_init(), and get_lock().

00206 {
00207    const struct ast_channel *chan = obj;
00208    return ast_str_case_hash(chan->name);
00209 }

static int get_lock ( struct ast_channel chan,
char *  lockname,
int  trylock 
) [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, ast_tvnow(), lock_frame::cond, 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, ast_channel::name, lock_frame::owner, and lock_frame::requesters.

Referenced by lock_read(), and trylock_read().

00218 {
00219    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00220    struct lock_frame *current;
00221    struct channel_lock_frame *clframe = NULL;
00222    AST_LIST_HEAD(, channel_lock_frame) *list;
00223    int res = 0;
00224    struct timespec timeout = { 0, };
00225    struct timeval now;
00226 
00227    if (!lock_store) {
00228       ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00229       lock_store = ast_datastore_alloc(&lock_info, NULL);
00230       if (!lock_store) {
00231          ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
00232          return -1;
00233       }
00234 
00235       list = ast_calloc(1, sizeof(*list));
00236       if (!list) {
00237          ast_log(LOG_ERROR,
00238             "Unable to allocate datastore list head.  %sLOCK will fail.\n",
00239             trylock ? "TRY" : "");
00240          ast_datastore_free(lock_store);
00241          return -1;
00242       }
00243 
00244       lock_store->data = list;
00245       AST_LIST_HEAD_INIT(list);
00246       ast_channel_datastore_add(chan, lock_store);
00247    } else
00248       list = lock_store->data;
00249 
00250    /* Lock already exists? */
00251    AST_LIST_LOCK(&locklist);
00252    AST_LIST_TRAVERSE(&locklist, current, entries) {
00253       if (strcmp(current->name, lockname) == 0) {
00254          break;
00255       }
00256    }
00257 
00258    if (!current) {
00259       if (unloading) {
00260          /* Don't bother */
00261          AST_LIST_UNLOCK(&locklist);
00262          return -1;
00263       }
00264 
00265       /* Create new lock entry */
00266       current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00267       if (!current) {
00268          AST_LIST_UNLOCK(&locklist);
00269          return -1;
00270       }
00271 
00272       strcpy(current->name, lockname); /* SAFE */
00273       if ((res = ast_mutex_init(&current->mutex))) {
00274          ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00275          ast_free(current);
00276          AST_LIST_UNLOCK(&locklist);
00277          return -1;
00278       }
00279       if ((res = ast_cond_init(&current->cond, NULL))) {
00280          ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00281          ast_mutex_destroy(&current->mutex);
00282          ast_free(current);
00283          AST_LIST_UNLOCK(&locklist);
00284          return -1;
00285       }
00286       if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
00287          ast_mutex_destroy(&current->mutex);
00288          ast_cond_destroy(&current->cond);
00289          ast_free(current);
00290          AST_LIST_UNLOCK(&locklist);
00291          return -1;
00292       }
00293       AST_LIST_INSERT_TAIL(&locklist, current, entries);
00294    }
00295    AST_LIST_UNLOCK(&locklist);
00296 
00297    /* Found lock or created one - now find or create the corresponding link in the channel */
00298    AST_LIST_LOCK(list);
00299    AST_LIST_TRAVERSE(list, clframe, list) {
00300       if (clframe->lock_frame == current) {
00301          break;
00302       }
00303    }
00304 
00305    if (!clframe) {
00306       if (unloading) {
00307          /* Don't bother */
00308          AST_LIST_UNLOCK(list);
00309          return -1;
00310       }
00311 
00312       if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00313          ast_log(LOG_ERROR,
00314             "Unable to allocate channel lock frame.  %sLOCK will fail.\n",
00315             trylock ? "TRY" : "");
00316          AST_LIST_UNLOCK(list);
00317          return -1;
00318       }
00319 
00320       clframe->lock_frame = current;
00321       clframe->channel = chan;
00322       AST_LIST_INSERT_TAIL(list, clframe, list);
00323    }
00324    AST_LIST_UNLOCK(list);
00325 
00326    /* If we already own the lock, then we're being called recursively.
00327     * Keep track of how many times that is, because we need to unlock
00328     * the same amount, before we'll release this one.
00329     */
00330    if (current->owner == chan) {
00331       current->count++;
00332       return 0;
00333    }
00334 
00335    /* Okay, we have both frames, so now we need to try to lock.
00336     *
00337     * Locking order: always lock locklist first.  We need the
00338     * locklist lock because the broker thread counts whether
00339     * there are requesters with the locklist lock held, and we
00340     * need to hold it, so that when we send our signal, below,
00341     * to wake up the broker thread, it definitely will see that
00342     * a requester exists at that point in time.  Otherwise, we
00343     * could add to the requesters after it has already seen that
00344     * that lock is unoccupied and wait forever for another signal.
00345     */
00346    AST_LIST_LOCK(&locklist);
00347    ast_mutex_lock(&current->mutex);
00348    /* Add to requester list */
00349    ao2_link(current->requesters, chan);
00350    pthread_kill(broker_tid, SIGURG);
00351    AST_LIST_UNLOCK(&locklist);
00352 
00353    /* Wait up to three seconds from now for LOCK. */
00354    now = ast_tvnow();
00355    timeout.tv_sec = now.tv_sec + 3;
00356    timeout.tv_nsec = now.tv_usec * 1000;
00357 
00358    if (!current->owner
00359       || (!trylock
00360          && !(res = ast_cond_timedwait(&current->cond, &current->mutex, &timeout)))) {
00361       res = 0;
00362       current->owner = chan;
00363       current->count++;
00364    } else {
00365       res = -1;
00366    }
00367    /* Remove from requester list */
00368    ao2_unlink(current->requesters, chan);
00369    ast_mutex_unlock(&current->mutex);
00370 
00371    return res;
00372 }

static int load_module ( void   )  [static]

Definition at line 498 of file func_lock.c.

References ast_custom_function_register, ast_pthread_create_background, lock_broker(), lock_function, trylock_function, and unlock_function.

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::entries, lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.

Referenced by load_module().

00170 {
00171    struct lock_frame *frame;
00172    struct timespec forever = { 1000000, 0 };
00173    for (;;) {
00174       int found_requester = 0;
00175 
00176       /* Test for cancel outside of the lock */
00177       pthread_testcancel();
00178       AST_LIST_LOCK(&locklist);
00179 
00180       AST_LIST_TRAVERSE(&locklist, frame, entries) {
00181          if (ao2_container_count(frame->requesters)) {
00182             found_requester++;
00183             ast_mutex_lock(&frame->mutex);
00184             if (!frame->owner) {
00185                ast_cond_signal(&frame->cond);
00186             }
00187             ast_mutex_unlock(&frame->mutex);
00188          }
00189       }
00190 
00191       AST_LIST_UNLOCK(&locklist);
00192       pthread_testcancel();
00193 
00194       /* If there are no requesters, then wait for a signal */
00195       if (!found_requester) {
00196          nanosleep(&forever, NULL);
00197       } else {
00198          sched_yield();
00199       }
00200    }
00201    /* Not reached */
00202    return NULL;
00203 }

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, lock_info, and lock_frame::owner.

00148 {
00149    struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00150    AST_LIST_HEAD(, channel_lock_frame) *list;
00151    struct channel_lock_frame *clframe = NULL;
00152 
00153    if (!lock_store) {
00154       return;
00155    }
00156    list = lock_store->data;
00157 
00158    AST_LIST_LOCK(list);
00159    AST_LIST_TRAVERSE(list, clframe, list) {
00160       if (clframe->lock_frame->owner == oldchan) {
00161          clframe->lock_frame->owner = newchan;
00162       }
00163       /* We don't move requesters, because the thread stack is different */
00164       clframe->channel = newchan;
00165    }
00166    AST_LIST_UNLOCK(list);
00167 }

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::list, channel_lock_frame::lock_frame, and lock_frame::owner.

00130 {
00131    AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00132    struct channel_lock_frame *clframe;
00133    AST_LIST_LOCK(oldlist);
00134    while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00135       /* Only unlock if we own the lock */
00136       if (clframe->channel == clframe->lock_frame->owner) {
00137          clframe->lock_frame->count = 0;
00138          clframe->lock_frame->owner = NULL;
00139       }
00140       ast_free(clframe);
00141    }
00142    AST_LIST_UNLOCK(oldlist);
00143    AST_LIST_HEAD_DESTROY(oldlist);
00144    ast_free(oldlist);
00145 }

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

Definition at line 418 of file func_lock.c.

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

00419 {
00420    if (chan)
00421       ast_autoservice_start(chan);
00422 
00423    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00424 
00425    if (chan)
00426       ast_autoservice_stop(chan);
00427 
00428    return 0;
00429 }

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

Definition at line 431 of file func_lock.c.

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

00432 {
00433    if (chan)
00434       ast_autoservice_start(chan);
00435 
00436    ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00437 
00438    if (chan)
00439       ast_autoservice_stop(chan);
00440 
00441    return 0;
00442 }

static int unload_module ( void   )  [static]

Definition at line 462 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::entries, lock_function, lock_frame::mutex, lock_frame::owner, lock_frame::requesters, trylock_function, and unlock_function.

00463 {
00464    struct lock_frame *current;
00465 
00466    /* Module flag */
00467    unloading = 1;
00468 
00469    AST_LIST_LOCK(&locklist);
00470    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00471       /* If any locks are currently in use, then we cannot unload this module */
00472       if (current->owner || ao2_container_count(current->requesters)) {
00473          /* Put it back */
00474          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00475          AST_LIST_UNLOCK(&locklist);
00476          unloading = 0;
00477          return -1;
00478       }
00479       ast_mutex_destroy(&current->mutex);
00480       ao2_ref(current->requesters, -1);
00481       ast_free(current);
00482    }
00483 
00484    /* No locks left, unregister functions */
00485    ast_custom_function_unregister(&lock_function);
00486    ast_custom_function_unregister(&trylock_function);
00487    ast_custom_function_unregister(&unlock_function);
00488 
00489    pthread_cancel(broker_tid);
00490    pthread_kill(broker_tid, SIGURG);
00491    pthread_join(broker_tid, NULL);
00492 
00493    AST_LIST_UNLOCK(&locklist);
00494 
00495    return 0;
00496 }

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

Definition at line 374 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, lock_info, LOG_WARNING, lock_frame::name, and lock_frame::owner.

00375 {
00376    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00377    struct channel_lock_frame *clframe;
00378    AST_LIST_HEAD(, channel_lock_frame) *list;
00379 
00380    if (!lock_store) {
00381       ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
00382       ast_copy_string(buf, "0", len);
00383       return 0;
00384    }
00385 
00386    if (!(list = lock_store->data)) {
00387       ast_debug(1, "This should NEVER happen\n");
00388       ast_copy_string(buf, "0", len);
00389       return 0;
00390    }
00391 
00392    /* Find item in the channel list */
00393    AST_LIST_LOCK(list);
00394    AST_LIST_TRAVERSE(list, clframe, list) {
00395       if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00396          break;
00397       }
00398    }
00399    /* We never destroy anything until channel destruction, which will never
00400     * happen while this routine is executing, so we don't need to hold the
00401     * lock beyond this point. */
00402    AST_LIST_UNLOCK(list);
00403 
00404    if (!clframe) {
00405       /* We didn't have this lock in the first place */
00406       ast_copy_string(buf, "0", len);
00407       return 0;
00408    }
00409 
00410    if (--clframe->lock_frame->count == 0) {
00411       clframe->lock_frame->owner = NULL;
00412    }
00413 
00414    ast_copy_string(buf, "1", len);
00415    return 0;
00416 }


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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 507 of file func_lock.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 507 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 100 of file func_lock.c.

struct ast_custom_function lock_function [static]

Initial value:

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

Definition at line 444 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,
   .chan_fixup = lock_fixup,
}

Definition at line 102 of file func_lock.c.

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

struct ast_custom_function trylock_function [static]

Initial value:

 {
   .name = "TRYLOCK",
   .read = trylock_read,
   .read_max = 2,
}

Definition at line 450 of file func_lock.c.

Referenced by load_module(), and unload_module().

int unloading = 0 [static]

Definition at line 99 of file func_lock.c.

struct ast_custom_function unlock_function [static]

Initial value:

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

Definition at line 456 of file func_lock.c.

Referenced by load_module(), and unload_module().


Generated on Mon Mar 19 11:30:48 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7