#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 trylock) |
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 = "ac1f6a56484a8820659555499174e588" , .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 519 of file func_lock.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 519 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 ast_channels_init(), and get_lock().
00212 { 00213 struct ast_channel *chan = obj, *cmp_args = arg; 00214 return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH; 00215 }
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 ast_channels_init(), and get_lock().
00206 { 00207 const struct ast_channel *chan = obj; 00208 return ast_str_case_hash(chan->name); 00209 }
static int get_lock | ( | struct ast_channel * | chan, | |
char * | lockname, | |||
int | trylock | |||
) | [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, ast_tvnow(), 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().
00218 { 00219 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00220 struct lock_frame *current; 00221 struct channel_lock_frame *clframe = NULL; 00222 AST_LIST_HEAD(, channel_lock_frame) *list; 00223 int res = 0; 00224 struct timespec timeout = { 0, }; 00225 struct timeval now; 00226 00227 if (!lock_store) { 00228 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name); 00229 lock_store = ast_datastore_alloc(&lock_info, NULL); 00230 if (!lock_store) { 00231 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n"); 00232 return -1; 00233 } 00234 00235 list = ast_calloc(1, sizeof(*list)); 00236 if (!list) { 00237 ast_log(LOG_ERROR, 00238 "Unable to allocate datastore list head. %sLOCK will fail.\n", 00239 trylock ? "TRY" : ""); 00240 ast_datastore_free(lock_store); 00241 return -1; 00242 } 00243 00244 lock_store->data = list; 00245 AST_LIST_HEAD_INIT(list); 00246 ast_channel_datastore_add(chan, lock_store); 00247 } else 00248 list = lock_store->data; 00249 00250 /* Lock already exists? */ 00251 AST_LIST_LOCK(&locklist); 00252 AST_LIST_TRAVERSE(&locklist, current, entries) { 00253 if (strcmp(current->name, lockname) == 0) { 00254 break; 00255 } 00256 } 00257 00258 if (!current) { 00259 if (unloading) { 00260 /* Don't bother */ 00261 AST_LIST_UNLOCK(&locklist); 00262 return -1; 00263 } 00264 00265 /* Create new lock entry */ 00266 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1); 00267 if (!current) { 00268 AST_LIST_UNLOCK(&locklist); 00269 return -1; 00270 } 00271 00272 strcpy(current->name, lockname); /* SAFE */ 00273 if ((res = ast_mutex_init(¤t->mutex))) { 00274 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res)); 00275 ast_free(current); 00276 AST_LIST_UNLOCK(&locklist); 00277 return -1; 00278 } 00279 if ((res = ast_cond_init(¤t->cond, NULL))) { 00280 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res)); 00281 ast_mutex_destroy(¤t->mutex); 00282 ast_free(current); 00283 AST_LIST_UNLOCK(&locklist); 00284 return -1; 00285 } 00286 if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) { 00287 ast_mutex_destroy(¤t->mutex); 00288 ast_cond_destroy(¤t->cond); 00289 ast_free(current); 00290 AST_LIST_UNLOCK(&locklist); 00291 return -1; 00292 } 00293 AST_LIST_INSERT_TAIL(&locklist, current, entries); 00294 } 00295 AST_LIST_UNLOCK(&locklist); 00296 00297 /* Found lock or created one - now find or create the corresponding link in the channel */ 00298 AST_LIST_LOCK(list); 00299 AST_LIST_TRAVERSE(list, clframe, list) { 00300 if (clframe->lock_frame == current) { 00301 break; 00302 } 00303 } 00304 00305 if (!clframe) { 00306 if (unloading) { 00307 /* Don't bother */ 00308 AST_LIST_UNLOCK(list); 00309 return -1; 00310 } 00311 00312 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) { 00313 ast_log(LOG_ERROR, 00314 "Unable to allocate channel lock frame. %sLOCK will fail.\n", 00315 trylock ? "TRY" : ""); 00316 AST_LIST_UNLOCK(list); 00317 return -1; 00318 } 00319 00320 clframe->lock_frame = current; 00321 clframe->channel = chan; 00322 AST_LIST_INSERT_TAIL(list, clframe, list); 00323 } 00324 AST_LIST_UNLOCK(list); 00325 00326 /* If we already own the lock, then we're being called recursively. 00327 * Keep track of how many times that is, because we need to unlock 00328 * the same amount, before we'll release this one. 00329 */ 00330 if (current->owner == chan) { 00331 current->count++; 00332 return 0; 00333 } 00334 00335 /* Okay, we have both frames, so now we need to try to lock. 00336 * 00337 * Locking order: always lock locklist first. We need the 00338 * locklist lock because the broker thread counts whether 00339 * there are requesters with the locklist lock held, and we 00340 * need to hold it, so that when we send our signal, below, 00341 * to wake up the broker thread, it definitely will see that 00342 * a requester exists at that point in time. Otherwise, we 00343 * could add to the requesters after it has already seen that 00344 * that lock is unoccupied and wait forever for another signal. 00345 */ 00346 AST_LIST_LOCK(&locklist); 00347 ast_mutex_lock(¤t->mutex); 00348 /* Add to requester list */ 00349 ao2_link(current->requesters, chan); 00350 pthread_kill(broker_tid, SIGURG); 00351 AST_LIST_UNLOCK(&locklist); 00352 00353 /* Wait up to three seconds from now for LOCK. */ 00354 now = ast_tvnow(); 00355 timeout.tv_sec = now.tv_sec + 3; 00356 timeout.tv_nsec = now.tv_usec * 1000; 00357 00358 if (!current->owner 00359 || (!trylock 00360 && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &timeout)))) { 00361 res = 0; 00362 current->owner = chan; 00363 current->count++; 00364 } else { 00365 res = -1; 00366 } 00367 /* Remove from requester list */ 00368 ao2_unlink(current->requesters, chan); 00369 ast_mutex_unlock(¤t->mutex); 00370 00371 return res; 00372 }
static int load_module | ( | void | ) | [static] |
Definition at line 503 of file func_lock.c.
References ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, ast_pthread_create_background, AST_PTHREADT_NULL, lock_broker(), lock_function, LOG_ERROR, trylock_function, unload_module, and unlock_function.
00504 { 00505 int res = ast_custom_function_register(&lock_function); 00506 res |= ast_custom_function_register(&trylock_function); 00507 res |= ast_custom_function_register(&unlock_function); 00508 00509 if (ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL)) { 00510 ast_log(LOG_ERROR, "Failed to start lock broker thread. Unloading func_lock module.\n"); 00511 broker_tid = AST_PTHREADT_NULL; 00512 unload_module(); 00513 return AST_MODULE_LOAD_DECLINE; 00514 } 00515 00516 return res; 00517 }
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::entries, lock_frame::mutex, lock_frame::owner, and lock_frame::requesters.
Referenced by load_module().
00170 { 00171 struct lock_frame *frame; 00172 struct timespec forever = { 1000000, 0 }; 00173 for (;;) { 00174 int found_requester = 0; 00175 00176 /* Test for cancel outside of the lock */ 00177 pthread_testcancel(); 00178 AST_LIST_LOCK(&locklist); 00179 00180 AST_LIST_TRAVERSE(&locklist, frame, entries) { 00181 if (ao2_container_count(frame->requesters)) { 00182 found_requester++; 00183 ast_mutex_lock(&frame->mutex); 00184 if (!frame->owner) { 00185 ast_cond_signal(&frame->cond); 00186 } 00187 ast_mutex_unlock(&frame->mutex); 00188 } 00189 } 00190 00191 AST_LIST_UNLOCK(&locklist); 00192 pthread_testcancel(); 00193 00194 /* If there are no requesters, then wait for a signal */ 00195 if (!found_requester) { 00196 nanosleep(&forever, NULL); 00197 } else { 00198 sched_yield(); 00199 } 00200 } 00201 /* Not reached */ 00202 return NULL; 00203 }
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, lock_info, and lock_frame::owner.
00148 { 00149 struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL); 00150 AST_LIST_HEAD(, channel_lock_frame) *list; 00151 struct channel_lock_frame *clframe = NULL; 00152 00153 if (!lock_store) { 00154 return; 00155 } 00156 list = lock_store->data; 00157 00158 AST_LIST_LOCK(list); 00159 AST_LIST_TRAVERSE(list, clframe, list) { 00160 if (clframe->lock_frame->owner == oldchan) { 00161 clframe->lock_frame->owner = newchan; 00162 } 00163 /* We don't move requesters, because the thread stack is different */ 00164 clframe->channel = newchan; 00165 } 00166 AST_LIST_UNLOCK(list); 00167 }
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::list, channel_lock_frame::lock_frame, and lock_frame::owner.
00130 { 00131 AST_LIST_HEAD(, channel_lock_frame) *oldlist = data; 00132 struct channel_lock_frame *clframe; 00133 AST_LIST_LOCK(oldlist); 00134 while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00135 /* Only unlock if we own the lock */ 00136 if (clframe->channel == clframe->lock_frame->owner) { 00137 clframe->lock_frame->count = 0; 00138 clframe->lock_frame->owner = NULL; 00139 } 00140 ast_free(clframe); 00141 } 00142 AST_LIST_UNLOCK(oldlist); 00143 AST_LIST_HEAD_DESTROY(oldlist); 00144 ast_free(oldlist); 00145 }
static int lock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 423 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().
00424 { 00425 if (!chan) { 00426 return -1; 00427 } 00428 ast_autoservice_start(chan); 00429 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len); 00430 ast_autoservice_stop(chan); 00431 00432 return 0; 00433 }
static int trylock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 435 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().
00436 { 00437 if (!chan) { 00438 return -1; 00439 } 00440 ast_autoservice_start(chan); 00441 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len); 00442 ast_autoservice_stop(chan); 00443 00444 return 0; 00445 }
static int unload_module | ( | void | ) | [static] |
Definition at line 465 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, AST_PTHREADT_NULL, lock_frame::entries, lock_function, lock_frame::mutex, lock_frame::owner, lock_frame::requesters, trylock_function, and unlock_function.
00466 { 00467 struct lock_frame *current; 00468 00469 /* Module flag */ 00470 unloading = 1; 00471 00472 AST_LIST_LOCK(&locklist); 00473 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) { 00474 /* If any locks are currently in use, then we cannot unload this module */ 00475 if (current->owner || ao2_container_count(current->requesters)) { 00476 /* Put it back */ 00477 AST_LIST_INSERT_HEAD(&locklist, current, entries); 00478 AST_LIST_UNLOCK(&locklist); 00479 unloading = 0; 00480 return -1; 00481 } 00482 ast_mutex_destroy(¤t->mutex); 00483 ao2_ref(current->requesters, -1); 00484 ast_free(current); 00485 } 00486 00487 /* No locks left, unregister functions */ 00488 ast_custom_function_unregister(&lock_function); 00489 ast_custom_function_unregister(&trylock_function); 00490 ast_custom_function_unregister(&unlock_function); 00491 00492 if (broker_tid != AST_PTHREADT_NULL) { 00493 pthread_cancel(broker_tid); 00494 pthread_kill(broker_tid, SIGURG); 00495 pthread_join(broker_tid, NULL); 00496 } 00497 00498 AST_LIST_UNLOCK(&locklist); 00499 00500 return 0; 00501 }
static int unlock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 374 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.
00375 { 00376 struct ast_datastore *lock_store; 00377 struct channel_lock_frame *clframe; 00378 AST_LIST_HEAD(, channel_lock_frame) *list; 00379 00380 if (!chan) { 00381 return -1; 00382 } 00383 00384 lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00385 if (!lock_store) { 00386 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n"); 00387 ast_copy_string(buf, "0", len); 00388 return 0; 00389 } 00390 00391 if (!(list = lock_store->data)) { 00392 ast_debug(1, "This should NEVER happen\n"); 00393 ast_copy_string(buf, "0", len); 00394 return 0; 00395 } 00396 00397 /* Find item in the channel list */ 00398 AST_LIST_LOCK(list); 00399 AST_LIST_TRAVERSE(list, clframe, list) { 00400 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) { 00401 break; 00402 } 00403 } 00404 /* We never destroy anything until channel destruction, which will never 00405 * happen while this routine is executing, so we don't need to hold the 00406 * lock beyond this point. */ 00407 AST_LIST_UNLOCK(list); 00408 00409 if (!clframe) { 00410 /* We didn't have this lock in the first place */ 00411 ast_copy_string(buf, "0", len); 00412 return 0; 00413 } 00414 00415 if (--clframe->lock_frame->count == 0) { 00416 clframe->lock_frame->owner = NULL; 00417 } 00418 00419 ast_copy_string(buf, "1", len); 00420 return 0; 00421 }
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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 519 of file func_lock.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 519 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] |
Initial value:
{ .name = "LOCK", .read = lock_read, .read_max = 2, }
Definition at line 447 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 102 of file func_lock.c.
Referenced by dummy_start(), get_lock(), lock_fixup(), and unlock_read().
struct ast_custom_function trylock_function [static] |
Initial value:
{ .name = "TRYLOCK", .read = trylock_read, .read_max = 2, }
Definition at line 453 of file func_lock.c.
Referenced by load_module(), and unload_module().
int unloading = 0 [static] |
Definition at line 99 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 459 of file func_lock.c.
Referenced by load_module(), and unload_module().