Mon Jun 27 16:51:14 2011

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 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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 492 of file func_lock.c.

static void __unreg_module ( void   )  [static]

Definition at line 492 of file func_lock.c.

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

Definition at line 207 of file func_lock.c.

References CMP_MATCH, and ast_channel::name.

Referenced by ast_channels_init(), and get_lock().

00208 {
00209    struct ast_channel *chan = obj, *cmp_args = arg;
00210    return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
00211 }

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

Definition at line 201 of file func_lock.c.

References ast_str_case_hash(), and ast_channel::name.

Referenced by ast_channels_init(), and get_lock().

00202 {
00203    const struct ast_channel *chan = obj;
00204    return ast_str_case_hash(chan->name);
00205 }

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

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

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

static int load_module ( void   )  [static]

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

Referenced by load_module().

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

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

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

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

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

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

Definition at line 403 of file func_lock.c.

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

00404 {
00405    if (chan)
00406       ast_autoservice_start(chan);
00407 
00408    ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
00409 
00410    if (chan)
00411       ast_autoservice_stop(chan);
00412 
00413    return 0;
00414 }

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

Definition at line 416 of file func_lock.c.

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

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

static int unload_module ( void   )  [static]

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

00448 {
00449    struct lock_frame *current;
00450 
00451    /* Module flag */
00452    unloading = 1;
00453 
00454    AST_LIST_LOCK(&locklist);
00455    while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00456       /* If any locks are currently in use, then we cannot unload this module */
00457       if (current->owner || ao2_container_count(current->requesters)) {
00458          /* Put it back */
00459          AST_LIST_INSERT_HEAD(&locklist, current, entries);
00460          AST_LIST_UNLOCK(&locklist);
00461          unloading = 0;
00462          return -1;
00463       }
00464       ast_mutex_destroy(&current->mutex);
00465       ao2_ref(current->requesters, -1);
00466       ast_free(current);
00467    }
00468 
00469    /* No locks left, unregister functions */
00470    ast_custom_function_unregister(&lock_function);
00471    ast_custom_function_unregister(&trylock_function);
00472    ast_custom_function_unregister(&unlock_function);
00473 
00474    pthread_cancel(broker_tid);
00475    pthread_kill(broker_tid, SIGURG);
00476    pthread_join(broker_tid, NULL);
00477 
00478    AST_LIST_UNLOCK(&locklist);
00479 
00480    return 0;
00481 }

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

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

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


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

Definition at line 492 of file func_lock.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 492 of file func_lock.c.

pthread_t broker_tid = AST_PTHREADT_NULL [static]

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

Referenced by append_lock_information(), ast_find_lock_info(), ast_mark_lock_acquired(), ast_mark_lock_failed(), ast_remove_lock_info(), ast_store_lock_info(), dummy_start(), get_lock(), handle_show_locks(), lock_fixup(), lock_info_destroy(), log_show_lock(), and unlock_read().

struct ast_custom_function trylock_function [static]

Initial value:

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

Definition at line 435 of file func_lock.c.

Referenced by load_module(), and unload_module().

int unloading = 0 [static]

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

Referenced by load_module(), and unload_module().


Generated on Mon Jun 27 16:51:14 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7