ami_testhooks.c automatically registers hook
[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 <signal.h>
34
35 #include "asterisk/lock.h"
36 #include "asterisk/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/linkedlists.h"
41 #include "asterisk/astobj2.h"
42 #include "asterisk/utils.h"
43
44 /*** DOCUMENTATION
45         <function name="LOCK" language="en_US">
46                 <synopsis>
47                         Attempt to obtain a named mutex.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="lockname" required="true" />
51                 </syntax>
52                 <description>
53                         <para>Attempts to grab a named lock exclusively, and prevents other channels from
54                         obtaining the same lock.  LOCK will wait for the lock to become available.
55                         Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
56                         <note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
57                         obtain the lock for 3 seconds if the channel already has another lock.</para></note>
58                 </description>
59         </function>
60         <function name="TRYLOCK" language="en_US">
61                 <synopsis>
62                         Attempt to obtain a named mutex.
63                 </synopsis>
64                 <syntax>
65                         <parameter name="lockname" required="true" />
66                 </syntax>
67                 <description>
68                         <para>Attempts to grab a named lock exclusively, and prevents other channels
69                         from obtaining the same lock.  Returns <literal>1</literal> if the lock was 
70                         available or <literal>0</literal> otherwise.</para>
71                 </description>
72         </function>
73         <function name="UNLOCK" language="en_US">
74                 <synopsis>
75                         Unlocks a named mutex.
76                 </synopsis>
77                 <syntax>
78                         <parameter name="lockname" required="true" />
79                 </syntax>
80                 <description>
81                         <para>Unlocks a previously locked mutex. Returns <literal>1</literal> if the channel 
82                         had a lock or <literal>0</literal> otherwise.</para>
83                         <note><para>It is generally unnecessary to unlock in a hangup routine, as any locks 
84                         held are automatically freed when the channel is destroyed.</para></note>
85                 </description>
86         </function>
87  ***/
88
89
90
91 static AST_LIST_HEAD_STATIC(locklist, lock_frame);
92
93 static void lock_free(void *data);
94 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
95 static int unloading = 0;
96 static pthread_t broker_tid = -1;
97
98 static struct ast_datastore_info lock_info = {
99         .type = "MUTEX",
100         .destroy = lock_free,
101         .chan_fixup = lock_fixup,
102 };
103
104 struct lock_frame {
105         AST_LIST_ENTRY(lock_frame) entries;
106         ast_mutex_t mutex;
107         ast_cond_t cond;
108         /*! count is needed so if a recursive mutex exits early, we know how many times to unlock it. */
109         unsigned int count;
110         /*! Container of requesters for the named lock */
111         struct ao2_container *requesters;
112         /*! who owns us */
113         struct ast_channel *owner;
114         /*! name of the lock */
115         char name[0];
116 };
117
118 struct channel_lock_frame {
119         AST_LIST_ENTRY(channel_lock_frame) list;
120         /*! Need to save channel pointer here, because during destruction, we won't have it. */
121         struct ast_channel *channel;
122         struct lock_frame *lock_frame;
123 };
124
125 static void lock_free(void *data)
126 {
127         AST_LIST_HEAD(, channel_lock_frame) *oldlist = data;
128         struct channel_lock_frame *clframe;
129         AST_LIST_LOCK(oldlist);
130         while ((clframe = AST_LIST_REMOVE_HEAD(oldlist, list))) {
131                 /* Only unlock if we own the lock */
132                 if (clframe->channel == clframe->lock_frame->owner) {
133                         clframe->lock_frame->count = 0;
134                         clframe->lock_frame->owner = NULL;
135                 }
136                 ast_free(clframe);
137         }
138         AST_LIST_UNLOCK(oldlist);
139         AST_LIST_HEAD_DESTROY(oldlist);
140         ast_free(oldlist);
141 }
142
143 static void lock_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
144 {
145         struct ast_datastore *lock_store = ast_channel_datastore_find(oldchan, &lock_info, NULL);
146         AST_LIST_HEAD(, channel_lock_frame) *list;
147         struct channel_lock_frame *clframe = NULL;
148
149         if (!lock_store) {
150                 return;
151         }
152         list = lock_store->data;
153
154         AST_LIST_LOCK(list);
155         AST_LIST_TRAVERSE(list, clframe, list) {
156                 if (clframe->lock_frame->owner == oldchan) {
157                         clframe->lock_frame->owner = newchan;
158                 }
159                 /* We don't move requesters, because the thread stack is different */
160                 clframe->channel = newchan;
161         }
162         AST_LIST_UNLOCK(list);
163 }
164
165 static void *lock_broker(void *unused)
166 {
167         struct lock_frame *frame;
168         struct timespec forever = { 1000000, 0 };
169         for (;;) {
170                 int found_requester = 0;
171
172                 /* Test for cancel outside of the lock */
173                 pthread_testcancel();
174                 AST_LIST_LOCK(&locklist);
175
176                 AST_LIST_TRAVERSE(&locklist, frame, entries) {
177                         if (ao2_container_count(frame->requesters)) {
178                                 found_requester++;
179                                 ast_mutex_lock(&frame->mutex);
180                                 if (!frame->owner) {
181                                         ast_cond_signal(&frame->cond);
182                                 }
183                                 ast_mutex_unlock(&frame->mutex);
184                         }
185                 }
186
187                 AST_LIST_UNLOCK(&locklist);
188                 pthread_testcancel();
189
190                 /* If there are no requesters, then wait for a signal */
191                 if (!found_requester) {
192                         nanosleep(&forever, NULL);
193                 } else {
194                         sched_yield();
195                 }
196         }
197         /* Not reached */
198         return NULL;
199 }
200
201 static int ast_channel_hash_cb(const void *obj, const int flags)
202 {
203         const struct ast_channel *chan = obj;
204         return ast_str_case_hash(chan->name);
205 }
206
207 static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
208 {
209         struct ast_channel *chan = obj, *cmp_args = arg;
210         return strcasecmp(chan->name, cmp_args->name) ? 0 : CMP_MATCH;
211 }
212
213 static int get_lock(struct ast_channel *chan, char *lockname, int try)
214 {
215         struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
216         struct lock_frame *current;
217         struct channel_lock_frame *clframe = NULL;
218         AST_LIST_HEAD(, channel_lock_frame) *list;
219         int res = 0;
220         struct timespec three_seconds = { .tv_sec = 3 };
221
222         if (!lock_store) {
223                 ast_debug(1, "Channel %s has no lock datastore, so we're allocating one.\n", chan->name);
224                 lock_store = ast_datastore_alloc(&lock_info, NULL);
225                 if (!lock_store) {
226                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  No locks will be obtained.\n");
227                         return -1;
228                 }
229
230                 list = ast_calloc(1, sizeof(*list));
231                 if (!list) {
232                         ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %sLOCK will fail.\n", try ? "TRY" : "");
233                         ast_datastore_free(lock_store);
234                         return -1;
235                 }
236
237                 lock_store->data = list;
238                 AST_LIST_HEAD_INIT(list);
239                 ast_channel_datastore_add(chan, lock_store);
240         } else
241                 list = lock_store->data;
242
243         /* Lock already exists? */
244         AST_LIST_LOCK(&locklist);
245         AST_LIST_TRAVERSE(&locklist, current, entries) {
246                 if (strcmp(current->name, lockname) == 0) {
247                         break;
248                 }
249         }
250
251         if (!current) {
252                 if (unloading) {
253                         /* Don't bother */
254                         AST_LIST_UNLOCK(&locklist);
255                         return -1;
256                 }
257
258                 /* Create new lock entry */
259                 current = ast_calloc(1, sizeof(*current) + strlen(lockname) + 1);
260                 if (!current) {
261                         AST_LIST_UNLOCK(&locklist);
262                         return -1;
263                 }
264
265                 strcpy(current->name, lockname); /* SAFE */
266                 if ((res = ast_mutex_init(&current->mutex))) {
267                         ast_log(LOG_ERROR, "Unable to initialize mutex: %s\n", strerror(res));
268                         ast_free(current);
269                         AST_LIST_UNLOCK(&locklist);
270                         return -1;
271                 }
272                 if ((res = ast_cond_init(&current->cond, NULL))) {
273                         ast_log(LOG_ERROR, "Unable to initialize condition variable: %s\n", strerror(res));
274                         ast_mutex_destroy(&current->mutex);
275                         ast_free(current);
276                         AST_LIST_UNLOCK(&locklist);
277                         return -1;
278                 }
279                 if (!(current->requesters = ao2_container_alloc(1, ast_channel_hash_cb, ast_channel_cmp_cb))) {
280                         ast_mutex_destroy(&current->mutex);
281                         ast_cond_destroy(&current->cond);
282                         ast_free(current);
283                         AST_LIST_UNLOCK(&locklist);
284                         return -1;
285                 }
286                 AST_LIST_INSERT_TAIL(&locklist, current, entries);
287         }
288         AST_LIST_UNLOCK(&locklist);
289
290         /* Found lock or created one - now find or create the corresponding link in the channel */
291         AST_LIST_LOCK(list);
292         AST_LIST_TRAVERSE(list, clframe, list) {
293                 if (clframe->lock_frame == current) {
294                         break;
295                 }
296         }
297
298         if (!clframe) {
299                 if (unloading) {
300                         /* Don't bother */
301                         AST_LIST_UNLOCK(list);
302                         return -1;
303                 }
304
305                 if (!(clframe = ast_calloc(1, sizeof(*clframe)))) {
306                         ast_log(LOG_ERROR, "Unable to allocate channel lock frame.  %sLOCK will fail.\n", try ? "TRY" : "");
307                         AST_LIST_UNLOCK(list);
308                         return -1;
309                 }
310
311                 clframe->lock_frame = current;
312                 clframe->channel = chan;
313                 AST_LIST_INSERT_TAIL(list, clframe, list);
314         }
315         AST_LIST_UNLOCK(list);
316
317         /* If we already own the lock, then we're being called recursively.
318          * Keep track of how many times that is, because we need to unlock
319          * the same amount, before we'll release this one.
320          */
321         if (current->owner == chan) {
322                 current->count++;
323                 return 0;
324         }
325
326         /* Okay, we have both frames, so now we need to try to lock.
327          *
328          * Locking order: always lock locklist first.  We need the
329          * locklist lock because the broker thread counts whether
330          * there are requesters with the locklist lock held, and we
331          * need to hold it, so that when we send our signal, below,
332          * to wake up the broker thread, it definitely will see that
333          * a requester exists at that point in time.  Otherwise, we
334          * could add to the requesters after it has already seen that
335          * that lock is unoccupied and wait forever for another signal.
336          */
337         AST_LIST_LOCK(&locklist);
338         ast_mutex_lock(&current->mutex);
339         /* Add to requester list */
340         ao2_link(current->requesters, chan);
341         pthread_kill(broker_tid, SIGURG);
342         AST_LIST_UNLOCK(&locklist);
343
344         if ((!current->owner) ||
345                 (!try && !(res = ast_cond_timedwait(&current->cond, &current->mutex, &three_seconds)))) {
346                 res = 0;
347                 current->owner = chan;
348                 current->count++;
349         } else {
350                 res = -1;
351         }
352         /* Remove from requester list */
353         ao2_unlink(current->requesters, chan);
354         ast_mutex_unlock(&current->mutex);
355
356         return res;
357 }
358
359 static int unlock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
360 {
361         struct ast_datastore *lock_store = ast_channel_datastore_find(chan, &lock_info, NULL);
362         struct channel_lock_frame *clframe;
363         AST_LIST_HEAD(, channel_lock_frame) *list;
364
365         if (!lock_store) {
366                 ast_log(LOG_WARNING, "No datastore for dialplan locks.  Nothing was ever locked!\n");
367                 ast_copy_string(buf, "0", len);
368                 return 0;
369         }
370
371         if (!(list = lock_store->data)) {
372                 ast_debug(1, "This should NEVER happen\n");
373                 ast_copy_string(buf, "0", len);
374                 return 0;
375         }
376
377         /* Find item in the channel list */
378         AST_LIST_LOCK(list);
379         AST_LIST_TRAVERSE(list, clframe, list) {
380                 if (clframe->lock_frame && clframe->lock_frame->owner == chan && strcmp(clframe->lock_frame->name, data) == 0) {
381                         break;
382                 }
383         }
384         /* We never destroy anything until channel destruction, which will never
385          * happen while this routine is executing, so we don't need to hold the
386          * lock beyond this point. */
387         AST_LIST_UNLOCK(list);
388
389         if (!clframe) {
390                 /* We didn't have this lock in the first place */
391                 ast_copy_string(buf, "0", len);
392                 return 0;
393         }
394
395         if (--clframe->lock_frame->count == 0) {
396                 clframe->lock_frame->owner = NULL;
397         }
398
399         ast_copy_string(buf, "1", len);
400         return 0;
401 }
402
403 static int lock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
404 {
405         if (chan)
406                 ast_autoservice_start(chan);
407
408         ast_copy_string(buf, get_lock(chan, data, 0) ? "0" : "1", len);
409
410         if (chan)
411                 ast_autoservice_stop(chan);
412
413         return 0;
414 }
415
416 static int trylock_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
417 {
418         if (chan)
419                 ast_autoservice_start(chan);
420
421         ast_copy_string(buf, get_lock(chan, data, 1) ? "0" : "1", len);
422
423         if (chan)
424                 ast_autoservice_stop(chan);
425
426         return 0;
427 }
428
429 static struct ast_custom_function lock_function = {
430         .name = "LOCK",
431         .read = lock_read,
432         .read_max = 2,
433 };
434
435 static struct ast_custom_function trylock_function = {
436         .name = "TRYLOCK",
437         .read = trylock_read,
438         .read_max = 2,
439 };
440
441 static struct ast_custom_function unlock_function = {
442         .name = "UNLOCK",
443         .read = unlock_read,
444         .read_max = 2,
445 };
446
447 static int unload_module(void)
448 {
449         struct lock_frame *current;
450
451         /* Module flag */
452         unloading = 1;
453
454         AST_LIST_LOCK(&locklist);
455         while ((current = AST_LIST_REMOVE_HEAD(&locklist, entries))) {
456                 /* If any locks are currently in use, then we cannot unload this module */
457                 if (current->owner || ao2_container_count(current->requesters)) {
458                         /* Put it back */
459                         AST_LIST_INSERT_HEAD(&locklist, current, entries);
460                         AST_LIST_UNLOCK(&locklist);
461                         unloading = 0;
462                         return -1;
463                 }
464                 ast_mutex_destroy(&current->mutex);
465                 ao2_ref(current->requesters, -1);
466                 ast_free(current);
467         }
468
469         /* No locks left, unregister functions */
470         ast_custom_function_unregister(&lock_function);
471         ast_custom_function_unregister(&trylock_function);
472         ast_custom_function_unregister(&unlock_function);
473
474         pthread_cancel(broker_tid);
475         pthread_kill(broker_tid, SIGURG);
476         pthread_join(broker_tid, NULL);
477
478         AST_LIST_UNLOCK(&locklist);
479
480         return 0;
481 }
482
483 static int load_module(void)
484 {
485         int res = ast_custom_function_register(&lock_function);
486         res |= ast_custom_function_register(&trylock_function);
487         res |= ast_custom_function_register(&unlock_function);
488         ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
489         return res;
490 }
491
492 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan mutexes");