#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_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 ast_custom_function | trylock_function |
static int | unloading = 0 |
static struct ast_custom_function | unlock_function |
Definition in file func_lock.c.
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(¤t->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(¤t->cond, NULL))) { 00273 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res)); 00274 ast_mutex_destroy(¤t->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(¤t->mutex); 00281 ast_cond_destroy(¤t->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(¤t->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(¤t->cond, ¤t->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(¤t->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.
00484 { 00485 int res = ast_custom_function_register(&lock_function); 00486 res |= ast_custom_function_register(&trylock_function); 00487 res |= ast_custom_function_register(&unlock_function); 00488 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL); 00489 return res; 00490 }
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(¤t->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 }
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().