Start untangling header inclusion in a way that does not affect
[asterisk/asterisk.git] / main / sched.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.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 Scheduler Routines (from cheops-NG)
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #ifdef DEBUG_SCHEDULER
31 #define DEBUG(a) do { \
32         if (option_debug) \
33                 DEBUG_M(a) \
34         } while (0)
35 #else
36 #define DEBUG(a) 
37 #endif
38
39 #include <sys/time.h>
40
41 #include "asterisk/sched.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/linkedlists.h"
47 #include "asterisk/options.h"
48
49 struct sched {
50         AST_LIST_ENTRY(sched) list;
51         int id;                       /*!< ID number of event */
52         struct timeval when;          /*!< Absolute time event should take place */
53         int resched;                  /*!< When to reschedule */
54         int variable;                 /*!< Use return value from callback to reschedule */
55         const void *data;             /*!< Data */
56         ast_sched_cb callback;        /*!< Callback */
57 };
58
59 struct sched_context {
60         ast_mutex_t lock;
61         unsigned int eventcnt;                  /*!< Number of events processed */
62         unsigned int schedcnt;                  /*!< Number of outstanding schedule events */
63         AST_LIST_HEAD_NOLOCK(, sched) schedq;   /*!< Schedule entry and main queue */
64
65 #ifdef SCHED_MAX_CACHE
66         AST_LIST_HEAD_NOLOCK(, sched) schedc;   /*!< Cache of unused schedule structures and how many */
67         unsigned int schedccnt;
68 #endif
69 };
70
71 struct sched_context *sched_context_create(void)
72 {
73         struct sched_context *tmp;
74
75         if (!(tmp = ast_calloc(1, sizeof(*tmp))))
76                 return NULL;
77
78         ast_mutex_init(&tmp->lock);
79         tmp->eventcnt = 1;
80         
81         return tmp;
82 }
83
84 void sched_context_destroy(struct sched_context *con)
85 {
86         struct sched *s;
87
88         ast_mutex_lock(&con->lock);
89
90 #ifdef SCHED_MAX_CACHE
91         /* Eliminate the cache */
92         while ((s = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
93                 ast_free(s);
94 #endif
95
96         /* And the queue */
97         while ((s = AST_LIST_REMOVE_HEAD(&con->schedq, list)))
98                 ast_free(s);
99         
100         /* And the context */
101         ast_mutex_unlock(&con->lock);
102         ast_mutex_destroy(&con->lock);
103         ast_free(con);
104 }
105
106 static struct sched *sched_alloc(struct sched_context *con)
107 {
108         struct sched *tmp;
109
110         /*
111          * We keep a small cache of schedule entries
112          * to minimize the number of necessary malloc()'s
113          */
114 #ifdef SCHED_MAX_CACHE
115         if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
116                 con->schedccnt--;
117         else
118 #endif
119                 tmp = ast_calloc(1, sizeof(*tmp));
120
121         return tmp;
122 }
123
124 static void sched_release(struct sched_context *con, struct sched *tmp)
125 {
126         /*
127          * Add to the cache, or just free() if we
128          * already have too many cache entries
129          */
130
131 #ifdef SCHED_MAX_CACHE   
132         if (con->schedccnt < SCHED_MAX_CACHE) {
133                 AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
134                 con->schedccnt++;
135         } else
136 #endif
137                 ast_free(tmp);
138 }
139
140 /*! \brief
141  * Return the number of milliseconds 
142  * until the next scheduled event
143  */
144 int ast_sched_wait(struct sched_context *con)
145 {
146         int ms;
147
148         DEBUG(ast_debug(1, "ast_sched_wait()\n"));
149
150         ast_mutex_lock(&con->lock);
151         if (AST_LIST_EMPTY(&con->schedq)) {
152                 ms = -1;
153         } else {
154                 ms = ast_tvdiff_ms(AST_LIST_FIRST(&con->schedq)->when, ast_tvnow());
155                 if (ms < 0)
156                         ms = 0;
157         }
158         ast_mutex_unlock(&con->lock);
159
160         return ms;
161 }
162
163
164 /*! \brief
165  * Take a sched structure and put it in the
166  * queue, such that the soonest event is
167  * first in the list. 
168  */
169 static void schedule(struct sched_context *con, struct sched *s)
170 {
171          
172         struct sched *cur = NULL;
173         
174         AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, cur, list) {
175                 if (ast_tvcmp(s->when, cur->when) == -1) {
176                         AST_LIST_INSERT_BEFORE_CURRENT(s, list);
177                         break;
178                 }
179         }
180         AST_LIST_TRAVERSE_SAFE_END;
181         if (!cur)
182                 AST_LIST_INSERT_TAIL(&con->schedq, s, list);
183         
184         con->schedcnt++;
185 }
186
187 /*! \brief
188  * given the last event *tv and the offset in milliseconds 'when',
189  * computes the next value,
190  */
191 static int sched_settime(struct timeval *tv, int when)
192 {
193         struct timeval now = ast_tvnow();
194
195         /*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
196         if (ast_tvzero(*tv))    /* not supplied, default to now */
197                 *tv = now;
198         *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
199         if (ast_tvcmp(*tv, now) < 0) {
200                 ast_debug(1, "Request to schedule in the past?!?!\n");
201                 *tv = now;
202         }
203         return 0;
204 }
205
206 int ast_sched_replace_variable(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
207 {
208         /* 0 means the schedule item is new; do not delete */
209         if (old_id > 0)
210                 ast_sched_del(con, old_id);
211         return ast_sched_add_variable(con, when, callback, data, variable);
212 }
213
214 /*! \brief
215  * Schedule callback(data) to happen when ms into the future
216  */
217 int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
218 {
219         struct sched *tmp;
220         int res = -1;
221         DEBUG(ast_debug(1, "ast_sched_add()\n"));
222         if (!when) {
223                 ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
224                 return -1;
225         }
226         ast_mutex_lock(&con->lock);
227         if ((tmp = sched_alloc(con))) {
228                 tmp->id = con->eventcnt++;
229                 tmp->callback = callback;
230                 tmp->data = data;
231                 tmp->resched = when;
232                 tmp->variable = variable;
233                 tmp->when = ast_tv(0, 0);
234                 if (sched_settime(&tmp->when, when)) {
235                         sched_release(con, tmp);
236                 } else {
237                         schedule(con, tmp);
238                         res = tmp->id;
239                 }
240         }
241 #ifdef DUMP_SCHEDULER
242         /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
243         if (option_debug)
244                 ast_sched_dump(con);
245 #endif
246         ast_mutex_unlock(&con->lock);
247         return res;
248 }
249
250 int ast_sched_replace(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data)
251 {
252         if (old_id > -1)
253                 ast_sched_del(con, old_id);
254         return ast_sched_add(con, when, callback, data);
255 }
256
257 int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data)
258 {
259         return ast_sched_add_variable(con, when, callback, data, 0);
260 }
261
262 /*! \brief
263  * Delete the schedule entry with number
264  * "id".  It's nearly impossible that there
265  * would be two or more in the list with that
266  * id.
267  */
268 int ast_sched_del(struct sched_context *con, int id)
269 {
270         struct sched *s;
271
272         DEBUG(ast_debug(1, "ast_sched_del()\n"));
273         
274         ast_mutex_lock(&con->lock);
275         AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, s, list) {
276                 if (s->id == id) {
277                         AST_LIST_REMOVE_CURRENT(list);
278                         con->schedcnt--;
279                         sched_release(con, s);
280                         break;
281                 }
282         }
283         AST_LIST_TRAVERSE_SAFE_END;
284
285 #ifdef DUMP_SCHEDULER
286         /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
287         if (option_debug)
288                 ast_sched_dump(con);
289 #endif
290         ast_mutex_unlock(&con->lock);
291
292         if (!s) {
293                 ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
294 #ifdef DO_CRASH
295                 CRASH;
296 #endif
297                 return -1;
298         }
299         
300         return 0;
301 }
302
303 /*! \brief Dump the contents of the scheduler to LOG_DEBUG */
304 void ast_sched_dump(const struct sched_context *con)
305 {
306         struct sched *q;
307         struct timeval tv = ast_tvnow();
308 #ifdef SCHED_MAX_CACHE
309         ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
310 #else
311         ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
312 #endif
313
314         ast_debug(1, "=============================================================\n");
315         ast_debug(1, "|ID    Callback          Data              Time  (sec:ms)   |\n");
316         ast_debug(1, "+-----+-----------------+-----------------+-----------------+\n");
317         AST_LIST_TRAVERSE(&con->schedq, q, list) {
318                 struct timeval delta = ast_tvsub(q->when, tv);
319
320                 ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n", 
321                         q->id,
322                         q->callback,
323                         q->data,
324                         delta.tv_sec,
325                         (long int)delta.tv_usec);
326         }
327         ast_debug(1, "=============================================================\n");
328 }
329
330 /*! \brief
331  * Launch all events which need to be run at this time.
332  */
333 int ast_sched_runq(struct sched_context *con)
334 {
335         struct sched *current;
336         struct timeval tv;
337         int numevents;
338         int res;
339
340         DEBUG(ast_debug(1, "ast_sched_runq()\n"));
341                 
342         ast_mutex_lock(&con->lock);
343
344         for (numevents = 0; !AST_LIST_EMPTY(&con->schedq); numevents++) {
345                 /* schedule all events which are going to expire within 1ms.
346                  * We only care about millisecond accuracy anyway, so this will
347                  * help us get more than one event at one time if they are very
348                  * close together.
349                  */
350                 tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
351                 if (ast_tvcmp(AST_LIST_FIRST(&con->schedq)->when, tv) != -1)
352                         break;
353                 
354                 current = AST_LIST_REMOVE_HEAD(&con->schedq, list);
355                 con->schedcnt--;
356
357                 /*
358                  * At this point, the schedule queue is still intact.  We
359                  * have removed the first event and the rest is still there,
360                  * so it's permissible for the callback to add new events, but
361                  * trying to delete itself won't work because it isn't in
362                  * the schedule queue.  If that's what it wants to do, it 
363                  * should return 0.
364                  */
365                         
366                 ast_mutex_unlock(&con->lock);
367                 res = current->callback(current->data);
368                 ast_mutex_lock(&con->lock);
369                         
370                 if (res) {
371                         /*
372                          * If they return non-zero, we should schedule them to be
373                          * run again.
374                          */
375                         if (sched_settime(&current->when, current->variable? res : current->resched)) {
376                                 sched_release(con, current);
377                         } else
378                                 schedule(con, current);
379                 } else {
380                         /* No longer needed, so release it */
381                         sched_release(con, current);
382                 }
383         }
384
385         ast_mutex_unlock(&con->lock);
386         
387         return numevents;
388 }
389
390 long ast_sched_when(struct sched_context *con,int id)
391 {
392         struct sched *s;
393         long secs = -1;
394         DEBUG(ast_debug(1, "ast_sched_when()\n"));
395
396         ast_mutex_lock(&con->lock);
397         AST_LIST_TRAVERSE(&con->schedq, s, list) {
398                 if (s->id == id)
399                         break;
400         }
401         if (s) {
402                 struct timeval now = ast_tvnow();
403                 secs = s->when.tv_sec - now.tv_sec;
404         }
405         ast_mutex_unlock(&con->lock);
406         
407         return secs;
408 }