Fix race condition in the scheduler when deleting a running entry.
[asterisk/asterisk.git] / main / sched.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  * Russell Bryant <russell@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Scheduler Routines (from cheops-NG)
23  *
24  * \author Mark Spencer <markster@digium.com>
25  */
26
27 /*** MODULEINFO
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #ifdef DEBUG_SCHEDULER
36 #define DEBUG(a) do { \
37         if (option_debug) \
38                 DEBUG_M(a) \
39         } while (0)
40 #else
41 #define DEBUG(a)
42 #endif
43
44 #include <sys/time.h>
45
46 #include "asterisk/sched.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/lock.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/heap.h"
51 #include "asterisk/threadstorage.h"
52
53 /*!
54  * \brief Max num of schedule structs
55  *
56  * \note The max number of schedule structs to keep around
57  * for use.  Undefine to disable schedule structure
58  * caching. (Only disable this on very low memory
59  * machines)
60  */
61 #define SCHED_MAX_CACHE 128
62
63 AST_THREADSTORAGE(last_del_id);
64
65 struct sched {
66         AST_LIST_ENTRY(sched) list;
67         int id;                       /*!< ID number of event */
68         struct timeval when;          /*!< Absolute time event should take place */
69         int resched;                  /*!< When to reschedule */
70         int variable;                 /*!< Use return value from callback to reschedule */
71         const void *data;             /*!< Data */
72         ast_sched_cb callback;        /*!< Callback */
73         ssize_t __heap_index;
74         /*!
75          * Used to synchronize between thread running a task and thread
76          * attempting to delete a task
77          */
78         ast_cond_t cond;
79         /*! Indication that a running task was deleted. */
80         unsigned int deleted:1;
81 };
82
83 struct sched_thread {
84         pthread_t thread;
85         ast_cond_t cond;
86         unsigned int stop:1;
87 };
88
89 struct ast_sched_context {
90         ast_mutex_t lock;
91         unsigned int eventcnt;                  /*!< Number of events processed */
92         unsigned int highwater;                                 /*!< highest count so far */
93         struct ast_heap *sched_heap;
94         struct sched_thread *sched_thread;
95         /*! The scheduled task that is currently executing */
96         struct sched *currently_executing;
97
98 #ifdef SCHED_MAX_CACHE
99         AST_LIST_HEAD_NOLOCK(, sched) schedc;   /*!< Cache of unused schedule structures and how many */
100         unsigned int schedccnt;
101 #endif
102 };
103
104 static void *sched_run(void *data)
105 {
106         struct ast_sched_context *con = data;
107
108         while (!con->sched_thread->stop) {
109                 int ms;
110                 struct timespec ts = {
111                         .tv_sec = 0,
112                 };
113
114                 ast_mutex_lock(&con->lock);
115
116                 if (con->sched_thread->stop) {
117                         ast_mutex_unlock(&con->lock);
118                         return NULL;
119                 }
120
121                 ms = ast_sched_wait(con);
122
123                 if (ms == -1) {
124                         ast_cond_wait(&con->sched_thread->cond, &con->lock);
125                 } else {
126                         struct timeval tv;
127                         tv = ast_tvadd(ast_tvnow(), ast_samp2tv(ms, 1000));
128                         ts.tv_sec = tv.tv_sec;
129                         ts.tv_nsec = tv.tv_usec * 1000;
130                         ast_cond_timedwait(&con->sched_thread->cond, &con->lock, &ts);
131                 }
132
133                 ast_mutex_unlock(&con->lock);
134
135                 if (con->sched_thread->stop) {
136                         return NULL;
137                 }
138
139                 ast_sched_runq(con);
140         }
141
142         return NULL;
143 }
144
145 static void sched_thread_destroy(struct ast_sched_context *con)
146 {
147         if (!con->sched_thread) {
148                 return;
149         }
150
151         if (con->sched_thread->thread != AST_PTHREADT_NULL) {
152                 ast_mutex_lock(&con->lock);
153                 con->sched_thread->stop = 1;
154                 ast_cond_signal(&con->sched_thread->cond);
155                 ast_mutex_unlock(&con->lock);
156                 pthread_join(con->sched_thread->thread, NULL);
157                 con->sched_thread->thread = AST_PTHREADT_NULL;
158         }
159
160         ast_cond_destroy(&con->sched_thread->cond);
161
162         ast_free(con->sched_thread);
163
164         con->sched_thread = NULL;
165 }
166
167 int ast_sched_start_thread(struct ast_sched_context *con)
168 {
169         struct sched_thread *st;
170
171         if (con->sched_thread) {
172                 ast_log(LOG_ERROR, "Thread already started on this scheduler context\n");
173                 return -1;
174         }
175
176         if (!(st = ast_calloc(1, sizeof(*st)))) {
177                 return -1;
178         }
179
180         ast_cond_init(&st->cond, NULL);
181
182         st->thread = AST_PTHREADT_NULL;
183
184         con->sched_thread = st;
185
186         if (ast_pthread_create_background(&st->thread, NULL, sched_run, con)) {
187                 ast_log(LOG_ERROR, "Failed to create scheduler thread\n");
188                 sched_thread_destroy(con);
189                 return -1;
190         }
191
192         return 0;
193 }
194
195 static int sched_time_cmp(void *a, void *b)
196 {
197         return ast_tvcmp(((struct sched *) b)->when, ((struct sched *) a)->when);
198 }
199
200 struct ast_sched_context *ast_sched_context_create(void)
201 {
202         struct ast_sched_context *tmp;
203
204         if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
205                 return NULL;
206         }
207
208         ast_mutex_init(&tmp->lock);
209         tmp->eventcnt = 1;
210
211         if (!(tmp->sched_heap = ast_heap_create(8, sched_time_cmp,
212                         offsetof(struct sched, __heap_index)))) {
213                 ast_sched_context_destroy(tmp);
214                 return NULL;
215         }
216
217         return tmp;
218 }
219
220 static void sched_free(struct sched *task)
221 {
222         ast_cond_destroy(&task->cond);
223         ast_free(task);
224 }
225
226 void ast_sched_context_destroy(struct ast_sched_context *con)
227 {
228         struct sched *s;
229
230         sched_thread_destroy(con);
231         con->sched_thread = NULL;
232
233         ast_mutex_lock(&con->lock);
234
235 #ifdef SCHED_MAX_CACHE
236         while ((s = AST_LIST_REMOVE_HEAD(&con->schedc, list))) {
237                 sched_free(s);
238         }
239 #endif
240
241         if (con->sched_heap) {
242                 while ((s = ast_heap_pop(con->sched_heap))) {
243                         sched_free(s);
244                 }
245                 ast_heap_destroy(con->sched_heap);
246                 con->sched_heap = NULL;
247         }
248
249         ast_mutex_unlock(&con->lock);
250         ast_mutex_destroy(&con->lock);
251
252         ast_free(con);
253 }
254
255 static struct sched *sched_alloc(struct ast_sched_context *con)
256 {
257         struct sched *tmp;
258
259         /*
260          * We keep a small cache of schedule entries
261          * to minimize the number of necessary malloc()'s
262          */
263 #ifdef SCHED_MAX_CACHE
264         if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list))) {
265                 con->schedccnt--;
266         } else 
267 #endif
268         {
269                 tmp = ast_calloc(1, sizeof(*tmp));
270                 ast_cond_init(&tmp->cond, NULL);
271         }
272
273         return tmp;
274 }
275
276 static void sched_release(struct ast_sched_context *con, struct sched *tmp)
277 {
278         /*
279          * Add to the cache, or just free() if we
280          * already have too many cache entries
281          */
282
283 #ifdef SCHED_MAX_CACHE
284         if (con->schedccnt < SCHED_MAX_CACHE) {
285                 AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
286                 con->schedccnt++;
287         } else
288 #endif
289                 sched_free(tmp);
290 }
291
292 /*! \brief
293  * Return the number of milliseconds
294  * until the next scheduled event
295  */
296 int ast_sched_wait(struct ast_sched_context *con)
297 {
298         int ms;
299         struct sched *s;
300
301         DEBUG(ast_debug(1, "ast_sched_wait()\n"));
302
303         ast_mutex_lock(&con->lock);
304         if ((s = ast_heap_peek(con->sched_heap, 1))) {
305                 ms = ast_tvdiff_ms(s->when, ast_tvnow());
306                 if (ms < 0) {
307                         ms = 0;
308                 }
309         } else {
310                 ms = -1;
311         }
312         ast_mutex_unlock(&con->lock);
313
314         return ms;
315 }
316
317
318 /*! \brief
319  * Take a sched structure and put it in the
320  * queue, such that the soonest event is
321  * first in the list.
322  */
323 static void schedule(struct ast_sched_context *con, struct sched *s)
324 {
325         ast_heap_push(con->sched_heap, s);
326
327         if (ast_heap_size(con->sched_heap) > con->highwater) {
328                 con->highwater = ast_heap_size(con->sched_heap);
329         }
330 }
331
332 /*! \brief
333  * given the last event *tv and the offset in milliseconds 'when',
334  * computes the next value,
335  */
336 static int sched_settime(struct timeval *t, int when)
337 {
338         struct timeval now = ast_tvnow();
339
340         /*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
341         if (ast_tvzero(*t))     /* not supplied, default to now */
342                 *t = now;
343         *t = ast_tvadd(*t, ast_samp2tv(when, 1000));
344         if (ast_tvcmp(*t, now) < 0) {
345                 *t = now;
346         }
347         return 0;
348 }
349
350 int ast_sched_replace_variable(int old_id, struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
351 {
352         /* 0 means the schedule item is new; do not delete */
353         if (old_id > 0) {
354                 AST_SCHED_DEL(con, old_id);
355         }
356         return ast_sched_add_variable(con, when, callback, data, variable);
357 }
358
359 /*! \brief
360  * Schedule callback(data) to happen when ms into the future
361  */
362 int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
363 {
364         struct sched *tmp;
365         int res = -1;
366
367         DEBUG(ast_debug(1, "ast_sched_add()\n"));
368
369         ast_mutex_lock(&con->lock);
370         if ((tmp = sched_alloc(con))) {
371                 tmp->id = con->eventcnt++;
372                 tmp->callback = callback;
373                 tmp->data = data;
374                 tmp->resched = when;
375                 tmp->variable = variable;
376                 tmp->when = ast_tv(0, 0);
377                 if (sched_settime(&tmp->when, when)) {
378                         sched_release(con, tmp);
379                 } else {
380                         schedule(con, tmp);
381                         res = tmp->id;
382                 }
383         }
384 #ifdef DUMP_SCHEDULER
385         /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
386         if (option_debug)
387                 ast_sched_dump(con);
388 #endif
389         if (con->sched_thread) {
390                 ast_cond_signal(&con->sched_thread->cond);
391         }
392         ast_mutex_unlock(&con->lock);
393
394         return res;
395 }
396
397 int ast_sched_replace(int old_id, struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data)
398 {
399         if (old_id > -1) {
400                 AST_SCHED_DEL(con, old_id);
401         }
402         return ast_sched_add(con, when, callback, data);
403 }
404
405 int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data)
406 {
407         return ast_sched_add_variable(con, when, callback, data, 0);
408 }
409
410 static struct sched *sched_find(struct ast_sched_context *con, int id)
411 {
412         int x;
413         size_t heap_size;
414
415         heap_size = ast_heap_size(con->sched_heap);
416         for (x = 1; x <= heap_size; x++) {
417                 struct sched *cur = ast_heap_peek(con->sched_heap, x);
418
419                 if (cur->id == id) {
420                         return cur;
421                 }
422         }
423
424         return NULL;
425 }
426
427 const void *ast_sched_find_data(struct ast_sched_context *con, int id)
428 {
429         struct sched *s;
430         const void *data = NULL;
431
432         ast_mutex_lock(&con->lock);
433
434         s = sched_find(con, id);
435         if (s) {
436                 data = s->data;
437         }
438
439         ast_mutex_unlock(&con->lock);
440
441         return data;
442 }
443
444 /*! \brief
445  * Delete the schedule entry with number
446  * "id".  It's nearly impossible that there
447  * would be two or more in the list with that
448  * id.
449  */
450 #ifndef AST_DEVMODE
451 int ast_sched_del(struct ast_sched_context *con, int id)
452 #else
453 int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int line, const char *function)
454 #endif
455 {
456         struct sched *s = NULL;
457         int *last_id = ast_threadstorage_get(&last_del_id, sizeof(int));
458
459         DEBUG(ast_debug(1, "ast_sched_del(%d)\n", id));
460
461         if (id < 0) {
462                 return 0;
463         }
464
465         ast_mutex_lock(&con->lock);
466
467         s = sched_find(con, id);
468         if (s) {
469                 if (!ast_heap_remove(con->sched_heap, s)) {
470                         ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->id);
471                 }
472                 sched_release(con, s);
473         } else if (con->currently_executing && (id == con->currently_executing->id)) {
474                 s = con->currently_executing;
475                 s->deleted = 1;
476                 /* Wait for executing task to complete so that caller of ast_sched_del() does not
477                  * free memory out from under the task.
478                  */
479                 ast_cond_wait(&s->cond, &con->lock);
480                 /* Do not sched_release() here because ast_sched_runq() will do it */
481         }
482
483 #ifdef DUMP_SCHEDULER
484         /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
485         if (option_debug)
486                 ast_sched_dump(con);
487 #endif
488         if (con->sched_thread) {
489                 ast_cond_signal(&con->sched_thread->cond);
490         }
491         ast_mutex_unlock(&con->lock);
492
493         if (!s && *last_id != id) {
494                 ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
495 #ifndef AST_DEVMODE
496                 ast_assert(s != NULL);
497 #else
498                 {
499                 char buf[100];
500                 snprintf(buf, sizeof(buf), "s != NULL, id=%d", id);
501                 _ast_assert(0, buf, file, line, function);
502                 }
503 #endif
504                 *last_id = id;
505                 return -1;
506         } else if (!s) {
507                 return -1;
508         }
509
510         return 0;
511 }
512
513 void ast_sched_report(struct ast_sched_context *con, struct ast_str **buf, struct ast_cb_names *cbnames)
514 {
515         int i, x;
516         struct sched *cur;
517         int countlist[cbnames->numassocs + 1];
518         size_t heap_size;
519
520         memset(countlist, 0, sizeof(countlist));
521         ast_str_set(buf, 0, " Highwater = %u\n schedcnt = %zu\n", con->highwater, ast_heap_size(con->sched_heap));
522
523         ast_mutex_lock(&con->lock);
524
525         heap_size = ast_heap_size(con->sched_heap);
526         for (x = 1; x <= heap_size; x++) {
527                 cur = ast_heap_peek(con->sched_heap, x);
528                 /* match the callback to the cblist */
529                 for (i = 0; i < cbnames->numassocs; i++) {
530                         if (cur->callback == cbnames->cblist[i]) {
531                                 break;
532                         }
533                 }
534                 if (i < cbnames->numassocs) {
535                         countlist[i]++;
536                 } else {
537                         countlist[cbnames->numassocs]++;
538                 }
539         }
540
541         ast_mutex_unlock(&con->lock);
542
543         for (i = 0; i < cbnames->numassocs; i++) {
544                 ast_str_append(buf, 0, "    %s : %d\n", cbnames->list[i], countlist[i]);
545         }
546
547         ast_str_append(buf, 0, "   <unknown> : %d\n", countlist[cbnames->numassocs]);
548 }
549
550 /*! \brief Dump the contents of the scheduler to LOG_DEBUG */
551 void ast_sched_dump(struct ast_sched_context *con)
552 {
553         struct sched *q;
554         struct timeval when = ast_tvnow();
555         int x;
556         size_t heap_size;
557 #ifdef SCHED_MAX_CACHE
558         ast_debug(1, "Asterisk Schedule Dump (%zu in Q, %u Total, %u Cache, %u high-water)\n", ast_heap_size(con->sched_heap), con->eventcnt - 1, con->schedccnt, con->highwater);
559 #else
560         ast_debug(1, "Asterisk Schedule Dump (%zu in Q, %u Total, %u high-water)\n", ast_heap_size(con->sched_heap), con->eventcnt - 1, con->highwater);
561 #endif
562
563         ast_debug(1, "=============================================================\n");
564         ast_debug(1, "|ID    Callback          Data              Time  (sec:ms)   |\n");
565         ast_debug(1, "+-----+-----------------+-----------------+-----------------+\n");
566         ast_mutex_lock(&con->lock);
567         heap_size = ast_heap_size(con->sched_heap);
568         for (x = 1; x <= heap_size; x++) {
569                 struct timeval delta;
570                 q = ast_heap_peek(con->sched_heap, x);
571                 delta = ast_tvsub(q->when, when);
572                 ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
573                         q->id,
574                         q->callback,
575                         q->data,
576                         (long)delta.tv_sec,
577                         (long int)delta.tv_usec);
578         }
579         ast_mutex_unlock(&con->lock);
580         ast_debug(1, "=============================================================\n");
581 }
582
583 /*! \brief
584  * Launch all events which need to be run at this time.
585  */
586 int ast_sched_runq(struct ast_sched_context *con)
587 {
588         struct sched *current;
589         struct timeval when;
590         int numevents;
591         int res;
592
593         DEBUG(ast_debug(1, "ast_sched_runq()\n"));
594
595         ast_mutex_lock(&con->lock);
596
597         when = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
598         for (numevents = 0; (current = ast_heap_peek(con->sched_heap, 1)); numevents++) {
599                 /* schedule all events which are going to expire within 1ms.
600                  * We only care about millisecond accuracy anyway, so this will
601                  * help us get more than one event at one time if they are very
602                  * close together.
603                  */
604                 if (ast_tvcmp(current->when, when) != -1) {
605                         break;
606                 }
607
608                 current = ast_heap_pop(con->sched_heap);
609
610                 /*
611                  * At this point, the schedule queue is still intact.  We
612                  * have removed the first event and the rest is still there,
613                  * so it's permissible for the callback to add new events, but
614                  * trying to delete itself won't work because it isn't in
615                  * the schedule queue.  If that's what it wants to do, it
616                  * should return 0.
617                  */
618
619                 con->currently_executing = current;
620                 ast_mutex_unlock(&con->lock);
621                 res = current->callback(current->data);
622                 ast_mutex_lock(&con->lock);
623                 con->currently_executing = NULL;
624                 ast_cond_signal(&current->cond);
625
626                 if (res && !current->deleted) {
627                         /*
628                          * If they return non-zero, we should schedule them to be
629                          * run again.
630                          */
631                         if (sched_settime(&current->when, current->variable? res : current->resched)) {
632                                 sched_release(con, current);
633                         } else {
634                                 schedule(con, current);
635                         }
636                 } else {
637                         /* No longer needed, so release it */
638                         sched_release(con, current);
639                 }
640         }
641
642         ast_mutex_unlock(&con->lock);
643
644         return numevents;
645 }
646
647 long ast_sched_when(struct ast_sched_context *con,int id)
648 {
649         struct sched *s;
650         long secs = -1;
651         DEBUG(ast_debug(1, "ast_sched_when()\n"));
652
653         ast_mutex_lock(&con->lock);
654
655         s = sched_find(con, id);
656         if (s) {
657                 struct timeval now = ast_tvnow();
658                 secs = s->when.tv_sec - now.tv_sec;
659         }
660
661         ast_mutex_unlock(&con->lock);
662
663         return secs;
664 }