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