Wed Aug 18 22:34:24 2010

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 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 __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 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 467 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 467 of file func_lock.c.

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

Definition at line 164 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, 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, null_cmp_cb(), null_hash_cb(), lock_frame::owner, and lock_frame::requesters.

Referenced by lock_read(), and trylock_read().

00165 {
00166    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00167    struct lock_frame *current;
00168    struct channel_lock_frame *clframe = NULL;
00169    AST_LIST_HEAD(, channel_lock_frame) *list;
00170    int res = 0, *link;
00171    struct timespec three_seconds = { .tv_sec = 3 };
00172 
00173    if (!lock_store) {
00174       ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
00175       lock_store = ast_datastore_alloc(&lock_info, NULL);
00176       if (!lock_store) {
00177          ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
00178          return -1;
00179       }
00180 
00181       list = ast_calloc(1, sizeof(*list));
00182       if (!list) {
00183          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
00184          ast_datastore_free(lock_store);
00185          return -1;
00186       }
00187 
00188       lock_store->data = list;
00189       AST_LIST_HEAD_INIT(list);
00190       ast_channel_datastore_add(chan, lock_store);
00191    } else
00192       list = lock_store->data;
00193 
00194    /* Lock already exists? */
00195    AST_LIST_LOCK(&locklist);
00196    AST_LIST_TRAVERSE(&locklist, current, entries) {
00197       if (strcmp(current->name, lockname) == 0) {
00198          break;
00199       }
00200    }
00201 
00202    if (!current) {
00203       if (unloading) {
00204          /* Don't bother */
00205          AST_LIST_UNLOCK(&locklist);
00206          return -1;
00207       }
00208 
00209       /* Create new lock entry */
00210       current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
00211       if (!current) {
00212          AST_LIST_UNLOCK(&locklist);
00213          return -1;
00214       }
00215 
00216       strcpy(current->name, lockname); /* SAFE */
00217       if ((res = ast_mutex_init(&current->mutex))) {
00218          ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
00219          ast_free(current);
00220          AST_LIST_UNLOCK(&locklist);
00221          return -1;
00222       }
00223       if ((res = ast_cond_init(&current->cond, NULL))) {
00224          ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
00225          ast_mutex_destroy(&current->mutex);
00226          ast_free(current);
00227          AST_LIST_UNLOCK(&locklist);
00228          return -1;
00229       }
00230       if (!(current->requesters = ao2_container_alloc(7, null_hash_cb, null_cmp_cb))) {
00231          ast_mutex_destroy(&current->mutex);
00232          ast_cond_destroy(&current->cond);
00233          ast_free(current);
00234          AST_LIST_UNLOCK(&locklist);
00235          return -1;
00236       }
00237       AST_LIST_INSERT_TAIL(&locklist, current, entries);
00238    }
00239    AST_LIST_UNLOCK(&locklist);
00240 
00241    /* Found lock or created one - now find or create the corresponding link in the channel */
00242    AST_LIST_LOCK(list);
00243    AST_LIST_TRAVERSE(list, clframe, list) {
00244       if (clframe->lock_frame == current) {
00245          break;
00246       }
00247    }
00248 
00249    if (!clframe) {
00250       if (unloading) {
00251          /* Don't bother */
00252          AST_LIST_UNLOCK(list);
00253          return -1;
00254       }
00255 
00256       if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
00257          ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
00258          AST_LIST_UNLOCK(list);
00259          return -1;
00260       }
00261 
00262       clframe->lock_frame = current;
00263       clframe->channel = chan;
00264       AST_LIST_INSERT_TAIL(list, clframe, list);
00265    }
00266    AST_LIST_UNLOCK(list);
00267 
00268    /* If we already own the lock, then we're being called recursively.
00269     * Keep track of how many times that is, because we need to unlock
00270     * the same amount, before we'll release this one.
00271     */
00272    if (current->owner == chan) {
00273       current->count++;
00274       return 0;
00275    }
00276 
00277    /* Link is just an empty flag, used to check whether more than one channel
00278     * is contending for the lock. */
00279    if (!(link = ao2_alloc(sizeof(*link), NULL))) {
00280       return -1;
00281    }
00282 
00283    /* Okay, we have both frames, so now we need to try to lock.
00284     *
00285     * Locking order: always lock locklist first.  We need the
00286     * locklist lock because the broker thread counts whether
00287     * there are requesters with the locklist lock held, and we
00288     * need to hold it, so that when we send our signal, below,
00289     * to wake up the broker thread, it definitely will see that
00290     * a requester exists at that point in time.  Otherwise, we
00291     * could add to the requesters after it has already seen that
00292     * that lock is unoccupied and wait forever for another signal.
00293     */
00294    AST_LIST_LOCK(&locklist);
00295    ast_mutex_lock(&current->mutex);
00296    /* Add to requester list */
00297    ao2_link(current->requesters, link);
00298    pthread_kill(broker_tid, SIGURG);
00299    AST_LIST_UNLOCK(&locklist);
00300 
00301    if ((!current->owner) ||
00302       (!try && !(res = ast_cond_timedwait(&current->cond, &current->mutex, &three_seconds)))) {
00303       res = 0;
00304       current->owner = chan;
00305       current->count++;
00306    } else {
00307       res = -1;
00308    }
00309    /* Remove from requester list */
00310    ao2_unlink(current->requesters, link);
00311    ao2_ref(link, -1);
00312    ast_mutex_unlock(&current->mutex);
00313 
00314    return res;
00315 }

