4 * Mark Spencer <markster@marko.net>
6 * Copyright(C) Mark Spencer
8 * Distributed under the terms of the GNU General Public License (GPL) Version 2
10 * Scheduler Routines (form cheops-NG)
14 #ifdef DEBUG_SCHEDULER
15 #define DEBUG(a) DEBUG_M(a)
26 #include <asterisk/sched.h>
27 #include <asterisk/logger.h>
28 #include <asterisk/channel.h>
30 /* Determine if a is sooner than b */
31 #define SOONER(a,b) (((b).tv_sec > (a).tv_sec) || \
32 (((b).tv_sec == (a).tv_sec) && ((b).tv_usec > (a).tv_usec)))
35 struct sched *next; /* Next event in the list */
36 int id; /* ID number of event */
37 struct timeval when; /* Absolute time event should take place */
38 int resched; /* When to reschedule */
39 void *data; /* Data */
40 ast_sched_cb callback; /* Callback */
43 struct sched_context {
45 /* Number of events processed */
48 /* Number of outstanding schedule events */
51 /* Schedule entry and main queue */
54 #ifdef SCHED_MAX_CACHE
55 /* Cache of unused schedule structures and how many */
61 struct sched_context *sched_context_create(void)
63 struct sched_context *tmp;
64 tmp = malloc(sizeof(struct sched_context));
66 ast_mutex_init(&tmp->lock);
70 #ifdef SCHED_MAX_CACHE
78 void sched_context_destroy(struct sched_context *con)
81 ast_mutex_lock(&con->lock);
82 #ifdef SCHED_MAX_CACHE
83 /* Eliminate the cache */
99 ast_mutex_unlock(&con->lock);
103 static struct sched *sched_alloc(struct sched_context *con)
106 * We keep a small cache of schedule entries
107 * to minimize the number of necessary malloc()'s
110 #ifdef SCHED_MAX_CACHE
113 con->schedc = con->schedc->next;
117 tmp = malloc(sizeof(struct sched));
121 static void sched_release(struct sched_context *con, struct sched *tmp)
124 * Add to the cache, or just free() if we
125 * already have too many cache entries
128 #ifdef SCHED_MAX_CACHE
129 if (con->schedccnt < SCHED_MAX_CACHE) {
130 tmp->next = con->schedc;
138 int ast_sched_wait(struct sched_context *con)
141 * Return the number of milliseconds
142 * until the next scheduled event
146 DEBUG(ast_log(LOG_DEBUG, "ast_sched_wait()\n"));
147 ast_mutex_lock(&con->lock);
150 } else if (gettimeofday(&tv, NULL) < 0) {
151 /* This should never happen */
154 ms = (con->schedq->when.tv_sec - tv.tv_sec) * 1000;
155 ms += (con->schedq->when.tv_usec - tv.tv_usec) / 1000;
159 ast_mutex_unlock(&con->lock);
165 static void schedule(struct sched_context *con, struct sched *s)
168 * Take a sched structure and put it in the
169 * queue, such that the soonest event is
173 struct sched *last=NULL;
174 struct sched *current=con->schedq;
176 if (SOONER(s->when, current->when))
179 current = current->next;
181 /* Insert this event into the schedule */
190 static inline int sched_settime(struct timeval *tv, int when)
192 struct timeval tv_tmp;
193 long error_sec, error_usec;
195 if (gettimeofday(&tv_tmp, NULL) < 0) {
196 /* This shouldn't ever happen, but let's be sure */
197 ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
200 /*ast_log(LOG_DEBUG, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
201 if (((unsigned long)(tv->tv_sec) > 0)&&((unsigned long)(tv->tv_usec) > 0)) {
202 if ((unsigned long)(tv_tmp.tv_usec) < (unsigned long)(tv->tv_usec)) {
203 tv_tmp.tv_usec += 1000000;
206 error_sec = (unsigned long)(tv_tmp.tv_sec) - (unsigned long)(tv->tv_sec);
207 error_usec = (unsigned long)(tv_tmp.tv_usec) - (unsigned long)(tv->tv_usec);
209 /*ast_log(LOG_DEBUG, "Initializing error\n");*/
213 /*ast_log(LOG_DEBUG, "ERROR -> %lu,%lu\n", error_sec, error_usec);*/
214 if (error_sec * 1000 + error_usec / 1000 < when) {
215 tv->tv_sec = tv_tmp.tv_sec + (when/1000 - error_sec);
216 tv->tv_usec = tv_tmp.tv_usec + ((when % 1000) * 1000 - error_usec);
218 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
219 tv->tv_sec = tv_tmp.tv_sec;
220 tv->tv_usec = tv_tmp.tv_usec;
222 if (tv->tv_usec > 1000000) {
224 tv->tv_usec-= 1000000;
229 int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
232 * Schedule callback(data) to happen when ms into the future
236 DEBUG(ast_log(LOG_DEBUG, "ast_sched_add()\n"));
238 ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
241 ast_mutex_lock(&con->lock);
242 if ((tmp = sched_alloc(con))) {
243 tmp->id = con->eventcnt++;
244 tmp->callback = callback;
247 tmp->when.tv_sec = 0;
248 tmp->when.tv_usec = 0;
249 if (sched_settime(&tmp->when, when)) {
250 sched_release(con, tmp);
256 ast_mutex_unlock(&con->lock);
260 int ast_sched_del(struct sched_context *con, int id)
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
268 struct sched *last=NULL, *s;
269 DEBUG(ast_log(LOG_DEBUG, "ast_sched_del()\n"));
270 ast_mutex_lock(&con->lock);
275 last->next = s->next;
277 con->schedq = s->next;
279 sched_release(con, s);
285 ast_mutex_unlock(&con->lock);
287 ast_log(LOG_NOTICE, "Attempted to delete non-existant schedule entry %d!\n", id);
296 void ast_sched_dump(struct sched_context *con)
299 * Dump the contents of the scheduler to
305 gettimeofday(&tv, NULL);
306 #ifdef SCHED_MAX_CACHE
307 ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n",
308 con-> schedcnt, con->eventcnt - 1, con->schedccnt);
310 ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n",
311 con-> schedcnt, con->eventcnt - 1);
314 ast_log(LOG_DEBUG, "=================================================\n");
315 ast_log(LOG_DEBUG, "|ID Callback Data Time (sec:ms) |\n");
316 ast_log(LOG_DEBUG, "+-----+-----------+-----------+-----------------+\n");
319 s = q->when.tv_sec - tv.tv_sec;
320 ms = q->when.tv_usec - tv.tv_usec;
325 ast_log(LOG_DEBUG, "|%.4d | %p | %p | %.6ld : %.6ld |\n",
333 ast_log(LOG_DEBUG, "=================================================\n");
337 int ast_sched_runq(struct sched_context *con)
340 * Launch all events which need to be run at this time.
342 struct sched *current;
345 DEBUG(ast_log(LOG_DEBUG, "ast_sched_runq()\n"));
347 ast_mutex_lock(&con->lock);
351 if (gettimeofday(&tv, NULL)) {
352 /* This should never happen */
353 ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
356 /* We only care about millisecond accuracy anyway, so this will
357 help us get more than one event at one time if they are very
360 if (SOONER(con->schedq->when, tv)) {
361 current = con->schedq;
362 con->schedq = con->schedq->next;
366 * At this point, the schedule queue is still intact. We
367 * have removed the first event and the rest is still there,
368 * so it's permissible for the callback to add new events, but
369 * trying to delete itself won't work because it isn't in
370 * the schedule queue. If that's what it wants to do, it
373 if (current->callback(current->data)) {
375 * If they return non-zero, we should schedule them to be
378 if (sched_settime(¤t->when, current->resched)) {
379 sched_release(con, current);
381 schedule(con, current);
383 /* No longer needed, so release it */
384 sched_release(con, current);
390 ast_mutex_unlock(&con->lock);