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