00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 89512 $")
00032
00033 #include "asterisk/lock.h"
00034 #include "asterisk/file.h"
00035 #include "asterisk/channel.h"
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/module.h"
00038 #include "asterisk/linkedlists.h"
00039
00040 AST_LIST_HEAD_STATIC(locklist, lock_frame);
00041
00042 static void lock_free(void *data);
00043 static int unloading = 0;
00044
00045 static struct ast_datastore_info lock_info = {
00046 .type = "MUTEX",
00047 .destroy = lock_free,
00048 };
00049
00050 struct lock_frame {
00051 AST_LIST_ENTRY(lock_frame) entries;
00052 ast_mutex_t mutex;
00053
00054 unsigned int count;
00055
00056 struct ast_channel *channel;
00057
00058 char name[0];
00059 };
00060
00061 struct channel_lock_frame {
00062 AST_LIST_ENTRY(channel_lock_frame) list;
00063
00064 struct ast_channel *channel;
00065 struct lock_frame *lock_frame;
00066 };
00067
00068 static void lock_free(void *data)
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
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 }
00088
00089 static int get_lock(struct ast_channel *chan, char *lockname, int try)
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
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
00129 AST_LIST_UNLOCK(&locklist);
00130 return -1;
00131 }
00132
00133
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
00147 AST_LIST_LOCK(list);
00148 AST_LIST_TRAVERSE(list, clframe, list) {
00149 if (clframe->lock_frame == current)
00150 save_clframe = clframe;
00151
00152
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
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
00176 count_channel_locks++;
00177 AST_LIST_INSERT_TAIL(list, clframe, list);
00178 }
00179 AST_LIST_UNLOCK(list);
00180
00181
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;
00189 usleep(1);
00190 }
00191 } else {
00192
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 }
00203
00204 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
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
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
00230
00231
00232 AST_LIST_UNLOCK(list);
00233
00234 if (!clframe) {
00235
00236 ast_copy_string(buf, "0", len);
00237 return 0;
00238 }
00239
00240
00241
00242 clframe->lock_frame->count--;
00243
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 }
00250
00251 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
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 }
00263
00264 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
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 }
00276
00277 static struct ast_custom_function lock_function = {
00278 .name = "LOCK",
00279 .synopsis = "Attempt to obtain a named mutex",
00280 .desc =
00281 "Attempts to grab a named lock exclusively, and prevents other channels from\n"
00282 "obtaining the same lock. LOCK will wait for the lock to become available.\n"
00283 "Returns 1 if the lock was obtained or 0 on error.\n\n"
00284 "Note: to avoid the possibility of a deadlock, LOCK will only attempt to\n"
00285 "obtain the lock for 3 seconds if the channel already has another lock.\n",
00286 .syntax = "LOCK(<lockname>)",
00287 .read = lock_read,
00288 };
00289
00290 static struct ast_custom_function trylock_function = {
00291 .name = "TRYLOCK",
00292 .synopsis = "Attempt to obtain a named mutex",
00293 .desc =
00294 "Attempts to grab a named lock exclusively, and prevents other channels\n"
00295 "from obtaining the same lock. Returns 1 if the lock was available or 0\n"
00296 "otherwise.\n",
00297 .syntax = "TRYLOCK(<lockname>)",
00298 .read = trylock_read,
00299 };
00300
00301 static struct ast_custom_function unlock_function = {
00302 .name = "UNLOCK",
00303 .synopsis = "Unlocks a named mutex",
00304 .desc =
00305 "Unlocks a previously locked mutex. Note that it is generally unnecessary to\n"
00306 "unlock in a hangup routine, as any locks held are automatically freed when the\n"
00307 "channel is destroyed. Returns 1 if the channel had a lock or 0 otherwise.\n",
00308 .syntax = "UNLOCK(<lockname>)",
00309 .read = unlock_read,
00310 };
00311
00312 static int unload_module(void)
00313 {
00314 struct lock_frame *current;
00315
00316
00317 unloading = 1;
00318
00319 AST_LIST_LOCK(&locklist);
00320 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
00321
00322 if (current->channel) {
00323
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
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 }
00341
00342 static int load_module(void)
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 }
00349
00350 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");