2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2007, Tilghman Lesher
6 * Tilghman Lesher <func_lock_2007@the-tilghman.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Dialplan mutexes
23 * \author Tilghman Lesher <func_lock_2007@the-tilghman.com>
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37 #include "asterisk/lock.h"
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/options.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/options.h"
45 #include "asterisk/linkedlists.h"
47 AST_LIST_HEAD_STATIC(locklist, lock_frame);
49 static void lock_free(void *data);
50 static int unloading = 0;
52 static struct ast_datastore_info lock_info = {
58 AST_LIST_ENTRY(lock_frame) entries;
60 struct ast_channel *channel;
64 static void lock_free(void *data)
66 struct lock_frame *frame = data;
69 frame->channel = NULL;
70 ast_mutex_unlock(&frame->mutex);
73 static int get_lock(struct ast_channel *chan, char *lockname, int try)
75 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
76 struct lock_frame *current;
80 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
81 lock_store = ast_channel_datastore_alloc(&lock_info, NULL);
83 ast_log(LOG_ERROR, "Unable to allocate new datastore. No locks will be obtained.\n");
86 ast_channel_datastore_add(chan, lock_store);
89 /* If the channel already has a lock, then free the existing lock */
90 if (lock_store->data) {
91 struct lock_frame *old = lock_store->data;
93 ast_mutex_unlock(&old->mutex);
96 /* Lock already exist? */
97 AST_LIST_LOCK(&locklist);
98 AST_LIST_TRAVERSE(&locklist, current, entries) {
99 if (strcmp(current->name, lockname) == 0) {
107 AST_LIST_UNLOCK(&locklist);
111 /* Create new lock entry */
112 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
114 AST_LIST_UNLOCK(&locklist);
118 strcpy((char *)current + sizeof(*current), lockname);
119 ast_mutex_init(¤t->mutex);
120 AST_LIST_INSERT_TAIL(&locklist, current, entries);
123 res = try ? ast_mutex_trylock(¤t->mutex) : ast_mutex_lock(¤t->mutex);
125 lock_store->data = current;
126 current->channel = chan;
129 AST_LIST_UNLOCK(&locklist);
133 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
135 struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
136 struct lock_frame *current;
139 ast_log(LOG_WARNING, "No datastore for dialplan locks. Nothing was ever locked!\n");
140 ast_copy_string(buf, "0", len);
144 current = lock_store->data;
147 ast_copy_string(buf, "0", len);
151 current->channel = NULL;
152 ast_mutex_unlock(¤t->mutex);
153 ast_copy_string(buf, "1", len);
157 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
159 ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
163 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
165 ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
169 static struct ast_custom_function lock_function = {
171 .synopsis = "Attempt to obtain a named mutex",
173 "Attempts to grab a named lock exclusively, and prevents other channels\n"
174 "from obtaining the same lock. LOCK will wait for the lock to become\n"
175 "available. Returns 1 if the lock was obtained or 0 on error.\n\n"
176 "Note: to avoid the possibility of a deadlock, LOCK will only attempt to\n"
177 "grab a single lock. If you have a lock already and you attempt to lock\n"
178 "another name, LOCK will unlock the first name before attempting to lock\n"
179 "the second name.\n",
180 .syntax = "LOCK(<lockname>)",
184 static struct ast_custom_function trylock_function = {
186 .synopsis = "Attempt to obtain a named mutex",
188 "Attempts to grab a named lock exclusively, and prevents other channels\n"
189 "from obtaining the same lock. Returns 1 if the lock was available or 0\n"
191 "Note: to avoid the possibility of a deadlock, TRYLOCK will only attempt to\n"
192 "grab a single lock. If you have a lock already and you attempt to lock\n"
193 "another name, TRYLOCK will unlock the first name before attempting to lock\n"
194 "the second name.\n",
195 .syntax = "TRYLOCK(<lockname>)",
196 .read = trylock_read,
199 static struct ast_custom_function unlock_function = {
201 .synopsis = "Unlocks a named mutex",
203 "Unlocks a previously locked mutex. Returns 1 if the channel had a lock\n"
205 .syntax = "UNLOCK()",
209 static int unload_module(void)
211 struct lock_frame *current;
216 AST_LIST_LOCK(&locklist);
217 while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
218 /* If any locks are currently in use, then we cannot unload this module */
219 if (current->channel) {
221 AST_LIST_INSERT_HEAD(&locklist, current, entries);
222 AST_LIST_UNLOCK(&locklist);
226 ast_mutex_destroy(¤t->mutex);
230 /* No locks left, unregister functions */
231 ast_custom_function_unregister(&lock_function);
232 ast_custom_function_unregister(&trylock_function);
233 ast_custom_function_unregister(&unlock_function);
235 AST_LIST_UNLOCK(&locklist);
239 static int load_module(void)
241 int res = ast_custom_function_register(&lock_function);
242 res |= ast_custom_function_register(&trylock_function);
243 res |= ast_custom_function_register(&unlock_function);
247 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");