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