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