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