xref: /illumos-gate/usr/src/lib/libresolv2/common/isc/ev_timers.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1995-1999 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22 
23 #pragma ident	"%Z%%M%	%I%	%E% SMI"
24 
25 /* ev_timers.c - implement timers for the eventlib
26  * vix 09sep95 [initial]
27  */
28 
29 #if !defined(LINT) && !defined(CODECENTER)
30 static const char rcsid[] = "$Id: ev_timers.c,v 1.33 2002/07/08 05:50:09 marka Exp $";
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 static int due_sooner(void *, void *);
54 static void set_index(void *, int);
55 static void free_timer(void *, void *);
56 static void print_timer(void *, void *);
57 static void idle_timeout(evContext, void *, struct timespec, struct timespec);
58 
59 /* Private type. */
60 
61 typedef struct {
62 	evTimerFunc	func;
63 	void *		uap;
64 	struct timespec	lastTouched;
65 	struct timespec	max_idle;
66 	evTimer *	timer;
67 } idle_timer;
68 
69 /* Public. */
70 
71 struct timespec
72 evConsTime(time_t sec, long nsec) {
73 	struct timespec x;
74 
75 	x.tv_sec = sec;
76 	x.tv_nsec = nsec;
77 	return (x);
78 }
79 
80 struct timespec
81 evAddTime(struct timespec addend1, struct timespec addend2) {
82 	struct timespec x;
83 
84 	x.tv_sec = addend1.tv_sec + addend2.tv_sec;
85 	x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
86 	if (x.tv_nsec >= BILLION) {
87 		x.tv_sec++;
88 		x.tv_nsec -= BILLION;
89 	}
90 	return (x);
91 }
92 
93 struct timespec
94 evSubTime(struct timespec minuend, struct timespec subtrahend) {
95 	struct timespec x;
96 
97 	x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
98 	if (minuend.tv_nsec >= subtrahend.tv_nsec)
99 		x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
100 	else {
101 		x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
102 		x.tv_sec--;
103 	}
104 	return (x);
105 }
106 
107 int
108 evCmpTime(struct timespec a, struct timespec b) {
109 	long x = a.tv_sec - b.tv_sec;
110 
111 	if (x == 0L)
112 		x = a.tv_nsec - b.tv_nsec;
113 	return (x < 0L ? (-1) : x > 0L ? (1) : (0));
114 }
115 
116 struct timespec
117 evNowTime() {
118 	struct timeval now;
119 
120 	if (gettimeofday(&now, NULL) < 0)
121 		return (evConsTime(0, 0));
122 	return (evTimeSpec(now));
123 }
124 
125 struct timespec
126 evLastEventTime(evContext opaqueCtx) {
127 	evContext_p *ctx = opaqueCtx.opaque;
128 
129 	return (ctx->lastEventTime);
130 }
131 
132 struct timespec
133 evTimeSpec(struct timeval tv) {
134 	struct timespec ts;
135 
136 	ts.tv_sec = tv.tv_sec;
137 	ts.tv_nsec = tv.tv_usec * 1000;
138 	return (ts);
139 }
140 
141 struct timeval
142 evTimeVal(struct timespec ts) {
143 	struct timeval tv;
144 
145 	tv.tv_sec = ts.tv_sec;
146 	tv.tv_usec = ts.tv_nsec / 1000;
147 	return (tv);
148 }
149 
150 int
151 evSetTimer(evContext opaqueCtx,
152 	   evTimerFunc func,
153 	   void *uap,
154 	   struct timespec due,
155 	   struct timespec inter,
156 	   evTimerID *opaqueID
157 ) {
158 	evContext_p *ctx = opaqueCtx.opaque;
159 	evTimer *id;
160 
161 	evPrintf(ctx, 1,
162 "evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
163 		 ctx, func, uap,
164 		 (long)due.tv_sec, due.tv_nsec,
165 		 (long)inter.tv_sec, inter.tv_nsec);
166 
167 	/* due={0,0} is a magic cookie meaning "now." */
168 	if (due.tv_sec == 0 && due.tv_nsec == 0L)
169 		due = evNowTime();
170 
171 	/* Allocate and fill. */
172 	OKNEW(id);
173 	id->func = func;
174 	id->uap = uap;
175 	id->due = due;
176 	id->inter = inter;
177 
178 	if (heap_insert(ctx->timers, id) < 0)
179 		return (-1);
180 
181 	/* Remember the ID if the caller provided us a place for it. */
182 	if (opaqueID)
183 		opaqueID->opaque = id;
184 
185 	if (ctx->debug > 7) {
186 		evPrintf(ctx, 7, "timers after evSetTimer:\n");
187 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
188 	}
189 
190 	return (0);
191 }
192 
193 int
194 evClearTimer(evContext opaqueCtx, evTimerID id) {
195 	evContext_p *ctx = opaqueCtx.opaque;
196 	evTimer *del = id.opaque;
197 
198 	if (ctx->cur != NULL &&
199 	    ctx->cur->type == Timer &&
200 	    ctx->cur->u.timer.this == del) {
201 		evPrintf(ctx, 8, "deferring delete of timer (executing)\n");
202 		/*
203 		 * Setting the interval to zero ensures that evDrop() will
204 		 * clean up the timer.
205 		 */
206 		del->inter = evConsTime(0, 0);
207 		return (0);
208 	}
209 
210 	if (heap_element(ctx->timers, del->index) != del)
211 		EV_ERR(ENOENT);
212 
213 	if (heap_delete(ctx->timers, del->index) < 0)
214 		return (-1);
215 	FREE(del);
216 
217 	if (ctx->debug > 7) {
218 		evPrintf(ctx, 7, "timers after evClearTimer:\n");
219 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
220 	}
221 
222 	return (0);
223 }
224 
225 int
226 evResetTimer(evContext opaqueCtx,
227 	     evTimerID id,
228 	     evTimerFunc func,
229 	     void *uap,
230 	     struct timespec due,
231 	     struct timespec inter
232 ) {
233 	evContext_p *ctx = opaqueCtx.opaque;
234 	evTimer *timer = id.opaque;
235 	struct timespec old_due;
236 	int result=0;
237 
238 	if (heap_element(ctx->timers, timer->index) != timer)
239 		EV_ERR(ENOENT);
240 
241 	old_due = timer->due;
242 
243 	timer->func = func;
244 	timer->uap = uap;
245 	timer->due = due;
246 	timer->inter = inter;
247 
248 	switch (evCmpTime(due, old_due)) {
249 	case -1:
250 		result = heap_increased(ctx->timers, timer->index);
251 		break;
252 	case 0:
253 		result = 0;
254 		break;
255 	case 1:
256 		result = heap_decreased(ctx->timers, timer->index);
257 		break;
258 	}
259 
260 	if (ctx->debug > 7) {
261 		evPrintf(ctx, 7, "timers after evResetTimer:\n");
262 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
263 	}
264 
265 	return (result);
266 }
267 
268 int
269 evSetIdleTimer(evContext opaqueCtx,
270 		evTimerFunc func,
271 		void *uap,
272 		struct timespec max_idle,
273 		evTimerID *opaqueID
274 ) {
275 	evContext_p *ctx = opaqueCtx.opaque;
276 	idle_timer *tt;
277 
278 	/* Allocate and fill. */
279 	OKNEW(tt);
280 	tt->func = func;
281 	tt->uap = uap;
282 	tt->lastTouched = ctx->lastEventTime;
283 	tt->max_idle = max_idle;
284 
285 	if (evSetTimer(opaqueCtx, idle_timeout, tt,
286 		       evAddTime(ctx->lastEventTime, max_idle),
287 		       max_idle, opaqueID) < 0) {
288 		FREE(tt);
289 		return (-1);
290 	}
291 
292 	tt->timer = opaqueID->opaque;
293 
294 	return (0);
295 }
296 
297 int
298 evClearIdleTimer(evContext opaqueCtx, evTimerID id) {
299 	evTimer *del = id.opaque;
300 	idle_timer *tt = del->uap;
301 
302 	FREE(tt);
303 	return (evClearTimer(opaqueCtx, id));
304 }
305 
306 int
307 evResetIdleTimer(evContext opaqueCtx,
308 		 evTimerID opaqueID,
309 		 evTimerFunc func,
310 		 void *uap,
311 		 struct timespec max_idle
312 ) {
313 	evContext_p *ctx = opaqueCtx.opaque;
314 	evTimer *timer = opaqueID.opaque;
315 	idle_timer *tt = timer->uap;
316 
317 	tt->func = func;
318 	tt->uap = uap;
319 	tt->lastTouched = ctx->lastEventTime;
320 	tt->max_idle = max_idle;
321 
322 	return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt,
323 			     evAddTime(ctx->lastEventTime, max_idle),
324 			     max_idle));
325 }
326 
327 int
328 evTouchIdleTimer(evContext opaqueCtx, evTimerID id) {
329 	evContext_p *ctx = opaqueCtx.opaque;
330 	evTimer *t = id.opaque;
331 	idle_timer *tt = t->uap;
332 
333 	tt->lastTouched = ctx->lastEventTime;
334 
335 	return (0);
336 }
337 
338 /* Public to the rest of eventlib. */
339 
340 heap_context
341 evCreateTimers(const evContext_p *ctx) {
342 
343 	UNUSED(ctx);
344 
345 	return (heap_new(due_sooner, set_index, 2048));
346 }
347 
348 void
349 evDestroyTimers(const evContext_p *ctx) {
350 	(void) heap_for_each(ctx->timers, free_timer, NULL);
351 	(void) heap_free(ctx->timers);
352 }
353 
354 /* Private. */
355 
356 static int
357 due_sooner(void *a, void *b) {
358 	evTimer *a_timer, *b_timer;
359 
360 	a_timer = a;
361 	b_timer = b;
362 	return (evCmpTime(a_timer->due, b_timer->due) < 0);
363 }
364 
365 static void
366 set_index(void *what, int index) {
367 	evTimer *timer;
368 
369 	timer = what;
370 	timer->index = index;
371 }
372 
373 static void
374 free_timer(void *what, void *uap) {
375 	evTimer *t = what;
376 
377 	UNUSED(uap);
378 
379 	FREE(t);
380 }
381 
382 static void
383 print_timer(void *what, void *uap) {
384 	evTimer *cur = what;
385 	evContext_p *ctx = uap;
386 
387 	cur = what;
388 	evPrintf(ctx, 7,
389 	    "  func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
390 		 cur->func, cur->uap,
391 		 (long)cur->due.tv_sec, cur->due.tv_nsec,
392 		 (long)cur->inter.tv_sec, cur->inter.tv_nsec);
393 }
394 
395 static void
396 idle_timeout(evContext opaqueCtx,
397 	     void *uap,
398 	     struct timespec due,
399 	     struct timespec inter
400 ) {
401 	evContext_p *ctx = opaqueCtx.opaque;
402 	idle_timer *this = uap;
403 	struct timespec idle;
404 
405 	UNUSED(due);
406 	UNUSED(inter);
407 
408 	idle = evSubTime(ctx->lastEventTime, this->lastTouched);
409 	if (evCmpTime(idle, this->max_idle) >= 0) {
410 		(this->func)(opaqueCtx, this->uap, this->timer->due,
411 			     this->max_idle);
412 		/*
413 		 * Setting the interval to zero will cause the timer to
414 		 * be cleaned up in evDrop().
415 		 */
416 		this->timer->inter = evConsTime(0, 0);
417 		FREE(this);
418 	} else {
419 		/* evDrop() will reschedule the timer. */
420 		this->timer->inter = evSubTime(this->max_idle, idle);
421 	}
422 }
423