#include "asterisk.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"
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_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_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 = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } |
static const struct ast_module_info * | ast_module_info = &__mod_info |
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 350 of file func_lock.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 350 of file func_lock.c.
static int get_lock | ( | struct ast_channel * | chan, | |
char * | lockname, | |||
int | try | |||
) | [static] |
Definition at line 89 of file func_lock.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), ast_debug, AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_trylock(), ast_tvdiff_ms(), ast_tvnow(), chan, lock_frame::channel, 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, and ast_channel::name.
Referenced by lock_read(), and trylock_read().
00090 { 00091 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00092 struct lock_frame *current; 00093 struct channel_lock_frame *clframe = NULL, *save_clframe = NULL; 00094 AST_LIST_HEAD(, channel_lock_frame) *list; 00095 int res, count_channel_locks = 0; 00096 00097 if (!lock_store) { 00098 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name); 00099 lock_store = ast_channel_datastore_alloc(&lock_info, NULL); 00100 if (!lock_store) { 00101 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n"); 00102 return -1; 00103 } 00104 00105 list = ast_calloc(1, sizeof(*list)); 00106 if (!list) { 00107 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %sLOCK will fail.\n", try ? "TRY" : ""); 00108 ast_channel_datastore_free(lock_store); 00109 return -1; 00110 } 00111 00112 lock_store->data = list; 00113 AST_LIST_HEAD_INIT(list); 00114 ast_channel_datastore_add(chan, lock_store); 00115 } else 00116 list = lock_store->data; 00117 00118 /* Lock already exists? */ 00119 AST_LIST_LOCK(&locklist); 00120 AST_LIST_TRAVERSE(&locklist, current, entries) { 00121 if (strcmp(current->name, lockname) == 0) { 00122 break; 00123 } 00124 } 00125 00126 if (!current) { 00127 if (unloading) { 00128 /* Don't bother */ 00129 AST_LIST_UNLOCK(&locklist); 00130 return -1; 00131 } 00132 00133 /* Create new lock entry */ 00134 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1); 00135 if (!current) { 00136 AST_LIST_UNLOCK(&locklist); 00137 return -1; 00138 } 00139 00140 strcpy((char *)current + sizeof(*current), lockname); 00141 ast_mutex_init(¤t->mutex); 00142 AST_LIST_INSERT_TAIL(&locklist, current, entries); 00143 } 00144 AST_LIST_UNLOCK(&locklist); 00145 00146 /* Found lock or created one - now find or create the corresponding link in the channel */ 00147 AST_LIST_LOCK(list); 00148 AST_LIST_TRAVERSE(list, clframe, list) { 00149 if (clframe->lock_frame == current) 00150 save_clframe = clframe; 00151 00152 /* Only count mutexes that we currently hold */ 00153 if (clframe->lock_frame->channel == chan) 00154 count_channel_locks++; 00155 } 00156 00157 if (save_clframe) { 00158 clframe = save_clframe; 00159 } else { 00160 if (unloading) { 00161 /* Don't bother */ 00162 AST_LIST_UNLOCK(list); 00163 return -1; 00164 } 00165 00166 clframe = ast_calloc(1, sizeof(*clframe)); 00167 if (!clframe) { 00168 ast_log(LOG_ERROR, "Unable to allocate channel lock frame. %sLOCK will fail.\n", try ? "TRY" : ""); 00169 AST_LIST_UNLOCK(list); 00170 return -1; 00171 } 00172 00173 clframe->lock_frame = current; 00174 clframe->channel = chan; 00175 /* Count the lock just created */ 00176 count_channel_locks++; 00177 AST_LIST_INSERT_TAIL(list, clframe, list); 00178 } 00179 AST_LIST_UNLOCK(list); 00180 00181 /* Okay, we have both frames, so now we need to try to lock the mutex. */ 00182 if (count_channel_locks > 1) { 00183 struct timeval start = ast_tvnow(); 00184 for (;;) { 00185 if ((res = ast_mutex_trylock(¤t->mutex)) == 0) 00186 break; 00187 if (ast_tvdiff_ms(ast_tvnow(), start) > 3000) 00188 break; /* bail after 3 seconds of waiting */ 00189 usleep(1); 00190 } 00191 } else { 00192 /* If the channel doesn't have any locks so far, then there's no possible deadlock. */ 00193 res = try ? ast_mutex_trylock(¤t->mutex) : ast_mutex_lock(¤t->mutex); 00194 } 00195 00196 if (res == 0) { 00197 current->count++; 00198 current->channel = chan; 00199 } 00200 00201 return res; 00202 }
static int load_module | ( | void | ) | [static] |
Definition at line 342 of file func_lock.c.
References ast_custom_function_register, lock_function, trylock_function, and unlock_function.
00343 { 00344 int res = ast_custom_function_register(&lock_function); 00345 res |= ast_custom_function_register(&trylock_function); 00346 res |= ast_custom_function_register(&unlock_function); 00347 return res; 00348 }
static void lock_free | ( | void * | data | ) | [static] |
Definition at line 68 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, ast_mutex_unlock(), lock_frame::channel, channel_lock_frame::channel, lock_frame::count, channel_lock_frame::list, channel_lock_frame::lock_frame, and lock_frame::mutex.
00069 { 00070 AST_LIST_HEAD(, channel_lock_frame) *oldlist = data; 00071 struct channel_lock_frame *clframe; 00072 AST_LIST_LOCK(oldlist); 00073 while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) { 00074 /* Only unlock if we own the lock */ 00075 if (clframe->channel == clframe->lock_frame->channel) { 00076 clframe->lock_frame->channel = NULL; 00077 while (clframe->lock_frame->count > 0) { 00078 clframe->lock_frame->count--; 00079 ast_mutex_unlock(&clframe->lock_frame->mutex); 00080 } 00081 } 00082 ast_free(clframe); 00083 } 00084 AST_LIST_UNLOCK(oldlist); 00085 AST_LIST_HEAD_DESTROY(oldlist); 00086 ast_free(oldlist); 00087 }
static int lock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 251 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), chan, and get_lock().
00252 { 00253 if (chan) 00254 ast_autoservice_start(chan); 00255 00256 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len); 00257 00258 if (chan) 00259 ast_autoservice_stop(chan); 00260 00261 return 0; 00262 }
static int trylock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 264 of file func_lock.c.
References ast_autoservice_start(), ast_autoservice_stop(), ast_copy_string(), chan, and get_lock().
00265 { 00266 if (chan) 00267 ast_autoservice_start(chan); 00268 00269 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len); 00270 00271 if (chan) 00272 ast_autoservice_stop(chan); 00273 00274 return 0; 00275 }
static int unload_module | ( | void | ) | [static] |
Definition at line 312 of file func_lock.c.
References 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::channel, lock_frame::entries, lock_function, lock_frame::mutex, trylock_function, and unlock_function.
00313 { 00314 struct lock_frame *current; 00315 00316 /* Module flag */ 00317 unloading = 1; 00318 00319 AST_LIST_LOCK(&locklist); 00320 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) { 00321 /* If any locks are currently in use, then we cannot unload this module */ 00322 if (current->channel) { 00323 /* Put it back */ 00324 AST_LIST_INSERT_HEAD(&locklist, current, entries); 00325 AST_LIST_UNLOCK(&locklist); 00326 unloading = 0; 00327 return -1; 00328 } 00329 ast_mutex_destroy(¤t->mutex); 00330 ast_free(current); 00331 } 00332 00333 /* No locks left, unregister functions */ 00334 ast_custom_function_unregister(&lock_function); 00335 ast_custom_function_unregister(&trylock_function); 00336 ast_custom_function_unregister(&unlock_function); 00337 00338 AST_LIST_UNLOCK(&locklist); 00339 return 0; 00340 }
static int unlock_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 204 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_mutex_unlock(), chan, lock_frame::channel, lock_frame::count, ast_datastore::data, channel_lock_frame::list, channel_lock_frame::lock_frame, lock_info, LOG_WARNING, lock_frame::mutex, and lock_frame::name.
00205 { 00206 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL); 00207 struct channel_lock_frame *clframe; 00208 AST_LIST_HEAD(, channel_lock_frame) *list; 00209 00210 if (!lock_store) { 00211 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n"); 00212 ast_copy_string(buf, "0", len); 00213 return 0; 00214 } 00215 00216 if (!(list = lock_store->data)) { 00217 ast_debug(1, "This should NEVER happen\n"); 00218 ast_copy_string(buf, "0", len); 00219 return 0; 00220 } 00221 00222 /* Find item in the channel list */ 00223 AST_LIST_LOCK(list); 00224 AST_LIST_TRAVERSE(list, clframe, list) { 00225 if (clframe->lock_frame && clframe->lock_frame->channel == chan && strcmp(clframe->lock_frame->name, data) == 0) { 00226 break; 00227 } 00228 } 00229 /* We never destroy anything until channel destruction, which will never 00230 * happen while this routine is executing, so we don't need to hold the 00231 * lock beyond this point. */ 00232 AST_LIST_UNLOCK(list); 00233 00234 if (!clframe) { 00235 /* We didn't have this lock in the first place */ 00236 ast_copy_string(buf, "0", len); 00237 return 0; 00238 } 00239 00240 /* Decrement before we release, because if a channel is waiting on the 00241 * mutex, there's otherwise a race to alter count. */ 00242 clframe->lock_frame->count--; 00243 /* If we get another lock, this one shouldn't count against us for deadlock avoidance. */ 00244 clframe->lock_frame->channel = NULL; 00245 ast_mutex_unlock(&clframe->lock_frame->mutex); 00246 00247 ast_copy_string(buf, "1", len); 00248 return 0; 00249 }
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 = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 350 of file func_lock.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 350 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, }
Definition at line 45 of file func_lock.c.
Referenced by dummy_start(), get_lock(), and unlock_read().
struct ast_custom_function trylock_function [static] |
int unloading = 0 [static] |
Definition at line 43 of file func_lock.c.
struct ast_custom_function unlock_function [static] |