]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/isc/ev_timers.c
Merge llvm-project release/18.x llvmorg-18.1.3-0-gc13b7485b879
[FreeBSD/FreeBSD.git] / lib / libc / isc / ev_timers.c
1 /*-
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (c) 1995-1999 by Internet Software Consortium
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 /* ev_timers.c - implement timers for the eventlib
21  * vix 09sep95 [initial]
22  */
23
24 /* Import. */
25
26 #include "port_before.h"
27 #ifndef _LIBC
28 #include "fd_setsize.h"
29 #endif
30
31 #include <errno.h>
32
33 #ifndef _LIBC
34 #include <isc/assertions.h>
35 #endif
36 #include <isc/eventlib.h>
37 #include "eventlib_p.h"
38
39 #include "port_after.h"
40
41 /* Constants. */
42
43 #define MILLION 1000000
44 #define BILLION 1000000000
45
46 /* Forward. */
47
48 #ifdef _LIBC
49 static int      __evOptMonoTime;
50 #else
51 static int due_sooner(void *, void *);
52 static void set_index(void *, int);
53 static void free_timer(void *, void *);
54 static void print_timer(void *, void *);
55 static void idle_timeout(evContext, void *, struct timespec, struct timespec);
56
57 /* Private type. */
58
59 typedef struct {
60         evTimerFunc     func;
61         void *          uap;
62         struct timespec lastTouched;
63         struct timespec max_idle;
64         evTimer *       timer;
65 } idle_timer;
66 #endif
67
68 /* Public. */
69
70 struct timespec
71 evConsTime(time_t sec, long nsec) {
72         struct timespec x;
73
74         x.tv_sec = sec;
75         x.tv_nsec = nsec;
76         return (x);
77 }
78
79 struct timespec
80 evAddTime(struct timespec addend1, struct timespec addend2) {
81         struct timespec x;
82
83         x.tv_sec = addend1.tv_sec + addend2.tv_sec;
84         x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
85         if (x.tv_nsec >= BILLION) {
86                 x.tv_sec++;
87                 x.tv_nsec -= BILLION;
88         }
89         return (x);
90 }
91
92 struct timespec
93 evSubTime(struct timespec minuend, struct timespec subtrahend) {
94         struct timespec x;
95
96         x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
97         if (minuend.tv_nsec >= subtrahend.tv_nsec)
98                 x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
99         else {
100                 x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
101                 x.tv_sec--;
102         }
103         return (x);
104 }
105
106 int
107 evCmpTime(struct timespec a, struct timespec b) {
108         long x = a.tv_sec - b.tv_sec;
109
110         if (x == 0L)
111                 x = a.tv_nsec - b.tv_nsec;
112         return (x < 0L ? (-1) : x > 0L ? (1) : (0));
113 }
114
115 struct timespec
116 evNowTime(void) {
117         struct timeval now;
118 #ifdef CLOCK_REALTIME
119         struct timespec tsnow;
120         int m = CLOCK_REALTIME;
121
122 #ifdef CLOCK_MONOTONIC
123         if (__evOptMonoTime)
124                 m = CLOCK_MONOTONIC;
125 #endif
126         if (clock_gettime(m, &tsnow) == 0)
127                 return (tsnow);
128 #endif
129         if (gettimeofday(&now, NULL) < 0)
130                 return (evConsTime(0, 0));
131         return (evTimeSpec(now));
132 }
133
134 struct timespec
135 evUTCTime(void) {
136         struct timeval now;
137 #ifdef CLOCK_REALTIME
138         struct timespec tsnow;
139         if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0)
140                 return (tsnow);
141 #endif
142         if (gettimeofday(&now, NULL) < 0)
143                 return (evConsTime(0, 0));
144         return (evTimeSpec(now));
145 }
146
147 #ifndef _LIBC
148 struct timespec
149 evLastEventTime(evContext opaqueCtx) {
150         evContext_p *ctx = opaqueCtx.opaque;
151
152         return (ctx->lastEventTime);
153 }
154 #endif
155
156 struct timespec
157 evTimeSpec(struct timeval tv) {
158         struct timespec ts;
159
160         ts.tv_sec = tv.tv_sec;
161         ts.tv_nsec = tv.tv_usec * 1000;
162         return (ts);
163 }
164
165 #if !defined(USE_KQUEUE) || !defined(_LIBC)
166 struct timeval
167 evTimeVal(struct timespec ts) {
168         struct timeval tv;
169
170         tv.tv_sec = ts.tv_sec;
171         tv.tv_usec = ts.tv_nsec / 1000;
172         return (tv);
173 }
174 #endif
175
176 #ifndef _LIBC
177 int
178 evSetTimer(evContext opaqueCtx,
179            evTimerFunc func,
180            void *uap,
181            struct timespec due,
182            struct timespec inter,
183            evTimerID *opaqueID
184 ) {
185         evContext_p *ctx = opaqueCtx.opaque;
186         evTimer *id;
187
188         evPrintf(ctx, 1,
189 "evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
190                  ctx, func, uap,
191                  (long)due.tv_sec, due.tv_nsec,
192                  (long)inter.tv_sec, inter.tv_nsec);
193
194 #ifdef __hpux
195         /*
196          * tv_sec and tv_nsec are unsigned.
197          */
198         if (due.tv_nsec >= BILLION)
199                 EV_ERR(EINVAL);
200
201         if (inter.tv_nsec >= BILLION)
202                 EV_ERR(EINVAL);
203 #else
204         if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
205                 EV_ERR(EINVAL);
206
207         if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
208                 EV_ERR(EINVAL);
209 #endif
210
211         /* due={0,0} is a magic cookie meaning "now." */
212         if (due.tv_sec == (time_t)0 && due.tv_nsec == 0L)
213                 due = evNowTime();
214
215         /* Allocate and fill. */
216         OKNEW(id);
217         id->func = func;
218         id->uap = uap;
219         id->due = due;
220         id->inter = inter;
221
222         if (heap_insert(ctx->timers, id) < 0)
223                 return (-1);
224
225         /* Remember the ID if the caller provided us a place for it. */
226         if (opaqueID)
227                 opaqueID->opaque = id;
228
229         if (ctx->debug > 7) {
230                 evPrintf(ctx, 7, "timers after evSetTimer:\n");
231                 (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
232         }
233
234         return (0);
235 }
236
237 int
238 evClearTimer(evContext opaqueCtx, evTimerID id) {
239         evContext_p *ctx = opaqueCtx.opaque;
240         evTimer *del = id.opaque;
241
242         if (ctx->cur != NULL &&
243             ctx->cur->type == Timer &&
244             ctx->cur->u.timer.this == del) {
245                 evPrintf(ctx, 8, "deferring delete of timer (executing)\n");
246                 /*
247                  * Setting the interval to zero ensures that evDrop() will
248                  * clean up the timer.
249                  */
250                 del->inter = evConsTime(0, 0);
251                 return (0);
252         }
253
254         if (heap_element(ctx->timers, del->index) != del)
255                 EV_ERR(ENOENT);
256
257         if (heap_delete(ctx->timers, del->index) < 0)
258                 return (-1);
259         FREE(del);
260
261         if (ctx->debug > 7) {
262                 evPrintf(ctx, 7, "timers after evClearTimer:\n");
263                 (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
264         }
265
266         return (0);
267 }
268
269 int
270 evConfigTimer(evContext opaqueCtx,
271              evTimerID id,
272              const char *param,
273              int value
274 ) {
275         evContext_p *ctx = opaqueCtx.opaque;
276         evTimer *timer = id.opaque;
277         int result=0;
278
279         UNUSED(value);
280
281         if (heap_element(ctx->timers, timer->index) != timer)
282                 EV_ERR(ENOENT);
283
284         if (strcmp(param, "rate") == 0)
285                 timer->mode |= EV_TMR_RATE;
286         else if (strcmp(param, "interval") == 0)
287                 timer->mode &= ~EV_TMR_RATE;
288         else
289                 EV_ERR(EINVAL);
290
291         return (result);
292 }
293
294 int
295 evResetTimer(evContext opaqueCtx,
296              evTimerID id,
297              evTimerFunc func,
298              void *uap,
299              struct timespec due,
300              struct timespec inter
301 ) {
302         evContext_p *ctx = opaqueCtx.opaque;
303         evTimer *timer = id.opaque;
304         struct timespec old_due;
305         int result=0;
306
307         if (heap_element(ctx->timers, timer->index) != timer)
308                 EV_ERR(ENOENT);
309
310 #ifdef __hpux
311         /*
312          * tv_sec and tv_nsec are unsigned.
313          */
314         if (due.tv_nsec >= BILLION)
315                 EV_ERR(EINVAL);
316
317         if (inter.tv_nsec >= BILLION)
318                 EV_ERR(EINVAL);
319 #else
320         if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
321                 EV_ERR(EINVAL);
322
323         if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
324                 EV_ERR(EINVAL);
325 #endif
326
327         old_due = timer->due;
328
329         timer->func = func;
330         timer->uap = uap;
331         timer->due = due;
332         timer->inter = inter;
333
334         switch (evCmpTime(due, old_due)) {
335         case -1:
336                 result = heap_increased(ctx->timers, timer->index);
337                 break;
338         case 0:
339                 result = 0;
340                 break;
341         case 1:
342                 result = heap_decreased(ctx->timers, timer->index);
343                 break;
344         }
345
346         if (ctx->debug > 7) {
347                 evPrintf(ctx, 7, "timers after evResetTimer:\n");
348                 (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
349         }
350
351         return (result);
352 }
353
354 int
355 evSetIdleTimer(evContext opaqueCtx,
356                 evTimerFunc func,
357                 void *uap,
358                 struct timespec max_idle,
359                 evTimerID *opaqueID
360 ) {
361         evContext_p *ctx = opaqueCtx.opaque;
362         idle_timer *tt;
363
364         /* Allocate and fill. */
365         OKNEW(tt);
366         tt->func = func;
367         tt->uap = uap;
368         tt->lastTouched = ctx->lastEventTime;
369         tt->max_idle = max_idle;
370
371         if (evSetTimer(opaqueCtx, idle_timeout, tt,
372                        evAddTime(ctx->lastEventTime, max_idle),
373                        max_idle, opaqueID) < 0) {
374                 FREE(tt);
375                 return (-1);
376         }
377
378         tt->timer = opaqueID->opaque;
379
380         return (0);
381 }
382
383 int
384 evClearIdleTimer(evContext opaqueCtx, evTimerID id) {
385         evTimer *del = id.opaque;
386         idle_timer *tt = del->uap;
387
388         FREE(tt);
389         return (evClearTimer(opaqueCtx, id));
390 }
391
392 int
393 evResetIdleTimer(evContext opaqueCtx,
394                  evTimerID opaqueID,
395                  evTimerFunc func,
396                  void *uap,
397                  struct timespec max_idle
398 ) {
399         evContext_p *ctx = opaqueCtx.opaque;
400         evTimer *timer = opaqueID.opaque;
401         idle_timer *tt = timer->uap;
402
403         tt->func = func;
404         tt->uap = uap;
405         tt->lastTouched = ctx->lastEventTime;
406         tt->max_idle = max_idle;
407
408         return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt,
409                              evAddTime(ctx->lastEventTime, max_idle),
410                              max_idle));
411 }
412
413 int
414 evTouchIdleTimer(evContext opaqueCtx, evTimerID id) {
415         evContext_p *ctx = opaqueCtx.opaque;
416         evTimer *t = id.opaque;
417         idle_timer *tt = t->uap;
418
419         tt->lastTouched = ctx->lastEventTime;
420
421         return (0);
422 }
423
424 /* Public to the rest of eventlib. */
425
426 heap_context
427 evCreateTimers(const evContext_p *ctx) {
428
429         UNUSED(ctx);
430
431         return (heap_new(due_sooner, set_index, 2048));
432 }
433
434 void
435 evDestroyTimers(const evContext_p *ctx) {
436         (void) heap_for_each(ctx->timers, free_timer, NULL);
437         (void) heap_free(ctx->timers);
438 }
439
440 /* Private. */
441
442 static int
443 due_sooner(void *a, void *b) {
444         evTimer *a_timer, *b_timer;
445
446         a_timer = a;
447         b_timer = b;
448         return (evCmpTime(a_timer->due, b_timer->due) < 0);
449 }
450
451 static void
452 set_index(void *what, int index) {
453         evTimer *timer;
454
455         timer = what;
456         timer->index = index;
457 }
458
459 static void
460 free_timer(void *what, void *uap) {
461         evTimer *t = what;
462
463         UNUSED(uap);
464
465         FREE(t);
466 }
467
468 static void
469 print_timer(void *what, void *uap) {
470         evTimer *cur = what;
471         evContext_p *ctx = uap;
472
473         cur = what;
474         evPrintf(ctx, 7,
475             "  func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
476                  cur->func, cur->uap,
477                  (long)cur->due.tv_sec, cur->due.tv_nsec,
478                  (long)cur->inter.tv_sec, cur->inter.tv_nsec);
479 }
480
481 static void
482 idle_timeout(evContext opaqueCtx,
483              void *uap,
484              struct timespec due,
485              struct timespec inter
486 ) {
487         evContext_p *ctx = opaqueCtx.opaque;
488         idle_timer *this = uap;
489         struct timespec idle;
490
491         UNUSED(due);
492         UNUSED(inter);
493         
494         idle = evSubTime(ctx->lastEventTime, this->lastTouched);
495         if (evCmpTime(idle, this->max_idle) >= 0) {
496                 (this->func)(opaqueCtx, this->uap, this->timer->due,
497                              this->max_idle);
498                 /*
499                  * Setting the interval to zero will cause the timer to
500                  * be cleaned up in evDrop().
501                  */
502                 this->timer->inter = evConsTime(0, 0);
503                 FREE(this);
504         } else {
505                 /* evDrop() will reschedule the timer. */
506                 this->timer->inter = evSubTime(this->max_idle, idle);
507         }
508 }
509 #endif
510
511 /*! \file */