static int load_module ( void   )  [static]

Definition at line 458 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 118 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().

00119 {
00120    struct lock_frame *frame;
00121    struct timespec forever = { 1000000, 0 };
00122    for (;;) {
00123       int found_requester = 0;
00124 
00125       /* Test for cancel outside of the lock */
00126       pthread_testcancel();
00127       AST_LIST_LOCK(&locklist);
00128 
00129       AST_LIST_TRAVERSE(&locklist, frame, entries) {
00130          if (ao2_container_count(frame->requesters)) {
00131             found_requester++;
00132             ast_mutex_lock(&frame->mutex);
00133             if (!frame->owner) {
00134                ast_cond_signal(&frame->cond);
00135             }
00136             ast_mutex_unlock(&frame->mutex);
00137          }
00138       }
00139 
00140       AST_LIST_UNLOCK(&locklist);
00141       pthread_testcancel();
00142 
00143       /* If there are no requesters, then wait for a signal */
00144       if (!found_requester) {
00145          nanosleep(&forever, NULL);
00146       } else {
00147          sched_yield();
00148       }
00149    }
00150    /* Not reached */
00151    return NULL;
00152 }

static void lock_fixup ( void *  data,
struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Definition at line 96 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.

00097 {
00098    struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
00099    AST_LIST_HEAD(, channel_lock_frame) *list;
00100    struct channel_lock_frame *clframe = NULL;
00101 
00102    if (!lock_store) {
00103       return;
00104    }
00105    list = lock_store->data;
00106 
00107    AST_LIST_LOCK(list);
00108    AST_LIST_TRAVERSE(list, clframe, list) {
00109       if (clframe->lock_frame->owner == oldchan) {
00110          clframe->lock_frame->owner = newchan;
00111       }
00112       /* We don't move requesters, because the thread stack is different */
00113       clframe->channel = newchan;
00114    }
00115    AST_LIST_UNLOCK(list);
00116 }

static void lock_free ( void *  data  )  [static]

Definition at line 78 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.

00079 {
00080    AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
00081    struct channel_lock_frame *clframe;
00082    AST_LIST_LOCK(oldlist);
00083    while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
00084       /* Only unlock if we own the lock */
00085       if (clframe->channel == clframe->lock_frame->owner) {
00086          clframe->lock_frame->count = 0;
00087          clframe->lock_frame->owner = NULL;
00088       }
00089       ast_free(clframe);
00090    }
00091    AST_LIST_UNLOCK(oldlist);
00092    AST_LIST_HEAD_DESTROY(oldlist);
00093    ast_free(oldlist);
00094 }

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

