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
49static int	__evOptMonoTime;
50#else
51static int due_sooner(void *, void *);
52static void set_index(void *, int);
53static void free_timer(void *, void *);
54static void print_timer(void *, void *);
55static void idle_timeout(evContext, void *, struct timespec, struct timespec);
56
57/* Private type. */
58
59typedef 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
70struct timespec
71evConsTime(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
79struct timespec
80evAddTime(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
92struct timespec
93evSubTime(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
106int
107evCmpTime(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
115struct timespec
116evNowTime(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
134struct timespec
135evUTCTime(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
148struct timespec
149evLastEventTime(evContext opaqueCtx) {
150	evContext_p *ctx = opaqueCtx.opaque;
151
152	return (ctx->lastEventTime);
153}
154#endif
155
156struct timespec
157evTimeSpec(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)
166struct timeval
167evTimeVal(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
177int
178evSetTimer(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
237int
238evClearTimer(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
269int
270evConfigTimer(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
294int
295evResetTimer(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
354int
355evSetIdleTimer(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
383int
384evClearIdleTimer(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
392int
393evResetIdleTimer(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
413int
414evTouchIdleTimer(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
426heap_context
427evCreateTimers(const evContext_p *ctx) {
428
429	UNUSED(ctx);
430
431	return (heap_new(due_sooner, set_index, 2048));
432}
433
434void
435evDestroyTimers(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
442static int
443due_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
451static void
452set_index(void *what, int index) {
453	evTimer *timer;
454
455	timer = what;
456	timer->index = index;
457}
458
459static void
460free_timer(void *what, void *uap) {
461	evTimer *t = what;
462
463	UNUSED(uap);
464
465	FREE(t);
466}
467
468static void
469print_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
481static void
482idle_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