Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/isc/ev_timers.c
39530 views
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 */
512
513