#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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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 |
Definition in file func_lock.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 467 of file func_lock.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 467 of file func_lock.c.
static int get_lock | ( | struct ast_channel * | chan, | |
char * | lockname, | |||
int | try | |||
) | [static] |
Definition at line 164 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(), chan, 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, null_cmp_cb(), null_hash_cb(), lock_frame::owner, and lock_frame::requesters.
Referenced by lock_read(), and trylock_read().
00165 { 00166 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00167 struct lock_frame *current; 00168 struct channel_lock_frame *clframe = NULL; 00169 AST_LIST_HEAD(, channel_lock_frame) *list; 00170 int res = 0, *link; 00171 struct timespec three_seconds = { .tv_sec = 3 }; 00172 00173 if (!lock_store) { 00174 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name); 00175 lock_store = ast_datastore_alloc(&lock_info, NULL); 00176 if (!lock_store) { 00177 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n"); 00178 return -1; 00179 } 00180 00181 list = ast_calloc(1, sizeof(*list)); 00182 if (!list) { 00183 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : ""); 00184 ast_datastore_free(lock_store); 00185 return -1; 00186 } 00187 00188 lock_store->data = list; 00189 AST_LIST_HEAD_INIT(list); 00190 ast_channel_datastore_add(chan, lock_store); 00191 } else 00192 list = lock_store->data; 00193 00194 /* Lock already exists? */ 00195 AST_LIST_LOCK(&locklist); 00196 AST_LIST_TRAVERSE(&locklist, current, entries) { 00197 if (strcmp(current->name, lockname) == 0) { 00198 break; 00199 } 00200 } 00201 00202 if (!current) { 00203 if (unloading) { 00204 /* Don't bother */ 00205 AST_LIST_UNLOCK(&locklist); 00206 return -1; 00207 } 00208 00209 /* Create new lock entry */ 00210 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1); 00211 if (!current) { 00212 AST_LIST_UNLOCK(&locklist); 00213 return -1; 00214 } 00215 00216 strcpy(current->name, lockname); /* SAFE */ 00217 if ((res = ast_mutex_init(¤t->mutex))) { 00218 ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res)); 00219 ast_free(current); 00220 AST_LIST_UNLOCK(&locklist); 00221 return -1; 00222 } 00223 if ((res = ast_cond_init(¤t->cond, NULL))) { 00224 ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res)); 00225 ast_mutex_destroy(¤t->mutex); 00226 ast_free(current); 00227 AST_LIST_UNLOCK(&locklist); 00228 return -1; 00229 } 00230 if (!(current->requesters = ao2_container_alloc(7, null_hash_cb, null_cmp_cb))) { 00231 ast_mutex_destroy(¤t->mutex); 00232 ast_cond_destroy(¤t->cond); 00233 ast_free(current); 00234 AST_LIST_UNLOCK(&locklist); 00235 return -1; 00236 } 00237 AST_LIST_INSERT_TAIL(&locklist, current, entries); 00238 } 00239 AST_LIST_UNLOCK(&locklist); 00240 00241 /* Found lock or created one - now find or create the corresponding link in the channel */ 00242 AST_LIST_LOCK(list); 00243 AST_LIST_TRAVERSE(list, clframe, list) { 00244 if (clframe->lock_frame == current) { 00245 break; 00246 } 00247 } 00248 00249 if (!clframe) { 00250 if (unloading) { 00251 /* Don't bother */ 00252 AST_LIST_UNLOCK(list); 00253 return -1; 00254 } 00255 00256 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) { 00257 ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : ""); 00258 AST_LIST_UNLOCK(list); 00259 return -1; 00260 } 00261 00262 clframe->lock_frame = current; 00263 clframe->channel = chan; 00264 AST_LIST_INSERT_TAIL(list, clframe, list); 00265 } 00266 AST_LIST_UNLOCK(list); 00267 00268 /* If we already own the lock, then we're being called recursively. 00269 * Keep track of how many times that is, because we need to unlock 00270 * the same amount, before we'll release this one. 00271 */ 00272 if (current->owner == chan) { 00273 current->count++; 00274 return 0; 00275 } 00276 00277 /* Link is just an empty flag, used to check whether more than one channel 00278 * is contending for the lock. */ 00279 if (!(link = ao2_alloc(sizeof(*link), NULL))) { 00280 return -1; 00281 } 00282 00283 /* Okay, we have both frames, so now we need to try to lock. 00284 * 00285 * Locking order: always lock locklist first. We need the 00286 * locklist lock because the broker thread counts whether 00287 * there are requesters with the locklist lock held, and we 00288 * need to hold it, so that when we send our signal, below, 00289 * to wake up the broker thread, it definitely will see that 00290 * a requester exists at that point in time. Otherwise, we 00291 * could add to the requesters after it has already seen that 00292 * that lock is unoccupied and wait forever for another signal. 00293 */ 00294 AST_LIST_LOCK(&locklist); 00295 ast_mutex_lock(¤t->mutex); 00296 /* Add to requester list */ 00297 ao2_link(current->requesters, link); 00298 pthread_kill(broker_tid, SIGURG); 00299 AST_LIST_UNLOCK(&locklist); 00300 00301 if ((!current->owner) || 00302 (!try && !(res = ast_cond_timedwait(¤t->cond, ¤t->mutex, &three_seconds)))) { 00303 res = 0; 00304 current->owner = chan; 00305 current->count++; 00306 } else { 00307 res = -1; 00308 } 00309 /* Remove from requester list */ 00310 ao2_unlink(current->requesters, link); 00311 ao2_ref(link, -1); 00312 ast_mutex_unlock(¤t->mutex); 00313 00314 return res; 00315 }
static int load_module | ( | void | ) | [static] |
Definition at line 458 of file func_lock.c.
References ast_custom_function_register, ast_pthread_create_background, lock_broker(), lock_function, trylock_function, and unlock_function.
00459 { 00460 int res = ast_custom_function_register(&lock_function); 00461 res |= ast_custom_function_register(&trylock_function); 00462 res |= ast_custom_function_register(&unlock_function); 00463 ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL); 00464 return res; 00465 }
static void* lock_broker | ( | void * | unused | ) | [static] |
Definition at line 118 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().
00119 { 00120 struct lock_frame *frame; 00121 struct timespec forever = { 1000000, 0 }; 00122 for (;;) { 00123 int found_requester = 0; 00124 00125 /* Test for cancel outside of the lock */ 00126 pthread_testcancel(); 00127 AST_LIST_LOCK(&locklist); 00128 00129 AST_LIST_TRAVERSE(&locklist, frame, entries) { 00130 if (ao2_container_count(frame->requesters)) { 00131 found_requester++; 00132 ast_mutex_lock(&frame->mutex); 00133 if (!frame->owner) { 00134 ast_cond_signal(&frame->cond); 00135 } 00136 ast_mutex_unlock(&frame->mutex); 00137 } 00138 } 00139 00140 AST_LIST_UNLOCK(&locklist); 00141 pthread_testcancel(); 00142 00143 /* If there are no requesters, then wait for a signal */ 00144 if (!found_requester) { 00145 nanosleep(&forever, NULL); 00146 } else { 00147 sched_yield(); 00148 } 00149 } 00150 /* Not reached */ 00151 return NULL; 00152 }
static void lock_fixup | ( | void * | data, | |
struct ast_channel * | oldchan, | |||
struct ast_channel * | newchan | |||
) | [static] |
Definition at line 96 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.
00097 { 00098 struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL); 00099 AST_LIST_HEAD(, channel_lock_frame) *list; 00100 struct channel_lock_frame *clframe = NULL; 00101 00102 if (!lock_store) { 00103 return; 00104 } 00105 list = lock_store->data; 00106 00107 AST_LIST_LOCK(list); 00108 AST_LIST_TRAVERSE(list, clframe, list) { 00109 if (clframe->lock_frame->owner == oldchan) { 00110 clframe->lock_frame->owner = newchan; 00111 } 00112 /* We don't move requesters, because the thread stack is different */ 00113 clframe->channel = newchan; 00114 } 00115 AST_LIST_UNLOCK(list); 00116 }
static void lock_free | ( | void * | data | ) | [static] |
Definition at line 78 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.
00079 { 00080 AST_LIST_HEAD(, channel_lock_frame) *oldlist = data; 00081 struct channel_lock_frame *clframe; 00082 AST_LIST_LOCK(oldlist); 00083 while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00084 /* Only unlock if we own the lock */ 00085 if (clframe->channel == clframe->lock_frame->owner) { 00086 clframe->lock_frame->count = 0; 00087 clframe->lock_frame->owner = NULL; 00088 } 00089 ast_free(clframe); 00090 } 00091 AST_LIST_UNLOCK(oldlist); 00092 AST_LIST_HEAD_DESTROY(oldlist); 00093 ast_free(oldlist); 00094 }
static int lock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 361 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), chan, and get_lock().
00362 { 00363 if (chan) 00364 ast_autoservice_start(chan); 00365 00366 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len); 00367 00368 if (chan) 00369 ast_autoservice_stop(chan); 00370 00371 return 0; 00372 }
static int null_cmp_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 159 of file func_lock.c.
References CMP_MATCH.
Referenced by get_lock().
00160 { 00161 return obj == arg ? CMP_MATCH : 0; 00162 }
static int null_hash_cb | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
static int trylock_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_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), chan, and get_lock().
00375 { 00376 if (chan) 00377 ast_autoservice_start(chan); 00378 00379 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len); 00380 00381 if (chan) 00382 ast_autoservice_stop(chan); 00383 00384 return 0; 00385 }
static int unload_module | ( | void | ) | [static] |
Definition at line 422 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.
00423 { 00424 struct lock_frame *current; 00425 00426 /* Module flag */ 00427 unloading = 1; 00428 00429 AST_LIST_LOCK(&locklist); 00430 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) { 00431 /* If any locks are currently in use, then we cannot unload this module */ 00432 if (current->owner || ao2_container_count(current->requesters)) { 00433 /* Put it back */ 00434 AST_LIST_INSERT_HEAD(&locklist, current, entries); 00435 AST_LIST_UNLOCK(&locklist); 00436 unloading = 0; 00437 return -1; 00438 } 00439 ast_mutex_destroy(¤t->mutex); 00440 ao2_ref(current->requesters, -1); 00441 ast_free(current); 00442 } 00443 00444 /* No locks left, unregister functions */ 00445 ast_custom_function_unregister(&lock_function); 00446 ast_custom_function_unregister(&trylock_function); 00447 ast_custom_function_unregister(&unlock_function); 00448 00449 pthread_cancel(broker_tid); 00450 pthread_kill(broker_tid, SIGURG); 00451 pthread_join(broker_tid, NULL); 00452 00453 AST_LIST_UNLOCK(&locklist); 00454 00455 return 0; 00456 }
static int unlock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 317 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(), chan, 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.
00318 { 00319 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00320 struct channel_lock_frame *clframe; 00321 AST_LIST_HEAD(, channel_lock_frame) *list; 00322 00323 if (!lock_store) { 00324 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n"); 00325 ast_copy_string(buf, "0", len); 00326 return 0; 00327 } 00328 00329 if (!(list = lock_store->data)) { 00330 ast_debug(1, "This should NEVER happen\n"); 00331 ast_copy_string(buf, "0", len); 00332 return 0; 00333 } 00334 00335 /* Find item in the channel list */ 00336 AST_LIST_LOCK(list); 00337 AST_LIST_TRAVERSE(list, clframe, list) { 00338 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) { 00339 break; 00340 } 00341 } 00342 /* We never destroy anything until channel destruction, which will never 00343 * happen while this routine is executing, so we don't need to hold the 00344 * lock beyond this point. */ 00345 AST_LIST_UNLOCK(list); 00346 00347 if (!clframe) { 00348 /* We didn't have this lock in the first place */ 00349 ast_copy_string(buf, "0", len); 00350 return 0; 00351 } 00352 00353 if (--clframe->lock_frame->count == 0) { 00354 clframe->lock_frame->owner = NULL; 00355 } 00356 00357 ast_copy_string(buf, "1", len); 00358 return 0; 00359 }
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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 467 of file func_lock.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 467 of file func_lock.c.
pthread_t broker_tid = AST_PTHREADT_NULL [static] |
Definition at line 49 of file func_lock.c.
struct ast_custom_function lock_function [static] |
struct ast_datastore_info lock_info [static] |
Initial value:
{ .type = "MUTEX", .destroy = lock_free, .chan_fixup = lock_fixup, }
Definition at line 51 of file func_lock.c.
Referenced by dummy_start(), get_lock(), lock_fixup(), and unlock_read().
struct ast_custom_function trylock_function [static] |
int unloading = 0 [static] |
Definition at line 48 of file func_lock.c.
struct ast_custom_function unlock_function [static] |