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.