1*731a29e0Stteras /* $NetBSD: schedule.c,v 1.7 2009/01/23 09:10:13 tteras Exp $ */
28006965bSmanu
3a8f0ad3cSmanu /* $KAME: schedule.c,v 1.19 2001/11/05 10:53:19 sakane Exp $ */
4a8f0ad3cSmanu
5a8f0ad3cSmanu /*
6a8f0ad3cSmanu * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7fbf62026Stteras * Copyright (C) 2008 Timo Teras.
8a8f0ad3cSmanu * All rights reserved.
9a8f0ad3cSmanu *
10a8f0ad3cSmanu * Redistribution and use in source and binary forms, with or without
11a8f0ad3cSmanu * modification, are permitted provided that the following conditions
12a8f0ad3cSmanu * are met:
13a8f0ad3cSmanu * 1. Redistributions of source code must retain the above copyright
14a8f0ad3cSmanu * notice, this list of conditions and the following disclaimer.
15a8f0ad3cSmanu * 2. Redistributions in binary form must reproduce the above copyright
16a8f0ad3cSmanu * notice, this list of conditions and the following disclaimer in the
17a8f0ad3cSmanu * documentation and/or other materials provided with the distribution.
18a8f0ad3cSmanu * 3. Neither the name of the project nor the names of its contributors
19a8f0ad3cSmanu * may be used to endorse or promote products derived from this software
20a8f0ad3cSmanu * without specific prior written permission.
21a8f0ad3cSmanu *
22a8f0ad3cSmanu * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23a8f0ad3cSmanu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24a8f0ad3cSmanu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25a8f0ad3cSmanu * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26a8f0ad3cSmanu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27a8f0ad3cSmanu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28a8f0ad3cSmanu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29a8f0ad3cSmanu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30a8f0ad3cSmanu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31a8f0ad3cSmanu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a8f0ad3cSmanu * SUCH DAMAGE.
33a8f0ad3cSmanu */
34a8f0ad3cSmanu
35a8f0ad3cSmanu #include "config.h"
36a8f0ad3cSmanu
37a8f0ad3cSmanu #include <sys/types.h>
38a8f0ad3cSmanu #include <sys/param.h>
39a8f0ad3cSmanu #include <sys/time.h>
40a8f0ad3cSmanu #include <sys/queue.h>
41a8f0ad3cSmanu #include <sys/socket.h>
42a8f0ad3cSmanu
43a8f0ad3cSmanu #include <stdlib.h>
445d5e4e2fStteras #include <unistd.h>
45a8f0ad3cSmanu #include <stdio.h>
46a8f0ad3cSmanu #include <string.h>
47a8f0ad3cSmanu #include <errno.h>
48a8f0ad3cSmanu #include <time.h>
49a8f0ad3cSmanu
50a8f0ad3cSmanu #include "misc.h"
51a8f0ad3cSmanu #include "plog.h"
52a8f0ad3cSmanu #include "schedule.h"
53a8f0ad3cSmanu #include "var.h"
54a8f0ad3cSmanu #include "gcmalloc.h"
55a8f0ad3cSmanu
56a8f0ad3cSmanu #ifndef TAILQ_FOREACH
57a8f0ad3cSmanu #define TAILQ_FOREACH(elm, head, field) \
58a8f0ad3cSmanu for (elm = TAILQ_FIRST(head); elm; elm = TAILQ_NEXT(elm, field))
59a8f0ad3cSmanu #endif
60a8f0ad3cSmanu
61a8f0ad3cSmanu static TAILQ_HEAD(_schedtree, sched) sctree;
62a8f0ad3cSmanu
635d5e4e2fStteras void
sched_get_monotonic_time(tv)645d5e4e2fStteras sched_get_monotonic_time(tv)
655d5e4e2fStteras struct timeval *tv;
665d5e4e2fStteras {
675d5e4e2fStteras #ifdef HAVE_CLOCK_MONOTONIC
685d5e4e2fStteras struct timespec ts;
695d5e4e2fStteras
705d5e4e2fStteras clock_gettime(CLOCK_MONOTONIC, &ts);
715d5e4e2fStteras tv->tv_sec = ts.tv_sec;
725d5e4e2fStteras tv->tv_usec = ts.tv_nsec / 1000;
735d5e4e2fStteras #else
745d5e4e2fStteras gettimeofday(tv, NULL);
755d5e4e2fStteras #endif
765d5e4e2fStteras }
775d5e4e2fStteras
785d5e4e2fStteras time_t
sched_monotonic_to_time_t(tv,now)795d5e4e2fStteras sched_monotonic_to_time_t(tv, now)
805d5e4e2fStteras struct timeval *tv, *now;
815d5e4e2fStteras {
825d5e4e2fStteras #ifdef HAVE_CLOCK_MONOTONIC
835d5e4e2fStteras struct timeval mynow, res;
845d5e4e2fStteras
855d5e4e2fStteras if (now == NULL) {
865d5e4e2fStteras sched_get_monotonic_time(&mynow);
875d5e4e2fStteras now = &mynow;
885d5e4e2fStteras }
895d5e4e2fStteras timersub(now, tv, &res);
905d5e4e2fStteras
915d5e4e2fStteras return time(NULL) + res.tv_sec;
925d5e4e2fStteras #else
935d5e4e2fStteras return tv->tv_sec;
945d5e4e2fStteras #endif
955d5e4e2fStteras }
96a8f0ad3cSmanu
97a8f0ad3cSmanu /*
98a8f0ad3cSmanu * schedule handler
99a8f0ad3cSmanu * OUT:
100a8f0ad3cSmanu * time to block until next event.
101a8f0ad3cSmanu * if no entry, NULL returned.
102a8f0ad3cSmanu */
103a8f0ad3cSmanu struct timeval *
schedular()104a8f0ad3cSmanu schedular()
105a8f0ad3cSmanu {
1065d5e4e2fStteras static struct timeval timeout;
1075d5e4e2fStteras struct timeval now;
108fbf62026Stteras struct sched *p;
109a8f0ad3cSmanu
1105d5e4e2fStteras sched_get_monotonic_time(&now);
1115d5e4e2fStteras while (!TAILQ_EMPTY(&sctree) &&
1125d5e4e2fStteras timercmp(&TAILQ_FIRST(&sctree)->xtime, &now, <=)) {
113fbf62026Stteras void (*func)(struct sched *);
114a8f0ad3cSmanu
115fbf62026Stteras p = TAILQ_FIRST(&sctree);
116fbf62026Stteras func = p->func;
117fbf62026Stteras sched_cancel(p);
118fbf62026Stteras func(p);
119a8f0ad3cSmanu }
120a8f0ad3cSmanu
121a8f0ad3cSmanu p = TAILQ_FIRST(&sctree);
122a8f0ad3cSmanu if (p == NULL)
123a8f0ad3cSmanu return NULL;
124a8f0ad3cSmanu
1255d5e4e2fStteras timersub(&p->xtime, &now, &timeout);
126a8f0ad3cSmanu
127a8f0ad3cSmanu return &timeout;
128a8f0ad3cSmanu }
129a8f0ad3cSmanu
130a8f0ad3cSmanu /*
131a8f0ad3cSmanu * add new schedule to schedule table.
132a8f0ad3cSmanu */
133fbf62026Stteras void
sched_schedule(sc,tick,func)134fbf62026Stteras sched_schedule(sc, tick, func)
135fbf62026Stteras struct sched *sc;
136a8f0ad3cSmanu time_t tick;
137fbf62026Stteras void (*func) __P((struct sched *));
138a8f0ad3cSmanu {
139a8f0ad3cSmanu static long id = 1;
140a8f0ad3cSmanu struct sched *p;
1415d5e4e2fStteras struct timeval now;
142a8f0ad3cSmanu
143fbf62026Stteras sched_cancel(sc);
1445d5e4e2fStteras
145fbf62026Stteras sc->func = func;
146fbf62026Stteras sc->id = id++;
1475d5e4e2fStteras sc->tick.tv_sec = tick;
1485d5e4e2fStteras sc->tick.tv_usec = 0;
1495d5e4e2fStteras sched_get_monotonic_time(&now);
1505d5e4e2fStteras timeradd(&now, &sc->tick, &sc->xtime);
151fbf62026Stteras
152fbf62026Stteras /* add to schedule table */
153a8f0ad3cSmanu TAILQ_FOREACH(p, &sctree, chain) {
1545d5e4e2fStteras if (timercmp(&sc->xtime, &p->xtime, <))
155fbf62026Stteras break;
156a8f0ad3cSmanu }
157a8f0ad3cSmanu if (p == NULL)
158a8f0ad3cSmanu TAILQ_INSERT_TAIL(&sctree, sc, chain);
159fbf62026Stteras else
160fbf62026Stteras TAILQ_INSERT_BEFORE(p, sc, chain);
161a8f0ad3cSmanu }
162a8f0ad3cSmanu
163fbf62026Stteras /*
164fbf62026Stteras * cancel scheduled callback
165fbf62026Stteras */
166fbf62026Stteras void
sched_cancel(sc)167fbf62026Stteras sched_cancel(sc)
168fbf62026Stteras struct sched *sc;
169fbf62026Stteras {
170fbf62026Stteras if (sc->func != NULL) {
171fbf62026Stteras TAILQ_REMOVE(&sctree, sc, chain);
172fbf62026Stteras sc->func = NULL;
173fbf62026Stteras }
174fbf62026Stteras }
175fbf62026Stteras
176a8f0ad3cSmanu /*
177a8f0ad3cSmanu * for debug
178a8f0ad3cSmanu */
179a8f0ad3cSmanu int
sched_dump(buf,len)180a8f0ad3cSmanu sched_dump(buf, len)
181a8f0ad3cSmanu caddr_t *buf;
182a8f0ad3cSmanu int *len;
183a8f0ad3cSmanu {
184a8f0ad3cSmanu caddr_t new;
185a8f0ad3cSmanu struct sched *p;
186a8f0ad3cSmanu struct scheddump *dst;
1875d5e4e2fStteras struct timeval now, created;
188a8f0ad3cSmanu int cnt = 0;
189a8f0ad3cSmanu
190a8f0ad3cSmanu /* initialize */
191a8f0ad3cSmanu *len = 0;
192a8f0ad3cSmanu *buf = NULL;
193a8f0ad3cSmanu
194a8f0ad3cSmanu TAILQ_FOREACH(p, &sctree, chain)
195a8f0ad3cSmanu cnt++;
196a8f0ad3cSmanu
197a8f0ad3cSmanu /* no entry */
198a8f0ad3cSmanu if (cnt == 0)
199a8f0ad3cSmanu return -1;
200a8f0ad3cSmanu
201a8f0ad3cSmanu *len = cnt * sizeof(*dst);
202a8f0ad3cSmanu
203a8f0ad3cSmanu new = racoon_malloc(*len);
204a8f0ad3cSmanu if (new == NULL)
205a8f0ad3cSmanu return -1;
206a8f0ad3cSmanu dst = (struct scheddump *)new;
207a8f0ad3cSmanu
2085d5e4e2fStteras sched_get_monotonic_time(&now);
209a8f0ad3cSmanu p = TAILQ_FIRST(&sctree);
210a8f0ad3cSmanu while (p) {
2115d5e4e2fStteras timersub(&p->xtime, &p->tick, &created);
2125d5e4e2fStteras dst->xtime = p->xtime.tv_sec;
213a8f0ad3cSmanu dst->id = p->id;
2145d5e4e2fStteras dst->created = sched_monotonic_to_time_t(&created, &now);
2155d5e4e2fStteras dst->tick = p->tick.tv_sec;
216a8f0ad3cSmanu
217a8f0ad3cSmanu p = TAILQ_NEXT(p, chain);
218a8f0ad3cSmanu if (p == NULL)
219a8f0ad3cSmanu break;
220a8f0ad3cSmanu dst++;
221a8f0ad3cSmanu }
222a8f0ad3cSmanu
223a8f0ad3cSmanu *buf = new;
224a8f0ad3cSmanu
225a8f0ad3cSmanu return 0;
226a8f0ad3cSmanu }
227a8f0ad3cSmanu
228a8f0ad3cSmanu /* initialize schedule table */
229a8f0ad3cSmanu void
sched_init()230a8f0ad3cSmanu sched_init()
231a8f0ad3cSmanu {
232a8f0ad3cSmanu TAILQ_INIT(&sctree);
233a8f0ad3cSmanu }
234a8f0ad3cSmanu
235a8f0ad3cSmanu #ifdef STEST
236a8f0ad3cSmanu #include <sys/types.h>
237a8f0ad3cSmanu #include <sys/time.h>
238a8f0ad3cSmanu #include <unistd.h>
239a8f0ad3cSmanu #include <err.h>
240a8f0ad3cSmanu
241a8f0ad3cSmanu void
test(tick)242a8f0ad3cSmanu test(tick)
243a8f0ad3cSmanu int *tick;
244a8f0ad3cSmanu {
245a8f0ad3cSmanu printf("execute %d\n", *tick);
246a8f0ad3cSmanu racoon_free(tick);
247a8f0ad3cSmanu }
248a8f0ad3cSmanu
249a8f0ad3cSmanu void
getstdin()250a8f0ad3cSmanu getstdin()
251a8f0ad3cSmanu {
252a8f0ad3cSmanu int *tick;
253a8f0ad3cSmanu char buf[16];
254a8f0ad3cSmanu
255a8f0ad3cSmanu read(0, buf, sizeof(buf));
256a8f0ad3cSmanu if (buf[0] == 'd') {
257a8f0ad3cSmanu struct scheddump *scbuf, *p;
258a8f0ad3cSmanu int len;
259a8f0ad3cSmanu sched_dump((caddr_t *)&scbuf, &len);
260c8214a0aSmanu if (scbuf == NULL)
261a8f0ad3cSmanu return;
262a8f0ad3cSmanu for (p = scbuf; len; p++) {
263a8f0ad3cSmanu printf("xtime=%ld\n", p->xtime);
264a8f0ad3cSmanu len -= sizeof(*p);
265a8f0ad3cSmanu }
266a8f0ad3cSmanu racoon_free(scbuf);
267a8f0ad3cSmanu return;
268a8f0ad3cSmanu }
269a8f0ad3cSmanu
270a8f0ad3cSmanu tick = (int *)racoon_malloc(sizeof(*tick));
271a8f0ad3cSmanu *tick = atoi(buf);
272a8f0ad3cSmanu printf("new queue tick = %d\n", *tick);
273a8f0ad3cSmanu sched_new(*tick, test, tick);
274a8f0ad3cSmanu }
275a8f0ad3cSmanu
276a8f0ad3cSmanu int
main()277a8f0ad3cSmanu main()
278a8f0ad3cSmanu {
279a8f0ad3cSmanu static fd_set mask0;
280a8f0ad3cSmanu int nfds = 0;
281a8f0ad3cSmanu fd_set rfds;
282a8f0ad3cSmanu struct timeval *timeout;
283a8f0ad3cSmanu int error;
284a8f0ad3cSmanu
285a8f0ad3cSmanu FD_ZERO(&mask0);
286a8f0ad3cSmanu FD_SET(0, &mask0);
287a8f0ad3cSmanu nfds = 1;
288a8f0ad3cSmanu
289a8f0ad3cSmanu /* initialize */
290a8f0ad3cSmanu sched_init();
291a8f0ad3cSmanu
292a8f0ad3cSmanu while (1) {
293a8f0ad3cSmanu rfds = mask0;
294a8f0ad3cSmanu
295a8f0ad3cSmanu timeout = schedular();
296a8f0ad3cSmanu
297a8f0ad3cSmanu error = select(nfds, &rfds, (fd_set *)0, (fd_set *)0, timeout);
298a8f0ad3cSmanu if (error < 0) {
299a8f0ad3cSmanu switch (errno) {
300a8f0ad3cSmanu case EINTR: continue;
301a8f0ad3cSmanu default:
302a8f0ad3cSmanu err(1, "select");
303a8f0ad3cSmanu }
304a8f0ad3cSmanu /*NOTREACHED*/
305a8f0ad3cSmanu }
306a8f0ad3cSmanu
307a8f0ad3cSmanu if (FD_ISSET(0, &rfds))
308a8f0ad3cSmanu getstdin();
309a8f0ad3cSmanu }
310a8f0ad3cSmanu }
311a8f0ad3cSmanu #endif
312