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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } |
| static struct ast_module_info * | ast_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 locklist | locklist |
| static struct ast_custom_function | trylock_function |
| static int | unloading = 0 |
| static struct ast_custom_function | unlock_function |
Dialplan mutexes.
Definition in file func_lock.c.
| static void __reg_module | ( | void | ) | [static] |
Definition at line 496 of file func_lock.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 496 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 get_lock().
{
struct ast_channel *chan = obj, *cmp_args = arg;
return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
}
| 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 get_lock().
{
const struct ast_channel *chan = obj;
return ast_str_case_hash(chan->name);
}
| static int get_lock | ( | struct ast_channel * | chan, |
| char * | lockname, | ||
| int | try | ||
| ) | [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, 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, 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;
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(¤t->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(¤t->cond, NULL))) {
ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
ast_mutex_destroy(¤t->mutex);
ast_free(current);
AST_LIST_UNLOCK(&locklist);
return -1;
}
if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
ast_mutex_destroy(¤t->mutex);
ast_cond_destroy(¤t->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;
}
/* 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(¤t->mutex);
/* Add to requester list */
ao2_link(current->requesters, chan);
pthread_kill(broker_tid, SIGURG);
AST_LIST_UNLOCK(&locklist);
if ((!current->owner) ||
(!try && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &three_seconds)))) {
res = 0;
current->owner = chan;
current->count++;
} else {
res = -1;
}
/* Remove from requester list */
ao2_unlink(current->requesters, chan);
ast_mutex_unlock(¤t->mutex);
return res;
}
| static int load_module | ( | void | ) | [static] |
Definition at line 487 of file func_lock.c.
References ast_custom_function_register, ast_pthread_create_background, and lock_broker().
{
int res = ast_custom_function_register(&lock_function);
res |= ast_custom_function_register(&trylock_function);
res |= ast_custom_function_register(&unlock_function);
ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
return res;
}
| 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::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 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, 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 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::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 407 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 trylock_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| char * | buf, | ||
| size_t | len | ||
| ) | [static] |
Definition at line 420 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 451 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(¤t->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 363 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;
}
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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 496 of file func_lock.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 496 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] |
{
.name = "LOCK",
.read = lock_read,
.read_max = 2,
}
Definition at line 433 of file func_lock.c.
struct ast_datastore_info lock_info [static] |
{
.type = "MUTEX",
.destroy = lock_free,
.chan_fixup = lock_fixup,
}
Definition at line 102 of file func_lock.c.
Referenced by dummy_start().
struct ast_custom_function trylock_function [static] |
{
.name = "TRYLOCK",
.read = trylock_read,
.read_max = 2,
}
Definition at line 439 of file func_lock.c.
int unloading = 0 [static] |
Definition at line 99 of file func_lock.c.
struct ast_custom_function unlock_function [static] |
{
.name = "UNLOCK",
.read = unlock_read,
.read_max = 2,
}
Definition at line 445 of file func_lock.c.