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