2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Scheduler Routines (from cheops-NG)
23 * \author Mark Spencer <markster@digium.com>
26 #ifdef DEBUG_SCHEDULER
27 #define DEBUG(a) DEBUG_M(a)
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42 #include "asterisk/sched.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/lock.h"
46 #include "asterisk/utils.h"
48 /* Determine if a is sooner than b */
49 #define SOONER(a,b) (((b).tv_sec > (a).tv_sec) || \
50 (((b).tv_sec == (a).tv_sec) && ((b).tv_usec > (a).tv_usec)))
53 struct sched *next; /*!< Next event in the list */
54 int id; /*!< ID number of event */
55 struct timeval when; /*!< Absolute time event should take place */
56 int resched; /*!< When to reschedule */
57 int variable; /*!< Use return value from callback to reschedule */
58 void *data; /*!< Data */
59 ast_sched_cb callback; /*!< Callback */
62 struct sched_context {
64 int eventcnt; /*!< Number of events processed */
65 int schedcnt; /*!< Number of outstanding schedule events */
66 struct sched *schedq; /*!< Schedule entry and main queue */
68 #ifdef SCHED_MAX_CACHE
69 struct sched *schedc; /*!< Cache of unused schedule structures and how many */
74 struct sched_context *sched_context_create(void)
76 struct sched_context *tmp;
77 tmp = malloc(sizeof(struct sched_context));
79 memset(tmp, 0, sizeof(struct sched_context));
80 ast_mutex_init(&tmp->lock);
84 #ifdef SCHED_MAX_CACHE
92 void sched_context_destroy(struct sched_context *con)
95 ast_mutex_lock(&con->lock);
96 #ifdef SCHED_MAX_CACHE
97 /* Eliminate the cache */
112 /* And the context */
113 ast_mutex_unlock(&con->lock);
114 ast_mutex_destroy(&con->lock);
118 static struct sched *sched_alloc(struct sched_context *con)
121 * We keep a small cache of schedule entries
122 * to minimize the number of necessary malloc()'s
125 #ifdef SCHED_MAX_CACHE
128 con->schedc = con->schedc->next;
132 tmp = malloc(sizeof(struct sched));
136 static void sched_release(struct sched_context *con, struct sched *tmp)
139 * Add to the cache, or just free() if we
140 * already have too many cache entries
143 #ifdef SCHED_MAX_CACHE
144 if (con->schedccnt < SCHED_MAX_CACHE) {
145 tmp->next = con->schedc;
154 * Return the number of milliseconds
155 * until the next scheduled event
157 int ast_sched_wait(struct sched_context *con)
160 DEBUG(ast_log(LOG_DEBUG, "ast_sched_wait()\n"));
161 ast_mutex_lock(&con->lock);
165 ms = ast_tvdiff_ms(con->schedq->when, ast_tvnow());
169 ast_mutex_unlock(&con->lock);
176 * Take a sched structure and put it in the
177 * queue, such that the soonest event is
180 static void schedule(struct sched_context *con, struct sched *s)
183 struct sched *last=NULL;
184 struct sched *current=con->schedq;
186 if (SOONER(s->when, current->when))
189 current = current->next;
191 /* Insert this event into the schedule */
201 * given the last event *tv and the offset in milliseconds 'when',
202 * computes the next value,
204 static int sched_settime(struct timeval *tv, int when)
206 struct timeval now = ast_tvnow();
208 /*ast_log(LOG_DEBUG, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
209 if (ast_tvzero(*tv)) /* not supplied, default to now */
211 *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
212 if (ast_tvcmp(*tv, now) < 0) {
213 ast_log(LOG_DEBUG, "Request to schedule in the past?!?!\n");
221 * Schedule callback(data) to happen when ms into the future
223 int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable)
227 DEBUG(ast_log(LOG_DEBUG, "ast_sched_add()\n"));
229 ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
232 ast_mutex_lock(&con->lock);
233 if ((tmp = sched_alloc(con))) {
234 tmp->id = con->eventcnt++;
235 tmp->callback = callback;
238 tmp->variable = variable;
239 tmp->when = ast_tv(0, 0);
240 if (sched_settime(&tmp->when, when)) {
241 sched_release(con, tmp);
247 #ifdef DUMP_SCHEDULER
248 /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
251 ast_mutex_unlock(&con->lock);
255 int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data)
257 return ast_sched_add_variable(con, when, callback, data, 0);
261 * Delete the schedule entry with number
262 * "id". It's nearly impossible that there
263 * would be two or more in the list with that
266 int ast_sched_del(struct sched_context *con, int id)
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 #ifdef DUMP_SCHEDULER
286 /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
289 ast_mutex_unlock(&con->lock);
291 ast_log(LOG_NOTICE, "Attempted to delete nonexistent schedule entry %d!\n", id);
300 void ast_sched_dump(const struct sched_context *con)
303 * Dump the contents of the scheduler to
307 struct timeval tv = ast_tvnow();
308 #ifdef SCHED_MAX_CACHE
309 ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
311 ast_log(LOG_DEBUG, "Asterisk Schedule Dump (%d in Q, %d Total)\n", 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");
317 for (q = con->schedq; q; q = q->next) {
318 struct timeval delta = ast_tvsub(q->when, tv);
320 ast_log(LOG_DEBUG, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
325 (long int)delta.tv_usec);
327 ast_log(LOG_DEBUG, "=============================================================\n");
332 * Launch all events which need to be run at this time.
334 int ast_sched_runq(struct sched_context *con)
336 struct sched *current;
340 DEBUG(ast_log(LOG_DEBUG, "ast_sched_runq()\n"));
342 ast_mutex_lock(&con->lock);
347 /* schedule all events which are going to expire within 1ms.
348 * We only care about millisecond accuracy anyway, so this will
349 * help us get more than one event at one time if they are very
352 tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
353 if (SOONER(con->schedq->when, tv)) {
354 current = con->schedq;
355 con->schedq = con->schedq->next;
359 * At this point, the schedule queue is still intact. We
360 * have removed the first event and the rest is still there,
361 * so it's permissible for the callback to add new events, but
362 * trying to delete itself won't work because it isn't in
363 * the schedule queue. If that's what it wants to do, it
367 ast_mutex_unlock(&con->lock);
368 res = current->callback(current->data);
369 ast_mutex_lock(&con->lock);
373 * If they return non-zero, we should schedule them to be
376 if (sched_settime(¤t->when, current->variable? res : current->resched)) {
377 sched_release(con, current);
379 schedule(con, current);
381 /* No longer needed, so release it */
382 sched_release(con, current);
388 ast_mutex_unlock(&con->lock);
392 long ast_sched_when(struct sched_context *con,int id)
396 DEBUG(ast_log(LOG_DEBUG, "ast_sched_when()\n"));
398 ast_mutex_lock(&con->lock);
401 if (s->id==id) break;
406 struct timeval now = ast_tvnow();
407 secs=s->when.tv_sec-now.tv_sec;
409 ast_mutex_unlock(&con->lock);