Definition at line 361 of file func_lock.c.

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

00362 {
00363    if (chan)
00364       ast_autoservice_start(chan);
00365 
00366    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00367 
00368    if (chan)
00369       ast_autoservice_stop(chan);
00370 
00371    return 0;
00372 }

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

Definition at line 159 of file func_lock.c.

References CMP_MATCH.

Referenced by get_lock().

00160 {
00161    return obj == arg ? CMP_MATCH : 0;
00162 }

static int null_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 154 of file func_lock.c.

Referenced by get_lock().

00155 {
00156    return (int)(long) obj;
00157 }

static int trylock_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_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), chan, and get_lock().

00375 {
00376    if (chan)
00377       ast_autoservice_start(chan);
00378 
00379    ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
00380 
00381    if (chan)
00382       ast_autoservice_stop(chan);
00383 
00384    return 0;
00385 }

static int unload_module ( void   )  [static]

Definition at line 422 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.

00423 {
00424    struct lock_frame *current;
00425 
00426    /* Module flag */
00427    unloading = 1;
00428 
00429    AST_LIST_LOCK(&locklist);
00430    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00431       /* If any locks are currently in use, then we cannot unload this module */
00432       if (current->owner || ao2_container_count(current->requesters)) {
00433          /* Put it back */
00434          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00435          AST_LIST_UNLOCK(&locklist);
00436          unloading = 0;
00437          return -1;
00438       }
00439       ast_mutex_destroy(&current->mutex);
00440       ao2_ref(current->requesters, -1);
00441       ast_free(current);
00442    }
00443 
00444    /* No locks left, unregister functions */
00445    ast_custom_function_unregister(&lock_function);
00446    ast_custom_function_unregister(&trylock_function);
00447    ast_custom_function_unregister(&unlock_function);
00448 
00449    pthread_cancel(broker_tid);
00450    pthread_kill(broker_tid, SIGURG);
00451    pthread_join(broker_tid, NULL);
00452 
00453    AST_LIST_UNLOCK(&locklist);
00454 
00455    return 0;
00456 }

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

Definition at line 317 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(), chan, 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.

00318 {
00319    struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
00320    struct channel_lock_frame *clframe;
00321    AST_LIST_HEAD(, channel_lock_frame) *list;
00322 
00323    if (!lock_store) {
00324       ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
00325       ast_copy_string(buf, "0", len);
00326       return 0;
00327    }
00328 
00329    if (!(list = lock_store->data)) {
00330       ast_debug(1, "This should NEVER happen\n");
00331       ast_copy_string(buf, "0", len);
00332       return 0;
00333    }
00334 
00335    /* Find item in the channel list */
00336    AST_LIST_LOCK(list);
00337    AST_LIST_TRAVERSE(list, clframe, list) {
00338       if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
00339          break;
00340       }
00341    }
00342    /* We never destroy anything until channel destruction, which will never
00343     * happen while this routine is executing, so we don't need to hold the
00344     * lock beyond this point. */
00345    AST_LIST_UNLOCK(list);
00346 
00347    if (!clframe) {
00348       /* We didn't have this lock in the first place */
00349       ast_copy_string(buf, "0", len);
00350       return 0;
00351    }
00352 
00353    if (--clframe->lock_frame->count == 0) {
00354       clframe->lock_frame->owner = NULL;
00355    }
00356 
00357    ast_copy_string(buf, "1", len);
00358    return 0;
00359 }


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 467 of file func_lock.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 467 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

Definition at line 49 of file func_lock.c.

struct ast_custom_function lock_function [static]

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

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

struct ast_custom_function trylock_function [static]

Definition at line 400 of file func_lock.c.

Referenced by load_module(), and unload_module().

int unloading = 0 [static]

Definition at line 48 of file func_lock.c.

struct ast_custom_function unlock_function [static]

Definition at line 411 of file func_lock.c.

Referenced by load_module(), and unload_module().


Generated on Wed Aug 18 22:34:24 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7