1 /*	$NetBSD: t_timers.c,v 1.6 2014/12/10 04:37:54 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2007-2009, 2011, 2013  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1999-2001  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or 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 WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: t_timers.c,v 1.33 2011/03/14 14:13:10 fdupont Exp  */
21 
22 #include <config.h>
23 
24 #include <stdlib.h>
25 
26 #include <isc/condition.h>
27 #include <isc/mem.h>
28 #include <isc/platform.h>
29 #include <isc/task.h>
30 #include <isc/time.h>
31 #include <isc/timer.h>
32 #include <isc/util.h>
33 
34 #include <tests/t_api.h>
35 
36 #ifdef ISC_PLATFORM_USETHREADS
37 isc_boolean_t threaded = ISC_TRUE;
38 #else
39 isc_boolean_t threaded = ISC_FALSE;
40 #endif
41 
42 #define	Tx_FUDGE_SECONDS	0	     /* in absence of clock_getres() */
43 #define	Tx_FUDGE_NANOSECONDS	500000000    /* in absence of clock_getres() */
44 
45 static	isc_time_t	Tx_endtime;
46 static	isc_time_t	Tx_lasttime;
47 static	int		Tx_eventcnt;
48 static	int		Tx_nevents;
49 static	isc_mutex_t	Tx_mx;
50 static	isc_condition_t	Tx_cv;
51 static	int		Tx_nfails;
52 static	int		Tx_nprobs;
53 static	isc_timer_t    *Tx_timer;
54 static	int		Tx_seconds;
55 static	int		Tx_nanoseconds;
56 
57 static void
58 require_threads(void) {
59 	t_info("This test requires threads\n");
60 	t_result(T_THREADONLY);
61 	return;
62 }
63 
64 static void
65 tx_sde(isc_task_t *task, isc_event_t *event) {
66 	isc_result_t	isc_result;
67 
68 	UNUSED(task);
69 	UNUSED(event);
70 
71 	/*
72 	 * Signal shutdown processing complete.
73 	 */
74 	isc_result = isc_mutex_lock(&Tx_mx);
75 	if (isc_result != ISC_R_SUCCESS) {
76 		t_info("isc_mutex_lock failed %s\n",
77 		       isc_result_totext(isc_result));
78 		++Tx_nprobs;
79 	}
80 
81 	isc_result = isc_condition_signal(&Tx_cv);
82 	if (isc_result != ISC_R_SUCCESS) {
83 		t_info("isc_condition_signal failed %s\n",
84 		       isc_result_totext(isc_result));
85 		++Tx_nprobs;
86 	}
87 
88 	isc_result = isc_mutex_unlock(&Tx_mx);
89 	if (isc_result != ISC_R_SUCCESS) {
90 		t_info("isc_mutex_unlock failed %s\n",
91 		       isc_result_totext(isc_result));
92 		++Tx_nprobs;
93 	}
94 
95 	isc_event_free(&event);
96 }
97 
98 static void
99 tx_te(isc_task_t *task, isc_event_t *event) {
100 	isc_result_t	isc_result;
101 	isc_time_t	now;
102 	isc_time_t	base;
103 	isc_time_t	ulim;
104 	isc_time_t	llim;
105 	isc_interval_t	interval;
106 	isc_eventtype_t	expected_event_type;
107 
108 	++Tx_eventcnt;
109 
110 	t_info("tick %d\n", Tx_eventcnt);
111 
112 	expected_event_type = ISC_TIMEREVENT_LIFE;
113 	if ((isc_timertype_t) event->ev_arg == isc_timertype_ticker)
114 		expected_event_type = ISC_TIMEREVENT_TICK;
115 
116 	if (event->ev_type != expected_event_type) {
117 		t_info("expected event type %d, got %d\n",
118 			expected_event_type, (int) event->ev_type);
119 		++Tx_nfails;
120 	}
121 
122 	isc_result = isc_time_now(&now);
123 	if (isc_result == ISC_R_SUCCESS) {
124 		isc_interval_set(&interval, Tx_seconds, Tx_nanoseconds);
125 		isc_result = isc_time_add(&Tx_lasttime, &interval, &base);
126 		if (isc_result != ISC_R_SUCCESS) {
127 			t_info("isc_time_add failed %s\n",
128 			       isc_result_totext(isc_result));
129 			++Tx_nprobs;
130 		}
131 	} else {
132 		t_info("isc_time_now failed %s\n",
133 			isc_result_totext(isc_result));
134 		++Tx_nprobs;
135 	}
136 
137 	if (isc_result == ISC_R_SUCCESS) {
138 		isc_interval_set(&interval,
139 				 Tx_FUDGE_SECONDS, Tx_FUDGE_NANOSECONDS);
140 		isc_result = isc_time_add(&base, &interval, &ulim);
141 		if (isc_result != ISC_R_SUCCESS) {
142 			t_info("isc_time_add failed %s\n",
143 			       isc_result_totext(isc_result));
144 			++Tx_nprobs;
145 		}
146 	}
147 
148 	if (isc_result == ISC_R_SUCCESS) {
149 		isc_result = isc_time_subtract(&base, &interval, &llim);
150 		if (isc_result != ISC_R_SUCCESS) {
151 			t_info("isc_time_subtract failed %s\n",
152 			       isc_result_totext(isc_result));
153 			++Tx_nprobs;
154 		}
155 	}
156 
157 	if (isc_result == ISC_R_SUCCESS) {
158 		if (isc_time_compare(&llim, &now) > 0) {
159 			t_info("timer range error: early by "
160 			       "%lu microseconds\n",
161 			       (unsigned long)isc_time_microdiff(&base, &now));
162 			++Tx_nfails;
163 		} else if (isc_time_compare(&ulim, &now) < 0) {
164 			t_info("timer range error: late by "
165 			       "%lu microseconds\n",
166 			       (unsigned long)isc_time_microdiff(&now, &base));
167 			++Tx_nfails;
168 		}
169 		Tx_lasttime = now;
170 	}
171 
172 	if (Tx_eventcnt == Tx_nevents) {
173 		isc_result = isc_time_now(&Tx_endtime);
174 		if (isc_result != ISC_R_SUCCESS) {
175 			t_info("isc_time_now failed %s\n",
176 				isc_result_totext(isc_result));
177 			++Tx_nprobs;
178 		}
179 		isc_timer_detach(&Tx_timer);
180 		isc_task_shutdown(task);
181 	}
182 
183 	isc_event_free(&event);
184 }
185 
186 static void
187 t_timers_x(isc_timertype_t timertype, isc_time_t *expires,
188 	   isc_interval_t *interval,
189 	   void (*action)(isc_task_t *, isc_event_t *))
190 {
191 	char		*p;
192 	isc_mem_t	*mctx;
193 	isc_taskmgr_t	*tmgr;
194 	isc_task_t	*task;
195 	unsigned int	workers;
196 	isc_result_t	isc_result;
197 	isc_timermgr_t	*timermgr;
198 
199 	Tx_eventcnt = 0;
200 	isc_time_settoepoch(&Tx_endtime);
201 
202 	workers = 2;
203 	p = t_getenv("ISC_TASK_WORKERS");
204 	if (p != NULL)
205 		workers = atoi(p);
206 
207 	mctx = NULL;
208 	isc_result = isc_mem_create(0, 0, &mctx);
209 	if (isc_result != ISC_R_SUCCESS) {
210 		t_info("isc_mem_create failed %s\n",
211 		       isc_result_totext(isc_result));
212 		++Tx_nprobs;
213 		return;
214 	}
215 
216 	isc_result = isc_mutex_init(&Tx_mx);
217 	if (isc_result != ISC_R_SUCCESS) {
218 		t_info("isc_mutex_init failed %s\n",
219 		       isc_result_totext(isc_result));
220 		isc_mem_destroy(&mctx);
221 		++Tx_nprobs;
222 		return;
223 	}
224 
225 	isc_result = isc_condition_init(&Tx_cv);
226 	if (isc_result != ISC_R_SUCCESS) {
227 		t_info("isc_condition_init failed %s\n",
228 		       isc_result_totext(isc_result));
229 		DESTROYLOCK(&Tx_mx);
230 		isc_mem_destroy(&mctx);
231 		++Tx_nprobs;
232 		return;
233 	}
234 
235 	tmgr = NULL;
236 	isc_result = isc_taskmgr_create(mctx, workers, 0, &tmgr);
237 	if (isc_result != ISC_R_SUCCESS) {
238 		t_info("isc_taskmgr_create failed %s\n",
239 		       isc_result_totext(isc_result));
240 		DESTROYLOCK(&Tx_mx);
241 		(void) isc_condition_destroy(&Tx_cv);
242 		isc_mem_destroy(&mctx);
243 		++Tx_nprobs;
244 		return;
245 	}
246 
247 	timermgr = NULL;
248 	isc_result = isc_timermgr_create(mctx, &timermgr);
249 	if (isc_result != ISC_R_SUCCESS) {
250 		t_info("isc_timermgr_create failed %s\n",
251 		       isc_result_totext(isc_result));
252 		isc_taskmgr_destroy(&tmgr);
253 		DESTROYLOCK(&Tx_mx);
254 		(void) isc_condition_destroy(&Tx_cv);
255 		isc_mem_destroy(&mctx);
256 		++Tx_nprobs;
257 		return;
258 	}
259 
260 	isc_result = isc_mutex_lock(&Tx_mx);
261 	if (isc_result != ISC_R_SUCCESS) {
262 		t_info("isc_mutex_lock failed %s\n",
263 		       isc_result_totext(isc_result));
264 		isc_timermgr_destroy(&timermgr);
265 		isc_taskmgr_destroy(&tmgr);
266 		DESTROYLOCK(&Tx_mx);
267 		(void) isc_condition_destroy(&Tx_cv);
268 		isc_mem_destroy(&mctx);
269 		++Tx_nprobs;
270 		return;
271 	}
272 
273 	task = NULL;
274 	isc_result = isc_task_create(tmgr, 0, &task);
275 	if (isc_result != ISC_R_SUCCESS) {
276 		t_info("isc_task_create failed %s\n",
277 		       isc_result_totext(isc_result));
278 		isc_timermgr_destroy(&timermgr);
279 		isc_taskmgr_destroy(&tmgr);
280 		DESTROYLOCK(&Tx_mx);
281 		(void) isc_condition_destroy(&Tx_cv);
282 		isc_mem_destroy(&mctx);
283 		++Tx_nprobs;
284 		return;
285 	}
286 
287 	isc_result = isc_task_onshutdown(task, tx_sde, NULL);
288 	if (isc_result != ISC_R_SUCCESS) {
289 		t_info("isc_task_onshutdown failed %s\n",
290 		       isc_result_totext(isc_result));
291 		isc_timermgr_destroy(&timermgr);
292 		isc_task_destroy(&task);
293 		isc_taskmgr_destroy(&tmgr);
294 		DESTROYLOCK(&Tx_mx);
295 		(void) isc_condition_destroy(&Tx_cv);
296 		isc_mem_destroy(&mctx);
297 		++Tx_nprobs;
298 		return;
299 	}
300 
301 	isc_result = isc_time_now(&Tx_lasttime);
302 	if (isc_result != ISC_R_SUCCESS) {
303 		isc_timermgr_destroy(&timermgr);
304 		isc_task_destroy(&task);
305 		isc_taskmgr_destroy(&tmgr);
306 		DESTROYLOCK(&Tx_mx);
307 		(void) isc_condition_destroy(&Tx_cv);
308 		isc_mem_destroy(&mctx);
309 		++Tx_nprobs;
310 		return;
311 	}
312 
313 	Tx_timer = NULL;
314 	isc_result = isc_timer_create(timermgr, timertype, expires, interval,
315 				      task, action, (void *)timertype,
316 				      &Tx_timer);
317 
318 	if (isc_result != ISC_R_SUCCESS) {
319 		isc_timermgr_destroy(&timermgr);
320 		isc_task_destroy(&task);
321 		isc_taskmgr_destroy(&tmgr);
322 		DESTROYLOCK(&Tx_mx);
323 		(void) isc_condition_destroy(&Tx_cv);
324 		isc_mem_destroy(&mctx);
325 		++Tx_nprobs;
326 		return;
327 	}
328 
329 	/*
330 	 * Wait for shutdown processing to complete.
331 	 */
332 	while (Tx_eventcnt != Tx_nevents) {
333 		isc_result = isc_condition_wait(&Tx_cv, &Tx_mx);
334 		if (isc_result != ISC_R_SUCCESS) {
335 			t_info("isc_condition_waituntil failed %s\n",
336 			       isc_result_totext(isc_result));
337 			++Tx_nprobs;
338 		}
339 	}
340 
341 	isc_result = isc_mutex_unlock(&Tx_mx);
342 	if (isc_result != ISC_R_SUCCESS) {
343 		t_info("isc_mutex_unlock failed %s\n",
344 		       isc_result_totext(isc_result));
345 		++Tx_nprobs;
346 	}
347 
348 	isc_task_detach(&task);
349 	isc_taskmgr_destroy(&tmgr);
350 	isc_timermgr_destroy(&timermgr);
351 	DESTROYLOCK(&Tx_mx);
352 	(void) isc_condition_destroy(&Tx_cv);
353 	isc_mem_destroy(&mctx);
354 
355 }
356 
357 #define	T1_SECONDS	2
358 #define	T1_NANOSECONDS	500000000
359 
360 static const char *a1 =
361 	"When type is isc_timertype_ticker, a call to isc_timer_create() "
362 	"creates a timer that posts an ISC_TIMEREVENT_TICK event to the "
363 	"specified task every 'interval' seconds and returns ISC_R_SUCCESS.";
364 
365 static void
366 t1(void) {
367 	int		result;
368 	isc_time_t	expires;
369 	isc_interval_t	interval;
370 
371 	t_assert("isc_timer_create", 1, T_REQUIRED, "%s", a1);
372 
373 	if (threaded) {
374 		Tx_nfails	= 0;
375 		Tx_nprobs	= 0;
376 		Tx_nevents	= 12;
377 		Tx_seconds	= T1_SECONDS;
378 		Tx_nanoseconds	= T1_NANOSECONDS;
379 		isc_interval_set(&interval, Tx_seconds, Tx_nanoseconds);
380 		isc_time_settoepoch(&expires);
381 
382 		t_timers_x(isc_timertype_ticker, &expires, &interval, tx_te);
383 
384 		result = T_UNRESOLVED;
385 
386 		if ((Tx_nfails == 0) && (Tx_nprobs == 0))
387 			result = T_PASS;
388 		else if (Tx_nfails)
389 			result = T_FAIL;
390 
391 		t_result(result);
392 	} else
393 		require_threads();
394 }
395 
396 #define	T2_SECONDS	5
397 #define	T2_NANOSECONDS	300000000;
398 
399 static const char *a2 =
400 	"When type is isc_timertype_once, a call to isc_timer_create() "
401 	"creates a timer that posts an ISC_TIMEEVENT_LIFE event to the "
402 	"specified task when the current time reaches or exceeds the time "
403 	"specified by 'expires'.";
404 
405 static void
406 t2(void) {
407 	int		result;
408 	int		isc_result;
409 	isc_time_t	expires;
410 	isc_interval_t	interval;
411 
412 	t_assert("isc_timer_create", 2, T_REQUIRED, "%s", a2);
413 
414 	if (threaded) {
415 		Tx_nfails	= 0;
416 		Tx_nprobs	= 0;
417 		Tx_nevents	= 1;
418 		Tx_seconds	= T2_SECONDS;
419 		Tx_nanoseconds	= T2_NANOSECONDS;
420 		isc_interval_set(&interval, Tx_seconds, Tx_nanoseconds);
421 
422 		isc_result = isc_time_nowplusinterval(&expires, &interval);
423 		if (isc_result == ISC_R_SUCCESS) {
424 
425 			isc_interval_set(&interval, 0, 0);
426 			t_timers_x(isc_timertype_once, &expires, &interval,
427 				   tx_te);
428 
429 		} else {
430 			t_info("isc_time_nowplusinterval failed %s\n",
431 			       isc_result_totext(isc_result));
432 		}
433 
434 		result = T_UNRESOLVED;
435 
436 		if ((Tx_nfails == 0) && (Tx_nprobs == 0))
437 			result = T_PASS;
438 		else if (Tx_nfails)
439 			result = T_FAIL;
440 
441 		t_result(result);
442 	} else
443 		require_threads();
444 }
445 
446 static void
447 t3_te(isc_task_t *task, isc_event_t *event) {
448 	isc_result_t	isc_result;
449 	isc_time_t	now;
450 	isc_time_t	base;
451 	isc_time_t	ulim;
452 	isc_time_t	llim;
453 	isc_interval_t	interval;
454 
455 	++Tx_eventcnt;
456 
457 	t_info("tick %d\n", Tx_eventcnt);
458 
459 	isc_result = isc_time_now(&now);
460 	if (isc_result != ISC_R_SUCCESS) {
461 		t_info("isc_time_now failed %s\n",
462 		       isc_result_totext(isc_result));
463 		++Tx_nprobs;
464 	}
465 
466 	if (isc_result == ISC_R_SUCCESS) {
467 		isc_interval_set(&interval, Tx_seconds, Tx_nanoseconds);
468 		isc_result = isc_time_add(&Tx_lasttime, &interval, &base);
469 		if (isc_result != ISC_R_SUCCESS) {
470 			t_info("isc_time_add failed %s\n",
471 			       isc_result_totext(isc_result));
472 			++Tx_nprobs;
473 		}
474 	}
475 
476 	if (isc_result == ISC_R_SUCCESS) {
477 		isc_interval_set(&interval,
478 				 Tx_FUDGE_SECONDS, Tx_FUDGE_NANOSECONDS);
479 		isc_result = isc_time_add(&base, &interval, &ulim);
480 		if (isc_result != ISC_R_SUCCESS) {
481 			t_info("isc_time_add failed %s\n",
482 			       isc_result_totext(isc_result));
483 			++Tx_nprobs;
484 		}
485 	}
486 
487 	if (isc_result == ISC_R_SUCCESS) {
488 		isc_result = isc_time_subtract(&base, &interval, &llim);
489 		if (isc_result != ISC_R_SUCCESS) {
490 			t_info("isc_time_subtract failed %s\n",
491 			       isc_result_totext(isc_result));
492 			++Tx_nprobs;
493 		}
494 	}
495 
496 	if (isc_result == ISC_R_SUCCESS) {
497 		if (isc_time_compare(&llim, &now) > 0) {
498 			t_info("timer range error: early by "
499 			       "%lu microseconds\n",
500 			       (unsigned long)isc_time_microdiff(&base, &now));
501 			++Tx_nfails;
502 		} else if (isc_time_compare(&ulim, &now) < 0) {
503 			t_info("timer range error: late by "
504 			       "%lu microseconds\n",
505 			       (unsigned long)isc_time_microdiff(&now, &base));
506 			++Tx_nfails;
507 		}
508 		Tx_lasttime = now;
509 	}
510 
511 	if (event->ev_type != ISC_TIMEREVENT_IDLE) {
512 		t_info("received event type %d, expected type %d\n",
513 		       event->ev_type, ISC_TIMEREVENT_IDLE);
514 		++Tx_nfails;
515 	}
516 
517 	isc_timer_detach(&Tx_timer);
518 	isc_task_shutdown(task);
519 	isc_event_free(&event);
520 }
521 
522 #define	T3_SECONDS	4
523 #define	T3_NANOSECONDS	400000000
524 
525 static const char *a3 =
526 	"When type is isc_timertype_once, a call to isc_timer_create() "
527 	"creates a timer that posts an ISC_TIMEEVENT_IDLE event to the "
528 	"specified task when the timer has been idle for 'interval' seconds.";
529 
530 static void
531 t3(void) {
532 	int		result;
533 	int		isc_result;
534 	isc_time_t	expires;
535 	isc_interval_t	interval;
536 
537 	t_assert("isc_timer_create", 3, T_REQUIRED, "%s", a3);
538 
539 	if (threaded) {
540 		Tx_nfails	= 0;
541 		Tx_nprobs	= 0;
542 		Tx_nevents	= 1;
543 		Tx_seconds	= T3_SECONDS;
544 		Tx_nanoseconds	= T3_NANOSECONDS;
545 
546 		isc_interval_set(&interval, Tx_seconds + 1, Tx_nanoseconds);
547 
548 		isc_result = isc_time_nowplusinterval(&expires, &interval);
549 		if (isc_result == ISC_R_SUCCESS) {
550 			isc_interval_set(&interval, Tx_seconds,
551 					 Tx_nanoseconds);
552 			t_timers_x(isc_timertype_once, &expires, &interval,
553 				   t3_te);
554 		} else {
555 			t_info("isc_time_nowplusinterval failed %s\n",
556 			       isc_result_totext(isc_result));
557 			++Tx_nprobs;
558 		}
559 
560 		result = T_UNRESOLVED;
561 
562 		if ((Tx_nfails == 0) && (Tx_nprobs == 0))
563 			result = T_PASS;
564 		else if (Tx_nfails)
565 			result = T_FAIL;
566 
567 		t_result(result);
568 	} else
569 		require_threads();
570 }
571 
572 #define	T4_SECONDS	2
573 #define	T4_NANOSECONDS	500000000
574 
575 static void
576 t4_te(isc_task_t *task, isc_event_t *event) {
577 
578 	isc_result_t	isc_result;
579 	isc_time_t	now;
580 	isc_time_t	base;
581 	isc_time_t	ulim;
582 	isc_time_t	llim;
583 	isc_time_t	expires;
584 	isc_interval_t	interval;
585 
586 	++Tx_eventcnt;
587 
588 	t_info("tick %d\n", Tx_eventcnt);
589 
590 	/*
591 	 * Check expired time.
592 	 */
593 
594 	isc_result = isc_time_now(&now);
595 	if (isc_result != ISC_R_SUCCESS) {
596 		t_info("isc_time_now failed %s\n",
597 		       isc_result_totext(isc_result));
598 		++Tx_nprobs;
599 	}
600 
601 	if (isc_result == ISC_R_SUCCESS) {
602 		isc_interval_set(&interval, Tx_seconds, Tx_nanoseconds);
603 		isc_result = isc_time_add(&Tx_lasttime, &interval, &base);
604 		if (isc_result != ISC_R_SUCCESS) {
605 			t_info("isc_time_add failed %s\n",
606 			       isc_result_totext(isc_result));
607 			++Tx_nprobs;
608 		}
609 	}
610 
611 	if (isc_result == ISC_R_SUCCESS) {
612 		isc_interval_set(&interval,
613 				 Tx_FUDGE_SECONDS, Tx_FUDGE_NANOSECONDS);
614 		isc_result = isc_time_add(&base, &interval, &ulim);
615 		if (isc_result != ISC_R_SUCCESS) {
616 			t_info("isc_time_add failed %s\n",
617 			       isc_result_totext(isc_result));
618 			++Tx_nprobs;
619 		}
620 	}
621 
622 	if (isc_result == ISC_R_SUCCESS) {
623 		isc_result = isc_time_subtract(&base, &interval, &llim);
624 		if (isc_result != ISC_R_SUCCESS) {
625 			t_info("isc_time_subtract failed %s\n",
626 			       isc_result_totext(isc_result));
627 			++Tx_nprobs;
628 		}
629 	}
630 
631 	if (isc_result == ISC_R_SUCCESS) {
632 		if (isc_time_compare(&llim, &now) > 0) {
633 			t_info("timer range error: early by "
634 			       "%lu microseconds\n",
635 			       (unsigned long)isc_time_microdiff(&base, &now));
636 			++Tx_nfails;
637 		} else if (isc_time_compare(&ulim, &now) < 0) {
638 			t_info("timer range error: late by "
639 			       "%lu microseconds\n",
640 			       (unsigned long)isc_time_microdiff(&now, &base));
641 			++Tx_nfails;
642 		}
643 		Tx_lasttime = now;
644 	}
645 
646 	if (Tx_eventcnt < 3) {
647 		if (event->ev_type != ISC_TIMEREVENT_TICK) {
648 			t_info("received event type %d, expected type %d\n",
649 			       event->ev_type, ISC_TIMEREVENT_IDLE);
650 			++Tx_nfails;
651 		}
652 		if (Tx_eventcnt == 2) {
653 			isc_interval_set(&interval, T4_SECONDS,
654 					 T4_NANOSECONDS);
655 			isc_result = isc_time_nowplusinterval(&expires,
656 							      &interval);
657 			if (isc_result == ISC_R_SUCCESS) {
658 				isc_interval_set(&interval, 0, 0);
659 				isc_result =
660 					isc_timer_reset(Tx_timer,
661 							isc_timertype_once,
662 							&expires, &interval,
663 							ISC_FALSE);
664 				if (isc_result != ISC_R_SUCCESS) {
665 					t_info("isc_timer_reset failed %s\n",
666 					       isc_result_totext(isc_result));
667 					++Tx_nfails;
668 				}
669 			} else {
670 				t_info("isc_time_nowplusinterval failed %s\n",
671 				       isc_result_totext(isc_result));
672 				++Tx_nprobs;
673 			}
674 		}
675 	} else {
676 		if (event->ev_type != ISC_TIMEREVENT_LIFE) {
677 			t_info("received event type %d, expected type %d\n",
678 			       event->ev_type, ISC_TIMEREVENT_IDLE);
679 			++Tx_nfails;
680 		}
681 
682 		isc_timer_detach(&Tx_timer);
683 		isc_task_shutdown(task);
684 	}
685 
686 	isc_event_free(&event);
687 }
688 
689 static const char *a4 =
690 	"A call to isc_timer_reset() changes the timer's type, expires and "
691 	"interval values to the given values.";
692 
693 static void
694 t4(void) {
695 	int		result;
696 	isc_time_t	expires;
697 	isc_interval_t	interval;
698 
699 	t_assert("isc_timer_reset", 4, T_REQUIRED, "%s", a4);
700 
701 	if (threaded) {
702 		Tx_nfails = 0;
703 		Tx_nprobs = 0;
704 		Tx_nevents = 3;
705 		Tx_seconds = T4_SECONDS;
706 		Tx_nanoseconds = T4_NANOSECONDS;
707 
708 		isc_interval_set(&interval, T4_SECONDS, T4_NANOSECONDS);
709 		isc_time_settoepoch(&expires);
710 		t_timers_x(isc_timertype_ticker, &expires, &interval, t4_te);
711 
712 		result = T_UNRESOLVED;
713 
714 		if ((Tx_nfails == 0) && (Tx_nprobs == 0))
715 			result = T_PASS;
716 		else if (Tx_nfails)
717 			result = T_FAIL;
718 
719 		t_result(result);
720 	} else
721 		require_threads();
722 }
723 
724 #define	T5_NTICKS	4
725 #define	T5_SECONDS	3
726 
727 static	int		T5_startflag;
728 static	int		T5_shutdownflag;
729 static	int		T5_eventcnt;
730 static	isc_mutex_t	T5_mx;
731 static	isc_condition_t	T5_cv;
732 static	int		T5_nfails;
733 static	int		T5_nprobs;
734 static	isc_timer_t	*T5_tickertimer;
735 static	isc_timer_t	*T5_oncetimer;
736 static	isc_task_t	*T5_task1;
737 static	isc_task_t	*T5_task2;
738 
739 /*
740  * T5_task1 blocks on T5_mx while events accumulate
741  * in it's queue, until signaled by T5_task2.
742  */
743 
744 static void
745 t5_start_event(isc_task_t *task, isc_event_t *event) {
746 	isc_result_t	isc_result;
747 
748 	UNUSED(task);
749 
750 	t_info("t5_start_event\n");
751 
752 	isc_result = isc_mutex_lock(&T5_mx);
753 	if (isc_result != ISC_R_SUCCESS) {
754 		t_info("isc_mutex_lock failed %s\n",
755 		       isc_result_totext(isc_result));
756 		++T5_nprobs;
757 	}
758 
759 	while (! T5_startflag) {
760 		(void) isc_condition_wait(&T5_cv, &T5_mx);
761 	}
762 
763 	isc_result = isc_mutex_unlock(&T5_mx);
764 	if (isc_result != ISC_R_SUCCESS) {
765 		t_info("isc_mutex_unlock failed %s\n",
766 		       isc_result_totext(isc_result));
767 		++T5_nprobs;
768 	}
769 	isc_event_free(&event);
770 }
771 
772 static void
773 t5_tick_event(isc_task_t *task, isc_event_t *event) {
774 	isc_result_t	isc_result;
775 	isc_time_t	expires;
776 	isc_interval_t	interval;
777 
778 	UNUSED(task);
779 
780 	++T5_eventcnt;
781 	t_info("t5_tick_event %d\n", T5_eventcnt);
782 
783 	/*
784 	 * On the first tick, purge all remaining tick events
785 	 * and then shut down the task.
786 	 */
787 	if (T5_eventcnt == 1) {
788 		isc_time_settoepoch(&expires);
789 		isc_interval_set(&interval, T5_SECONDS, 0);
790 		isc_result = isc_timer_reset(T5_tickertimer,
791 					     isc_timertype_ticker, &expires,
792 					     &interval, ISC_TRUE);
793 		if (isc_result != ISC_R_SUCCESS) {
794 			t_info("isc_timer_reset failed %s\n",
795 			       isc_result_totext(isc_result));
796 			++T5_nfails;
797 		}
798 		isc_task_shutdown(task);
799 	}
800 	isc_event_free(&event);
801 }
802 
803 static void
804 t5_once_event(isc_task_t *task, isc_event_t *event) {
805 
806 	isc_result_t	isc_result;
807 
808 	t_info("t5_once_event\n");
809 
810 	/*
811 	 * Allow task1 to start processing events.
812 	 */
813 	isc_result = isc_mutex_lock(&T5_mx);
814 	if (isc_result != ISC_R_SUCCESS) {
815 		t_info("isc_mutex_lock failed %s\n",
816 		       isc_result_totext(isc_result));
817 		++T5_nprobs;
818 	}
819 
820 	T5_startflag = 1;
821 
822 	isc_result = isc_condition_broadcast(&T5_cv);
823 	if (isc_result != ISC_R_SUCCESS) {
824 		t_info("isc_condition_broadcast failed %s\n",
825 		       isc_result_totext(isc_result));
826 		++T5_nprobs;
827 	}
828 
829 	isc_result = isc_mutex_unlock(&T5_mx);
830 	if (isc_result != ISC_R_SUCCESS) {
831 		t_info("isc_mutex_unlock failed %s\n",
832 		       isc_result_totext(isc_result));
833 		++T5_nprobs;
834 	}
835 
836 	isc_event_free(&event);
837 	isc_task_shutdown(task);
838 }
839 
840 static void
841 t5_shutdown_event(isc_task_t *task, isc_event_t *event) {
842 
843 	isc_result_t	isc_result;
844 
845 	UNUSED(task);
846 	UNUSED(event);
847 
848 	t_info("t5_shutdown_event\n");
849 
850 	/*
851 	 * Signal shutdown processing complete.
852 	 */
853 	isc_result = isc_mutex_lock(&T5_mx);
854 	if (isc_result != ISC_R_SUCCESS) {
855 		t_info("isc_mutex_lock failed %s\n",
856 		       isc_result_totext(isc_result));
857 		++T5_nprobs;
858 	}
859 
860 	T5_shutdownflag = 1;
861 
862 	isc_result = isc_condition_signal(&T5_cv);
863 	if (isc_result != ISC_R_SUCCESS) {
864 		t_info("isc_condition_signal failed %s\n",
865 		       isc_result_totext(isc_result));
866 		++T5_nprobs;
867 	}
868 
869 	isc_result = isc_mutex_unlock(&T5_mx);
870 	if (isc_result != ISC_R_SUCCESS) {
871 		t_info("isc_mutex_unlock failed %s\n",
872 		       isc_result_totext(isc_result));
873 		++T5_nprobs;
874 	}
875 	isc_event_free(&event);
876 }
877 
878 static int
879 t_timers5(void) {
880 	char		*p;
881 	int		result;
882 	isc_mem_t	*mctx;
883 	isc_taskmgr_t	*tmgr;
884 	unsigned int	workers;
885 	isc_result_t	isc_result;
886 	isc_timermgr_t	*timermgr;
887 	isc_event_t	*event;
888 	isc_time_t	expires;
889 	isc_interval_t	interval;
890 
891 	T5_startflag = 0;
892 	T5_shutdownflag = 0;
893 	T5_eventcnt = 0;
894 
895 	workers = 2;
896 	p = t_getenv("ISC_TASK_WORKERS");
897 	if (p != NULL)
898 		workers = atoi(p);
899 
900 	mctx = NULL;
901 	isc_result = isc_mem_create(0, 0, &mctx);
902 	if (isc_result != ISC_R_SUCCESS) {
903 		t_info("isc_mem_create failed %s\n",
904 		       isc_result_totext(isc_result));
905 		return(T_UNRESOLVED);
906 	}
907 
908 	isc_result = isc_mutex_init(&T5_mx);
909 	if (isc_result != ISC_R_SUCCESS) {
910 		t_info("isc_mutex_init failed %s\n",
911 		       isc_result_totext(isc_result));
912 		isc_mem_destroy(&mctx);
913 		return(T_UNRESOLVED);
914 	}
915 
916 	isc_result = isc_condition_init(&T5_cv);
917 	if (isc_result != ISC_R_SUCCESS) {
918 		t_info("isc_condition_init failed %s\n",
919 		       isc_result_totext(isc_result));
920 		DESTROYLOCK(&T5_mx);
921 		isc_mem_destroy(&mctx);
922 		return(T_UNRESOLVED);
923 	}
924 
925 	tmgr = NULL;
926 	isc_result = isc_taskmgr_create(mctx, workers, 0, &tmgr);
927 	if (isc_result != ISC_R_SUCCESS) {
928 		t_info("isc_taskmgr_create failed %s\n",
929 		       isc_result_totext(isc_result));
930 		DESTROYLOCK(&T5_mx);
931 		(void) isc_condition_destroy(&T5_cv);
932 		isc_mem_destroy(&mctx);
933 		return(T_UNRESOLVED);
934 	}
935 
936 	timermgr = NULL;
937 	isc_result = isc_timermgr_create(mctx, &timermgr);
938 	if (isc_result != ISC_R_SUCCESS) {
939 		t_info("isc_timermgr_create failed %s\n",
940 		       isc_result_totext(isc_result));
941 		isc_taskmgr_destroy(&tmgr);
942 		DESTROYLOCK(&T5_mx);
943 		(void) isc_condition_destroy(&T5_cv);
944 		isc_mem_destroy(&mctx);
945 		return(T_UNRESOLVED);
946 	}
947 
948 	T5_task1 = NULL;
949 	isc_result = isc_task_create(tmgr, 0, &T5_task1);
950 	if (isc_result != ISC_R_SUCCESS) {
951 		t_info("isc_task_create failed %s\n",
952 		       isc_result_totext(isc_result));
953 		isc_timermgr_destroy(&timermgr);
954 		isc_taskmgr_destroy(&tmgr);
955 		DESTROYLOCK(&T5_mx);
956 		(void) isc_condition_destroy(&T5_cv);
957 		isc_mem_destroy(&mctx);
958 		return(T_UNRESOLVED);
959 	}
960 
961 	isc_result = isc_task_onshutdown(T5_task1, t5_shutdown_event, NULL);
962 	if (isc_result != ISC_R_SUCCESS) {
963 		t_info("isc_task_onshutdown failed %s\n",
964 		       isc_result_totext(isc_result));
965 		isc_timermgr_destroy(&timermgr);
966 		isc_task_destroy(&T5_task1);
967 		isc_taskmgr_destroy(&tmgr);
968 		DESTROYLOCK(&T5_mx);
969 		(void) isc_condition_destroy(&T5_cv);
970 		isc_mem_destroy(&mctx);
971 		return(T_UNRESOLVED);
972 	}
973 
974 	T5_task2 = NULL;
975 	isc_result = isc_task_create(tmgr, 0, &T5_task2);
976 	if (isc_result != ISC_R_SUCCESS) {
977 		t_info("isc_task_create failed %s\n",
978 		       isc_result_totext(isc_result));
979 		isc_timermgr_destroy(&timermgr);
980 		isc_task_destroy(&T5_task1);
981 		isc_taskmgr_destroy(&tmgr);
982 		DESTROYLOCK(&T5_mx);
983 		(void) isc_condition_destroy(&T5_cv);
984 		isc_mem_destroy(&mctx);
985 		return(T_UNRESOLVED);
986 	}
987 
988 	isc_result = isc_mutex_lock(&T5_mx);
989 	if (isc_result != ISC_R_SUCCESS) {
990 		t_info("isc_mutex_lock failed %s\n",
991 		       isc_result_totext(isc_result));
992 		isc_timermgr_destroy(&timermgr);
993 		isc_taskmgr_destroy(&tmgr);
994 		DESTROYLOCK(&T5_mx);
995 		(void) isc_condition_destroy(&T5_cv);
996 		isc_mem_destroy(&mctx);
997 		return(T_UNRESOLVED);
998 	}
999 
1000 	event = isc_event_allocate(mctx, (void *)1 , (isc_eventtype_t)1,
1001 				   t5_start_event, NULL, sizeof(*event));
1002 	isc_task_send(T5_task1, &event);
1003 
1004 	isc_time_settoepoch(&expires);
1005 	isc_interval_set(&interval, T5_SECONDS, 0);
1006 
1007 	T5_tickertimer = NULL;
1008 	isc_result = isc_timer_create(timermgr, isc_timertype_ticker,
1009 				      &expires, &interval, T5_task1,
1010 				      t5_tick_event, NULL, &T5_tickertimer);
1011 
1012 	if (isc_result != ISC_R_SUCCESS) {
1013 		isc_timermgr_destroy(&timermgr);
1014 		(void) isc_condition_signal(&T5_cv);
1015 		(void) isc_mutex_unlock(&T5_mx);
1016 		isc_task_destroy(&T5_task1);
1017 		isc_task_destroy(&T5_task2);
1018 		isc_taskmgr_destroy(&tmgr);
1019 		DESTROYLOCK(&T5_mx);
1020 		(void) isc_condition_destroy(&T5_cv);
1021 		isc_mem_destroy(&mctx);
1022 		return(T_UNRESOLVED);
1023 	}
1024 
1025 	T5_oncetimer = NULL;
1026 	isc_interval_set(&interval, (T5_SECONDS * T5_NTICKS) + 2, 0);
1027 	isc_result = isc_time_nowplusinterval(&expires, &interval);
1028 	if (isc_result != ISC_R_SUCCESS) {
1029 		isc_timer_detach(&T5_tickertimer);
1030 		isc_timermgr_destroy(&timermgr);
1031 		(void)isc_condition_signal(&T5_cv);
1032 		(void)isc_mutex_unlock(&T5_mx);
1033 		isc_task_destroy(&T5_task1);
1034 		isc_task_destroy(&T5_task2);
1035 		isc_taskmgr_destroy(&tmgr);
1036 		DESTROYLOCK(&T5_mx);
1037 		(void) isc_condition_destroy(&T5_cv);
1038 		isc_mem_destroy(&mctx);
1039 		return(T_UNRESOLVED);
1040 	}
1041 
1042 	isc_interval_set(&interval, 0, 0);
1043 	isc_result = isc_timer_create(timermgr, isc_timertype_once,
1044 				      &expires, &interval, T5_task2,
1045 				      t5_once_event, NULL, &T5_oncetimer);
1046 
1047 	if (isc_result != ISC_R_SUCCESS) {
1048 		isc_timer_detach(&T5_tickertimer);
1049 		isc_timermgr_destroy(&timermgr);
1050 		(void) isc_condition_signal(&T5_cv);
1051 		(void) isc_mutex_unlock(&T5_mx);
1052 		isc_task_destroy(&T5_task1);
1053 		isc_task_destroy(&T5_task2);
1054 		isc_taskmgr_destroy(&tmgr);
1055 		DESTROYLOCK(&T5_mx);
1056 		(void) isc_condition_destroy(&T5_cv);
1057 		isc_mem_destroy(&mctx);
1058 		++T5_nprobs;
1059 		return(T_UNRESOLVED);
1060 	}
1061 
1062 	/*
1063 	 * Wait for shutdown processing to complete.
1064 	 */
1065 	while (! T5_shutdownflag) {
1066 		isc_result = isc_condition_wait(&T5_cv, &T5_mx);
1067 		if (isc_result != ISC_R_SUCCESS) {
1068 			t_info("isc_condition_waituntil failed %s\n",
1069 			       isc_result_totext(isc_result));
1070 			++T5_nprobs;
1071 		}
1072 	}
1073 
1074 	isc_result = isc_mutex_unlock(&T5_mx);
1075 	if (isc_result != ISC_R_SUCCESS) {
1076 		t_info("isc_mutex_unlock failed %s\n",
1077 		       isc_result_totext(isc_result));
1078 		++T5_nprobs;
1079 	}
1080 
1081 	if (T5_eventcnt != 1) {
1082 		t_info("processed %d events\n", T5_eventcnt);
1083 		++T5_nfails;
1084 	}
1085 
1086 	isc_timer_detach(&T5_tickertimer);
1087 	isc_timer_detach(&T5_oncetimer);
1088 	isc_timermgr_destroy(&timermgr);
1089 	isc_task_destroy(&T5_task1);
1090 	isc_task_destroy(&T5_task2);
1091 	isc_taskmgr_destroy(&tmgr);
1092 	DESTROYLOCK(&T5_mx);
1093 	(void) isc_condition_destroy(&T5_cv);
1094 	isc_mem_destroy(&mctx);
1095 
1096 	result = T_UNRESOLVED;
1097 
1098 	if ((T5_nfails == 0) && (T5_nprobs == 0))
1099 		result = T_PASS;
1100 	else if (T5_nfails)
1101 		result = T_FAIL;
1102 
1103 	return (result);
1104 }
1105 
1106 static const char *a5 =
1107 	"When 'purge' is TRUE, a call to isc_timer_reset() purges any pending "
1108 	"events from 'timer' from the task's event queue.";
1109 
1110 static void
1111 t5(void) {
1112 	t_assert("isc_timer_reset", 5, T_REQUIRED, "%s", a5);
1113 
1114 	if (threaded)
1115 		t_result(t_timers5());
1116 	else
1117 		require_threads();
1118 }
1119 
1120 testspec_t	T_testlist[] = {
1121 	{	(PFV) t1,		"timer_create"		},
1122 	{	(PFV) t2,		"timer_create"		},
1123 	{	(PFV) t3,		"timer_create"		},
1124 	{	(PFV) t4,		"timer_reset"		},
1125 	{	(PFV) t5,		"timer_reset"		},
1126 	{	(PFV) NULL,		NULL			}
1127 };
1128 
1129 #ifdef WIN32
1130 int
1131 main(int argc, char **argv) {
1132 	t_settests(T_testlist);
1133 	return (t_main(argc, argv));
1134 }
1135 #endif
1136