/* $NetBSD: schedule.c,v 1.4 2006/09/09 16:22:10 manu Exp $ */ /* $KAME: schedule.c,v 1.19 2001/11/05 10:53:19 sakane Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "plog.h" #include "schedule.h" #include "var.h" #include "gcmalloc.h" #define FIXY2038PROBLEM #ifndef TAILQ_FOREACH #define TAILQ_FOREACH(elm, head, field) \ for (elm = TAILQ_FIRST(head); elm; elm = TAILQ_NEXT(elm, field)) #endif static struct timeval timeout; #ifdef FIXY2038PROBLEM #define Y2038TIME_T 0x7fffffff static time_t launched; /* time when the program launched. */ static time_t deltaY2038; #endif static TAILQ_HEAD(_schedtree, sched) sctree; static void sched_add __P((struct sched *)); static time_t current_time __P((void)); /* * schedule handler * OUT: * time to block until next event. * if no entry, NULL returned. */ struct timeval * schedular() { time_t now, delta; struct sched *p, *next = NULL; now = current_time(); for (p = TAILQ_FIRST(&sctree); p; p = next) { /* if the entry has been daed, remove it */ if (p->dead) goto next_schedule; /* if the time hasn't come, proceed to the next entry */ if (now < p->xtime) { next = TAILQ_NEXT(p, chain); continue; } /* mark it with dead. and call the function. */ p->dead = 1; if (p->func != NULL) (p->func)(p->param); next_schedule: next = TAILQ_NEXT(p, chain); TAILQ_REMOVE(&sctree, p, chain); racoon_free(p); } p = TAILQ_FIRST(&sctree); if (p == NULL) return NULL; now = current_time(); delta = p->xtime - now; timeout.tv_sec = delta < 0 ? 0 : delta; timeout.tv_usec = 0; return &timeout; } /* * add new schedule to schedule table. */ struct sched * sched_new(tick, func, param) time_t tick; void (*func) __P((void *)); void *param; { static long id = 1; struct sched *new; new = (struct sched *)racoon_malloc(sizeof(*new)); if (new == NULL) return NULL; memset(new, 0, sizeof(*new)); new->func = func; new->param = param; new->id = id++; time(&new->created); new->tick = tick; new->xtime = current_time() + tick; new->dead = 0; /* add to schedule table */ sched_add(new); return(new); } /* add new schedule to schedule table */ static void sched_add(sc) struct sched *sc; { struct sched *p; TAILQ_FOREACH(p, &sctree, chain) { if (sc->xtime < p->xtime) { TAILQ_INSERT_BEFORE(p, sc, chain); return; } } if (p == NULL) TAILQ_INSERT_TAIL(&sctree, sc, chain); return; } /* get current time. * if defined FIXY2038PROBLEM, base time is the time when called sched_init(). * Otherwise, conform to time(3). */ static time_t current_time() { time_t n; #ifdef FIXY2038PROBLEM time_t t; time(&n); t = n - launched; if (t < 0) t += deltaY2038; return t; #else return time(&n); #endif } void sched_kill(sc) struct sched *sc; { sc->dead = 1; return; } /* XXX this function is probably unnecessary. */ void sched_scrub_param(param) void *param; { struct sched *sc; TAILQ_FOREACH(sc, &sctree, chain) { if (sc->param == param) { if (!sc->dead) { plog(LLV_DEBUG, LOCATION, NULL, "an undead schedule has been deleted.\n"); } sched_kill(sc); } } } /* * for debug */ int sched_dump(buf, len) caddr_t *buf; int *len; { caddr_t new; struct sched *p; struct scheddump *dst; int cnt = 0; /* initialize */ *len = 0; *buf = NULL; TAILQ_FOREACH(p, &sctree, chain) cnt++; /* no entry */ if (cnt == 0) return -1; *len = cnt * sizeof(*dst); new = racoon_malloc(*len); if (new == NULL) return -1; dst = (struct scheddump *)new; p = TAILQ_FIRST(&sctree); while (p) { dst->xtime = p->xtime; dst->id = p->id; dst->created = p->created; dst->tick = p->tick; p = TAILQ_NEXT(p, chain); if (p == NULL) break; dst++; } *buf = new; return 0; } /* initialize schedule table */ void sched_init() { #ifdef FIXY2038PROBLEM time(&launched); deltaY2038 = Y2038TIME_T - launched; #endif TAILQ_INIT(&sctree); return; } #ifdef STEST #include #include #include #include void test(tick) int *tick; { printf("execute %d\n", *tick); racoon_free(tick); } void getstdin() { int *tick; char buf[16]; read(0, buf, sizeof(buf)); if (buf[0] == 'd') { struct scheddump *scbuf, *p; int len; sched_dump((caddr_t *)&scbuf, &len); if (scbuf == NULL) return; for (p = scbuf; len; p++) { printf("xtime=%ld\n", p->xtime); len -= sizeof(*p); } racoon_free(scbuf); return; } tick = (int *)racoon_malloc(sizeof(*tick)); *tick = atoi(buf); printf("new queue tick = %d\n", *tick); sched_new(*tick, test, tick); } int main() { static fd_set mask0; int nfds = 0; fd_set rfds; struct timeval *timeout; int error; FD_ZERO(&mask0); FD_SET(0, &mask0); nfds = 1; /* initialize */ sched_init(); while (1) { rfds = mask0; timeout = schedular(); error = select(nfds, &rfds, (fd_set *)0, (fd_set *)0, timeout); if (error < 0) { switch (errno) { case EINTR: continue; default: err(1, "select"); } /*NOTREACHED*/ } if (FD_ISSET(0, &rfds)) getstdin(); } } #endif