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