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