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 | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "0901e4e500243c855563a2d78b0c03e4" , .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 |
| 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(), 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().
00212 { 00213 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00214 struct lock_frame *current; 00215 struct channel_lock_frame *clframe = NULL; 00216 AST_LIST_HEAD(, channel_lock_frame) *list; 00217 int res = 0, *link; 00218 struct timespec three_seconds = { .tv_sec = 3 }; 00219 00220 if (!lock_store) { 00221 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name); 00222 lock_store = ast_datastore_alloc(&lock_info, NULL); 00223 if (!lock_store) { 00224 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n"); 00225 return -1; 00226 } 00227 00228 list = ast_calloc(1, sizeof(*list)); 00229 if (!list) { 00230 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : ""); 00231 ast_datastore_free(lock_store); 00232 return -1; 00233 } 00234 00235 lock_store->data = list; 00236 AST_LIST_HEAD_INIT(list); 00237 ast_channel_datastore_add(chan, lock_store); 00238 } else 00239 list = lock_store->data; 00240 00241 /* Lock already exists? */ 00242 AST_LIST_LOCK(&locklist); 00243 AST_LIST_TRAVERSE(&locklist, current, entries) { 00244 if (strcmp(current->name, lockname) == 0) { 00245 break; 00246 } 00247 } 00248 00249 if (!current) { 00250 if (unloading) { 00251 /* Don't bother */ 00252 AST_LIST_UNLOCK(&locklist); 00253 return -1; 00254 } 00255 00256 /* Create new lock entry */ 00257 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1); 00258 if (!current) { 00259 AST_LIST_UNLOCK(&locklist); 00260 return -1; 00261 } 00262 00263 strcpy(current->name, lockname); /* SAFE */ 00264 if ((res = ast_mutex_init(¤t->mutex))) { 00265 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res)); 00266 ast_free(current); 00267 AST_LIST_UNLOCK(&locklist); 00268 return -1; 00269 } 00270 if ((res = ast_cond_init(¤t->cond, NULL))) { 00271 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res)); 00272 ast_mutex_destroy(¤t->mutex); 00273 ast_free(current); 00274 AST_LIST_UNLOCK(&locklist); 00275 return -1; 00276 } 00277 if (!(current->requesters = ao2_container_alloc(7, null_hash_cb, null_cmp_cb))) { 00278 ast_mutex_destroy(¤t->mutex); 00279 ast_cond_destroy(¤t->cond); 00280 ast_free(current); 00281 AST_LIST_UNLOCK(&locklist); 00282 return -1; 00283 } 00284 AST_LIST_INSERT_TAIL(&locklist, current, entries); 00285 } 00286 AST_LIST_UNLOCK(&locklist); 00287 00288 /* Found lock or created one - now find or create the corresponding link in the channel */ 00289 AST_LIST_LOCK(list); 00290 AST_LIST_TRAVERSE(list, clframe, list) { 00291 if (clframe->lock_frame == current) { 00292 break; 00293 } 00294 } 00295 00296 if (!clframe) { 00297 if (unloading) { 00298 /* Don't bother */ 00299 AST_LIST_UNLOCK(list); 00300 return -1; 00301 } 00302 00303 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) { 00304 ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : ""); 00305 AST_LIST_UNLOCK(list); 00306 return -1; 00307 } 00308 00309 clframe->lock_frame = current; 00310 clframe->channel = chan; 00311 AST_LIST_INSERT_TAIL(list, clframe, list); 00312 } 00313 AST_LIST_UNLOCK(list); 00314 00315 /* If we already own the lock, then we're being called recursively. 00316 * Keep track of how many times that is, because we need to unlock 00317 * the same amount, before we'll release this one. 00318 */ 00319 if (current->owner == chan) { 00320 current->count++; 00321 return 0; 00322 } 00323 00324 /* Link is just an empty flag, used to check whether more than one channel 00325 * is contending for the lock. */ 00326 if (!(link = ao2_alloc(sizeof(*link), NULL))) { 00327 return -1; 00328 } 00329 00330 /* Okay, we have both frames, so now we need to try to lock. 00331 * 00332 * Locking order: always lock locklist first. We need the 00333 * locklist lock because the broker thread counts whether 00334 * there are requesters with the locklist lock held, and we 00335 * need to hold it, so that when we send our signal, below, 00336 * to wake up the broker thread, it definitely will see that 00337 * a requester exists at that point in time. Otherwise, we 00338 * could add to the requesters after it has already seen that 00339 * that lock is unoccupied and wait forever for another signal. 00340 */ 00341 AST_LIST_LOCK(&locklist); 00342 ast_mutex_lock(¤t->mutex); 00343 /* Add to requester list */ 00344 ao2_link(current->requesters, link); 00345 pthread_kill(broker_tid, SIGURG); 00346 AST_LIST_UNLOCK(&locklist); 00347 00348 if ((!current->owner) || 00349 (!try && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &three_seconds)))) { 00350 res = 0; 00351 current->owner = chan; 00352 current->count++; 00353 } else { 00354 res = -1; 00355 } 00356 /* Remove from requester list */ 00357 ao2_unlink(current->requesters, link); 00358 ao2_ref(link, -1); 00359 ast_mutex_unlock(¤t->mutex); 00360 00361 return res; 00362 }
| 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().
00486 { 00487 int res = ast_custom_function_register(&lock_function); 00488 res |= ast_custom_function_register(&trylock_function); 00489 res |= ast_custom_function_register(&unlock_function); 00490 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL); 00491 return res; 00492 }
| 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().
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, 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::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 408 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), and get_lock().
00409 { 00410 if (chan) 00411 ast_autoservice_start(chan); 00412 00413 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len); 00414 00415 if (chan) 00416 ast_autoservice_stop(chan); 00417 00418 return 0; 00419 }
| 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().
00207 { 00208 return obj == arg ? CMP_MATCH : 0; 00209 }
| static int null_hash_cb | ( | const void * | obj, | |
| const int | flags | |||
| ) | [static] |
Definition at line 201 of file func_lock.c.
Referenced by get_lock().
| 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().
00422 { 00423 if (chan) 00424 ast_autoservice_start(chan); 00425 00426 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len); 00427 00428 if (chan) 00429 ast_autoservice_stop(chan); 00430 00431 return 0; 00432 }
| 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.
00450 { 00451 struct lock_frame *current; 00452 00453 /* Module flag */ 00454 unloading = 1; 00455 00456 AST_LIST_LOCK(&locklist); 00457 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) { 00458 /* If any locks are currently in use, then we cannot unload this module */ 00459 if (current->owner || ao2_container_count(current->requesters)) { 00460 /* Put it back */ 00461 AST_LIST_INSERT_HEAD(&locklist, current, entries); 00462 AST_LIST_UNLOCK(&locklist); 00463 unloading = 0; 00464 return -1; 00465 } 00466 ast_mutex_destroy(¤t->mutex); 00467 ao2_ref(current->requesters, -1); 00468 ast_free(current); 00469 } 00470 00471 /* No locks left, unregister functions */ 00472 ast_custom_function_unregister(&lock_function); 00473 ast_custom_function_unregister(&trylock_function); 00474 ast_custom_function_unregister(&unlock_function); 00475 00476 pthread_cancel(broker_tid); 00477 pthread_kill(broker_tid, SIGURG); 00478 pthread_join(broker_tid, NULL); 00479 00480 AST_LIST_UNLOCK(&locklist); 00481 00482 return 0; 00483 }
| 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.
00365 { 00366 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00367 struct channel_lock_frame *clframe; 00368 AST_LIST_HEAD(, channel_lock_frame) *list; 00369 00370 if (!lock_store) { 00371 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n"); 00372 ast_copy_string(buf, "0", len); 00373 return 0; 00374 } 00375 00376 if (!(list = lock_store->data)) { 00377 ast_debug(1, "This should NEVER happen\n"); 00378 ast_copy_string(buf, "0", len); 00379 return 0; 00380 } 00381 00382 /* Find item in the channel list */ 00383 AST_LIST_LOCK(list); 00384 AST_LIST_TRAVERSE(list, clframe, list) { 00385 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) { 00386 break; 00387 } 00388 } 00389 /* We never destroy anything until channel destruction, which will never 00390 * happen while this routine is executing, so we don't need to hold the 00391 * lock beyond this point. */ 00392 AST_LIST_UNLOCK(list); 00393 00394 if (!clframe) { 00395 /* We didn't have this lock in the first place */ 00396 ast_copy_string(buf, "0", len); 00397 return 0; 00398 } 00399 00400 if (--clframe->lock_frame->count == 0) { 00401 clframe->lock_frame->owner = NULL; 00402 } 00403 00404 ast_copy_string(buf, "1", len); 00405 return 0; 00406 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "0901e4e500243c855563a2d78b0c03e4" , .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.
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.
1.6.2