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 211 of file func_lock.c.
References CMP_MATCH.
Referenced by 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().
Referenced by get_lock().
00206 { 00207 const struct ast_channel *chan = obj; 00208 return ast_str_case_hash(chan->name); 00209 }
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 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(), ast_datastore::data, and LOG_ERROR.
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(), LOG_ERROR, and unload_module.
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, and ast_mutex_unlock.
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, and ast_datastore::data.
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, and AST_LIST_UNLOCK.
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, and AST_PTHREADT_NULL.
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(), ast_datastore::data, and LOG_WARNING.
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 }
pthread_t broker_tid = AST_PTHREADT_NULL [static] |
Definition at line 100 of file func_lock.c.
struct ast_custom_function lock_function [static] |
{ .name = "LOCK", .read = lock_read, .read_max = 2, }
Definition at line 447 of file func_lock.c.
struct ast_datastore_info lock_info [static] |
{ .type = "MUTEX", .destroy = lock_free, .chan_fixup = lock_fixup, }
Definition at line 102 of file func_lock.c.
struct ast_custom_function trylock_function [static] |
{ .name = "TRYLOCK", .read = trylock_read, .read_max = 2, }
Definition at line 453 of file func_lock.c.
int unloading = 0 [static] |
Definition at line 99 of file func_lock.c.
struct ast_custom_function unlock_function [static] |
{ .name = "UNLOCK", .read = unlock_read, .read_max = 2, }
Definition at line 459 of file func_lock.c.