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 __MODULE_INFO_SECTION | __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } |
| 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 |
| 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 494 of file func_lock.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 494 of file func_lock.c.
| static int get_lock | ( | struct ast_channel * | chan, |
| char * | lockname, | ||
| int | try | ||
| ) | [static] |
Definition at line 211 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, 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, null_cmp_cb(), null_hash_cb(), 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, *link;
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(7, null_hash_cb, null_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;
}
/* Link is just an empty flag, used to check whether more than one channel
* is contending for the lock. */
if (!(link = ao2_alloc(sizeof(*link), NULL))) {
return -1;
}
/* 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, link);
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, link);
ao2_ref(link, -1);
ast_mutex_unlock(¤t->mutex);
return res;
}
| static int load_module | ( | void | ) | [static] |
Definition at line 485 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 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::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 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, 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 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::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 408 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 null_cmp_cb | ( | void * | obj, |
| void * | arg, | ||
| int | flags | ||
| ) | [static] |
Definition at line 206 of file func_lock.c.
References CMP_MATCH.
Referenced by get_lock().
{
return obj == arg ? CMP_MATCH : 0;
}
| static int null_hash_cb | ( | const void * | obj, |
| const int | flags | ||
| ) | [static] |
| static int trylock_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| char * | buf, | ||
| size_t | len | ||
| ) | [static] |
Definition at line 421 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 449 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 364 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 __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialplan mutexes" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static] |
Definition at line 494 of file func_lock.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 494 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] |
{
.name = "LOCK",
.read = lock_read,
}
Definition at line 434 of file func_lock.c.
struct ast_datastore_info lock_info [static] |
{
.type = "MUTEX",
.destroy = lock_free,
.chan_fixup = lock_fixup,
}
Definition at line 98 of file func_lock.c.
Referenced by dummy_start().
struct ast_custom_function trylock_function [static] |
{
.name = "TRYLOCK",
.read = trylock_read,
}
Definition at line 439 of file func_lock.c.
int unloading = 0 [static] |
Definition at line 95 of file func_lock.c.
struct ast_custom_function unlock_function [static] |
{
.name = "UNLOCK",
.read = unlock_read,
}
Definition at line 444 of file func_lock.c.