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