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