Start untangling header inclusion in a way that does not affect
[asterisk/asterisk.git] / funcs / func_lock.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Tilghman Lesher
5  *
6  * Tilghman Lesher <func_lock_2007@the-tilghman.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Dialplan mutexes
22  *
23  * \author Tilghman Lesher <func_lock_2007@the-tilghman.com>
24  *
25  * \ingroup functions
26  * 
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/lock.h"
34 #include "asterisk/file.h"
35 #include "asterisk/logger.h"
36 #include "asterisk/options.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/options.h"
41 #include "asterisk/linkedlists.h"
42
43 AST_LIST_HEAD_STATIC(locklist, lock_frame);
44
45 static void lock_free(void *data);
46 static int unloading = 0;
47
48 static struct ast_datastore_info lock_info = {
49         .type = "MUTEX",
50         .destroy = lock_free,
51 };
52
53 struct lock_frame {
54         AST_LIST_ENTRY(lock_frame) entries;
55         ast_mutex_t mutex;
56         /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
57         unsigned int count;
58         /*! who owns us */
59         struct ast_channel *channel;
60         /*! name of the lock */
61         char name[0];
62 };
63
64 struct channel_lock_frame {
65         AST_LIST_ENTRY(channel_lock_frame) list;
66         /*! Need to save channel pointer here, because during destruction, we won't have it. */
67         struct ast_channel *channel;
68         struct lock_frame *lock_frame;
69 };
70
71 static void lock_free(void *data)
72 {
73         AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
74         struct channel_lock_frame *clframe;
75         AST_LIST_LOCK(oldlist);
76         while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
77                 /* Only unlock if we own the lock */
78                 if (clframe->channel == clframe->lock_frame->channel) {
79                         clframe->lock_frame->channel = NULL;
80                         while (clframe->lock_frame->count > 0) {
81                                 clframe->lock_frame->count--;
82                                 ast_mutex_unlock(&clframe->lock_frame->mutex);
83                         }
84                 }
85                 ast_free(clframe);
86         }
87         AST_LIST_UNLOCK(oldlist);
88         AST_LIST_HEAD_DESTROY(oldlist);
89         ast_free(oldlist);
90 }
91
92 static int get_lock(struct ast_channel *chan, char *lockname, int try)
93 {
94         struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
95         struct lock_frame *current;
96         struct channel_lock_frame *clframe = NULL, *save_clframe = NULL;
97         AST_LIST_HEAD(, channel_lock_frame) *list;
98         int res, count_channel_locks = 0;
99
100         if (!lock_store) {
101                 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
102                 lock_store = ast_channel_datastore_alloc(&lock_info, NULL);
103                 if (!lock_store) {
104                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
105                         return -1;
106                 }
107
108                 list = ast_calloc(1, sizeof(*list));
109                 if (!list) {
110                         ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
111                         ast_channel_datastore_free(lock_store);
112                         return -1;
113                 }
114
115                 lock_store->data = list;
116                 AST_LIST_HEAD_INIT(list);
117                 ast_channel_datastore_add(chan, lock_store);
118         } else
119                 list = lock_store->data;
120
121         /* Lock already exists? */
122         AST_LIST_LOCK(&locklist);
123         AST_LIST_TRAVERSE(&locklist, current, entries) {
124                 if (strcmp(current->name, lockname) == 0) {
125                         break;
126                 }
127         }
128
129         if (!current) {
130                 if (unloading) {
131                         /* Don't bother */
132                         AST_LIST_UNLOCK(&locklist);
133                         return -1;
134                 }
135
136                 /* Create new lock entry */
137                 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
138                 if (!current) {
139                         AST_LIST_UNLOCK(&locklist);
140                         return -1;
141                 }
142
143                 strcpy((char *)current + sizeof(*current), lockname);
144                 ast_mutex_init(&current->mutex);
145                 AST_LIST_INSERT_TAIL(&locklist, current, entries);
146         }
147         AST_LIST_UNLOCK(&locklist);
148
149         /* Found lock or created one - now find or create the corresponding link in the channel */
150         AST_LIST_LOCK(list);
151         AST_LIST_TRAVERSE(list, clframe, list) {
152                 if (clframe->lock_frame == current)
153                         save_clframe = clframe;
154
155                 /* Only count mutexes that we currently hold */
156                 if (clframe->lock_frame->channel == chan)
157                         count_channel_locks++;
158         }
159
160         if (save_clframe) {
161                 clframe = save_clframe;
162         } else {
163                 if (unloading) {
164                         /* Don't bother */
165                         AST_LIST_UNLOCK(list);
166                         return -1;
167                 }
168
169                 clframe = ast_calloc(1, sizeof(*clframe));
170                 if (!clframe) {
171                         ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
172                         AST_LIST_UNLOCK(list);
173                         return -1;
174                 }
175
176                 clframe->lock_frame = current;
177                 clframe->channel = chan;
178                 /* Count the lock just created */
179                 count_channel_locks++;
180                 AST_LIST_INSERT_TAIL(list, clframe, list);
181         }
182         AST_LIST_UNLOCK(list);
183
184         /* Okay, we have both frames, so now we need to try to lock the mutex. */
185         if (count_channel_locks > 1) {
186                 struct timeval start = ast_tvnow();
187                 for (;;) {
188                         if ((res = ast_mutex_trylock(&current->mutex)) == 0)
189                                 break;
190                         if (ast_tvdiff_ms(ast_tvnow(), start) > 3000)
191                                 break; /* bail after 3 seconds of waiting */
192                         usleep(1);
193                 }
194         } else {
195                 /* If the channel doesn't have any locks so far, then there's no possible deadlock. */
196                 res = try ? ast_mutex_trylock(&current->mutex) : ast_mutex_lock(&current->mutex);
197         }
198
199         if (res == 0) {
200                 current->count++;
201                 current->channel = chan;
202         }
203
204         return res;
205 }
206
207 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
208 {
209         struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
210         struct channel_lock_frame *clframe;
211         AST_LIST_HEAD(, channel_lock_frame) *list;
212
213         if (!lock_store) {
214                 ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
215                 ast_copy_string(buf, "0", len);
216                 return 0;
217         }
218
219         if (!(list = lock_store->data)) {
220                 ast_debug(1, "This should NEVER happen\n");
221                 ast_copy_string(buf, "0", len);
222                 return 0;
223         }
224
225         /* Find item in the channel list */
226         AST_LIST_LOCK(list);
227         AST_LIST_TRAVERSE(list, clframe, list) {
228                 if (clframe->lock_frame && clframe->lock_frame->channel == chan && strcmp(clframe->lock_frame->name, data) == 0) {
229                         break;
230                 }
231         }
232         /* We never destroy anything until channel destruction, which will never
233          * happen while this routine is executing, so we don't need to hold the
234          * lock beyond this point. */
235         AST_LIST_UNLOCK(list);
236
237         if (!clframe) {
238                 /* We didn't have this lock in the first place */
239                 ast_copy_string(buf, "0", len);
240                 return 0;
241         }
242
243         /* Decrement before we release, because if a channel is waiting on the
244          * mutex, there's otherwise a race to alter count. */
245         clframe->lock_frame->count--;
246         /* If we get another lock, this one shouldn't count against us for deadlock avoidance. */
247         clframe->lock_frame->channel = NULL;
248         ast_mutex_unlock(&clframe->lock_frame->mutex);
249
250         ast_copy_string(buf, "1", len);
251         return 0;
252 }
253
254 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
255 {       
256         if (chan)
257                 ast_autoservice_start(chan);
258
259         ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
260
261         if (chan)
262                 ast_autoservice_stop(chan);
263
264         return 0;
265 }
266
267 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
268 {
269         if (chan)
270                 ast_autoservice_start(chan);
271
272         ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
273
274         if (chan)
275                 ast_autoservice_stop(chan);
276
277         return 0;
278 }
279
280 static struct ast_custom_function lock_function = {
281         .name = "LOCK",
282         .synopsis = "Attempt to obtain a named mutex",
283         .desc =
284 "Attempts to grab a named lock exclusively, and prevents other channels from\n"
285 "obtaining the same lock.  LOCK will wait for the lock to become available.\n"
286 "Returns 1 if the lock was obtained or 0 on error.\n\n"
287 "Note: to avoid the possibility of a deadlock, LOCK will only attempt to\n"
288 "obtain the lock for 3 seconds if the channel already has another lock.\n",
289         .syntax = "LOCK(<lockname>)",
290         .read = lock_read,
291 };
292
293 static struct ast_custom_function trylock_function = {
294         .name = "TRYLOCK",
295         .synopsis = "Attempt to obtain a named mutex",
296         .desc =
297 "Attempts to grab a named lock exclusively, and prevents other channels\n"
298 "from obtaining the same lock.  Returns 1 if the lock was available or 0\n"
299 "otherwise.\n",
300         .syntax = "TRYLOCK(<lockname>)",
301         .read = trylock_read,
302 };
303
304 static struct ast_custom_function unlock_function = {
305         .name = "UNLOCK",
306         .synopsis = "Unlocks a named mutex",
307         .desc =
308 "Unlocks a previously locked mutex.  Note that it is generally unnecessary to\n"
309 "unlock in a hangup routine, as any locks held are automatically freed when the\n"
310 "channel is destroyed.  Returns 1 if the channel had a lock or 0 otherwise.\n",
311         .syntax = "UNLOCK(<lockname>)",
312         .read = unlock_read,
313 };
314
315 static int unload_module(void)
316 {
317         struct lock_frame *current;
318
319         /* Module flag */
320         unloading = 1;
321
322         AST_LIST_LOCK(&locklist);
323         while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
324                 /* If any locks are currently in use, then we cannot unload this module */
325                 if (current->channel) {
326                         /* Put it back */
327                         AST_LIST_INSERT_HEAD(&locklist, current, entries);
328                         AST_LIST_UNLOCK(&locklist);
329                         unloading = 0;
330                         return -1;
331                 }
332                 ast_mutex_destroy(&current->mutex);
333                 ast_free(current);
334         }
335
336         /* No locks left, unregister functions */
337         ast_custom_function_unregister(&lock_function);
338         ast_custom_function_unregister(&trylock_function);
339         ast_custom_function_unregister(&unlock_function);
340
341         AST_LIST_UNLOCK(&locklist);
342         return 0;
343 }
344
345 static int load_module(void)
346 {
347         int res = ast_custom_function_register(&lock_function);
348         res |= ast_custom_function_register(&trylock_function);
349         res |= ast_custom_function_register(&unlock_function);
350         return res;
351 }
352
353 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");