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