timer_test.c 13.1 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
4
5
6
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10
11
12
13
 */

#include <config.h>

Evan Hunt's avatar
Evan Hunt committed
14
15
16
17
18
#if HAVE_CMOCKA

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
19

Evan Hunt's avatar
Evan Hunt committed
20
21
#include <stdlib.h>
#include <string.h>
22
23
#include <unistd.h>

Evan Hunt's avatar
Evan Hunt committed
24
25
26
#define UNIT_TESTING
#include <cmocka.h>

27
#include <isc/condition.h>
Evan Hunt's avatar
Evan Hunt committed
28
#include <isc/commandline.h>
29
30
#include <isc/mem.h>
#include <isc/platform.h>
31
#include <isc/print.h>
32
33
34
35
36
37
38
#include <isc/task.h>
#include <isc/time.h>
#include <isc/timer.h>
#include <isc/util.h>

#include "isctest.h"

39
40
41
/* Set to true (or use -v option) for verbose output */
static bool verbose = false;

42
43
44
45
46
/*
 * This entire test requires threads.
 */
#ifdef ISC_PLATFORM_USETHREADS

47
48
49
#define	FUDGE_SECONDS	0	     /* in absence of clock_getres() */
#define	FUDGE_NANOSECONDS	500000000    /* in absence of clock_getres() */

Evan Hunt's avatar
Evan Hunt committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
static isc_timer_t *timer = NULL;
static isc_condition_t cv;
static isc_mutex_t mx;
static isc_time_t endtime;
static isc_time_t lasttime;
static int seconds;
static int nanoseconds;
static int eventcnt;
static int nevents;

static int
_setup(void **state) {
	isc_result_t result;

	UNUSED(state);

	/* Timer tests require two worker threads */
	result = isc_test_begin(NULL, true, 2);
	assert_int_equal(result, ISC_R_SUCCESS);

	return (0);
}

static int
_teardown(void **state) {
	UNUSED(state);

	isc_test_end();

	return (0);
}
81
82
83
84
85
86
87
88
89
90
91

static void
shutdown(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;

	UNUSED(task);

	/*
	 * Signal shutdown processing complete.
	 */
	result = isc_mutex_lock(&mx);
Evan Hunt's avatar
Evan Hunt committed
92
	assert_int_equal(result, ISC_R_SUCCESS);
93
94

	result = isc_condition_signal(&cv);
Evan Hunt's avatar
Evan Hunt committed
95
	assert_int_equal(result, ISC_R_SUCCESS);
96
97

	result = isc_mutex_unlock(&mx);
Evan Hunt's avatar
Evan Hunt committed
98
	assert_int_equal(result, ISC_R_SUCCESS);
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

	isc_event_free(&event);
}

static void
setup_test(isc_timertype_t timertype, isc_time_t *expires,
	   isc_interval_t *interval,
	   void (*action)(isc_task_t *, isc_event_t *))
{
	isc_result_t result;
	isc_task_t *task = NULL;
	isc_time_settoepoch(&endtime);
	eventcnt = 0;

	result = isc_mutex_init(&mx);
Evan Hunt's avatar
Evan Hunt committed
114
	assert_int_equal(result, ISC_R_SUCCESS);
115
116

	result = isc_condition_init(&cv);
Evan Hunt's avatar
Evan Hunt committed
117
	assert_int_equal(result, ISC_R_SUCCESS);
118
119
120
121

	LOCK(&mx);

	result = isc_task_create(taskmgr, 0, &task);
Evan Hunt's avatar
Evan Hunt committed
122
	assert_int_equal(result, ISC_R_SUCCESS);
123
124

	result = isc_task_onshutdown(task, shutdown, NULL);
Evan Hunt's avatar
Evan Hunt committed
125
	assert_int_equal(result, ISC_R_SUCCESS);
126
127

	result = isc_time_now(&lasttime);
Evan Hunt's avatar
Evan Hunt committed
128
	assert_int_equal(result, ISC_R_SUCCESS);
129
130
131
132

	result = isc_timer_create(timermgr, timertype, expires, interval,
				  task, action, (void *)timertype,
				  &timer);
Evan Hunt's avatar
Evan Hunt committed
133
	assert_int_equal(result, ISC_R_SUCCESS);
134
135
136
137
138
139

	/*
	 * Wait for shutdown processing to complete.
	 */
	while (eventcnt != nevents) {
		result = isc_condition_wait(&cv, &mx);
Evan Hunt's avatar
Evan Hunt committed
140
		assert_int_equal(result, ISC_R_SUCCESS);
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
	}

	UNLOCK(&mx);

	isc_task_detach(&task);
	DESTROYLOCK(&mx);
	(void) isc_condition_destroy(&cv);
}

static void
ticktock(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;
	isc_time_t now;
	isc_time_t base;
	isc_time_t ulim;
	isc_time_t llim;
	isc_interval_t interval;
	isc_eventtype_t expected_event_type;

	++eventcnt;

Evan Hunt's avatar
Evan Hunt committed
162
163
164
	if (verbose) {
		print_message("# tick %d\n", eventcnt);
	}
165
166
167
168
169
170
171

	expected_event_type = ISC_TIMEREVENT_LIFE;
	if ((isc_timertype_t) event->ev_arg == isc_timertype_ticker) {
		expected_event_type = ISC_TIMEREVENT_TICK;
	}

	if (event->ev_type != expected_event_type) {
Evan Hunt's avatar
Evan Hunt committed
172
173
		print_error("# expected event type %u, got %u\n",
			    expected_event_type, event->ev_type);
174
175
176
	}

	result = isc_time_now(&now);
Evan Hunt's avatar
Evan Hunt committed
177
	assert_int_equal(result, ISC_R_SUCCESS);
178
179
180

	isc_interval_set(&interval, seconds, nanoseconds);
	result = isc_time_add(&lasttime, &interval, &base);
Evan Hunt's avatar
Evan Hunt committed
181
	assert_int_equal(result, ISC_R_SUCCESS);
182
183
184

	isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS);
	result = isc_time_add(&base, &interval, &ulim);
Evan Hunt's avatar
Evan Hunt committed
185
	assert_int_equal(result, ISC_R_SUCCESS);
186
187

	result = isc_time_subtract(&base, &interval, &llim);
Evan Hunt's avatar
Evan Hunt committed
188
	assert_int_equal(result, ISC_R_SUCCESS);
189

Evan Hunt's avatar
Evan Hunt committed
190
191
	assert_true(isc_time_compare(&llim, &now) <= 0);
	assert_true(isc_time_compare(&ulim, &now) >= 0);
192
193
194
195
	lasttime = now;

	if (eventcnt == nevents) {
		result = isc_time_now(&endtime);
Evan Hunt's avatar
Evan Hunt committed
196
		assert_int_equal(result, ISC_R_SUCCESS);
197
198
199
200
201
202
203
204
205
206
207
		isc_timer_detach(&timer);
		isc_task_shutdown(task);
	}

	isc_event_free(&event);
}

/*
 * Individual unit tests
 */

Evan Hunt's avatar
Evan Hunt committed
208
209
210
/* timer type ticker */
static void
ticker(void **state) {
211
212
213
	isc_time_t expires;
	isc_interval_t interval;

Evan Hunt's avatar
Evan Hunt committed
214
	UNUSED(state);
215
216
217
218
219
220
221
222
223
224
225

	nevents = 12;
	seconds = 0;
	nanoseconds = 500000000;

	isc_interval_set(&interval, seconds, nanoseconds);
	isc_time_settoepoch(&expires);

	setup_test(isc_timertype_ticker, &expires, &interval, ticktock);
}

Evan Hunt's avatar
Evan Hunt committed
226
227
228
/* timer type once reaches lifetime */
static void
once_life(void **state) {
229
230
231
232
	isc_result_t result;
	isc_time_t expires;
	isc_interval_t interval;

Evan Hunt's avatar
Evan Hunt committed
233
	UNUSED(state);
234
235
236
237
238
239
240

	nevents = 1;
	seconds = 1;
	nanoseconds = 100000000;

	isc_interval_set(&interval, seconds, nanoseconds);
	result = isc_time_nowplusinterval(&expires, &interval);
Evan Hunt's avatar
Evan Hunt committed
241
	assert_int_equal(result, ISC_R_SUCCESS);
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

	isc_interval_set(&interval, 0, 0);

	setup_test(isc_timertype_once, &expires, &interval, ticktock);
}

static void
test_idle(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;
	isc_time_t now;
	isc_time_t base;
	isc_time_t ulim;
	isc_time_t llim;
	isc_interval_t interval;

	++eventcnt;

Evan Hunt's avatar
Evan Hunt committed
259
260
261
	if (verbose) {
		print_message("# tick %d\n", eventcnt);
	}
262
263

	result = isc_time_now(&now);
Evan Hunt's avatar
Evan Hunt committed
264
	assert_int_equal(result, ISC_R_SUCCESS);
265
266
267

	isc_interval_set(&interval, seconds, nanoseconds);
	result = isc_time_add(&lasttime, &interval, &base);
Evan Hunt's avatar
Evan Hunt committed
268
	assert_int_equal(result, ISC_R_SUCCESS);
269
270
271

	isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS);
	result = isc_time_add(&base, &interval, &ulim);
Evan Hunt's avatar
Evan Hunt committed
272
	assert_int_equal(result, ISC_R_SUCCESS);
273
274

	result = isc_time_subtract(&base, &interval, &llim);
Evan Hunt's avatar
Evan Hunt committed
275
	assert_int_equal(result, ISC_R_SUCCESS);
276

Evan Hunt's avatar
Evan Hunt committed
277
278
	assert_true(isc_time_compare(&llim, &now) <= 0);
	assert_true(isc_time_compare(&ulim, &now) >= 0);
279
280
	lasttime = now;

Evan Hunt's avatar
Evan Hunt committed
281
	assert_int_equal(event->ev_type, ISC_TIMEREVENT_IDLE);
282
283
284
285
286
287

	isc_timer_detach(&timer);
	isc_task_shutdown(task);
	isc_event_free(&event);
}

Evan Hunt's avatar
Evan Hunt committed
288
289
290
/* timer type once idles out */
static void
once_idle(void **state) {
291
292
293
294
	isc_result_t result;
	isc_time_t expires;
	isc_interval_t interval;

Evan Hunt's avatar
Evan Hunt committed
295
	UNUSED(state);
296
297
298
299
300
301
302

	nevents = 1;
	seconds = 1;
	nanoseconds = 200000000;

	isc_interval_set(&interval, seconds + 1, nanoseconds);
	result = isc_time_nowplusinterval(&expires, &interval);
Evan Hunt's avatar
Evan Hunt committed
303
	assert_int_equal(result, ISC_R_SUCCESS);
304
305
306
307
308
309

	isc_interval_set(&interval, seconds, nanoseconds);

	setup_test(isc_timertype_once, &expires, &interval, test_idle);
}

Evan Hunt's avatar
Evan Hunt committed
310
/* timer reset */
311
312
313
314
315
316
317
318
319
320
321
322
static void
test_reset(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;
	isc_time_t now;
	isc_time_t base;
	isc_time_t ulim;
	isc_time_t llim;
	isc_time_t expires;
	isc_interval_t interval;

	++eventcnt;

Evan Hunt's avatar
Evan Hunt committed
323
324
325
	if (verbose) {
		print_message("# tick %d\n", eventcnt);
	}
326
327
328
329
330
331

	/*
	 * Check expired time.
	 */

	result = isc_time_now(&now);
Evan Hunt's avatar
Evan Hunt committed
332
	assert_int_equal(result, ISC_R_SUCCESS);
333
334
335

	isc_interval_set(&interval, seconds, nanoseconds);
	result = isc_time_add(&lasttime, &interval, &base);
Evan Hunt's avatar
Evan Hunt committed
336
	assert_int_equal(result, ISC_R_SUCCESS);
337
338
339

	isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS);
	result = isc_time_add(&base, &interval, &ulim);
Evan Hunt's avatar
Evan Hunt committed
340
	assert_int_equal(result, ISC_R_SUCCESS);
341
342

	result = isc_time_subtract(&base, &interval, &llim);
Evan Hunt's avatar
Evan Hunt committed
343
	assert_int_equal(result, ISC_R_SUCCESS);
344

Evan Hunt's avatar
Evan Hunt committed
345
346
	assert_true(isc_time_compare(&llim, &now) <= 0);
	assert_true(isc_time_compare(&ulim, &now) >= 0);
347
348
349
	lasttime = now;

	if (eventcnt < 3) {
Evan Hunt's avatar
Evan Hunt committed
350
		assert_int_equal(event->ev_type, ISC_TIMEREVENT_TICK);
351
352
353
354

		if (eventcnt == 2) {
			isc_interval_set(&interval, seconds, nanoseconds);
			result = isc_time_nowplusinterval(&expires, &interval);
Evan Hunt's avatar
Evan Hunt committed
355
			assert_int_equal(result, ISC_R_SUCCESS);
356
357
358
359

			isc_interval_set(&interval, 0, 0);
			result = isc_timer_reset(timer, isc_timertype_once,
						 &expires, &interval,
360
						 false);
Evan Hunt's avatar
Evan Hunt committed
361
			assert_int_equal(result, ISC_R_SUCCESS);
362
363
		}
	} else {
Evan Hunt's avatar
Evan Hunt committed
364
		assert_int_equal(event->ev_type, ISC_TIMEREVENT_LIFE);
365
366
367
368
369
370
371
372

		isc_timer_detach(&timer);
		isc_task_shutdown(task);
	}

	isc_event_free(&event);
}

Evan Hunt's avatar
Evan Hunt committed
373
374
static void
reset(void **state) {
375
376
377
	isc_time_t expires;
	isc_interval_t interval;

Evan Hunt's avatar
Evan Hunt committed
378
	UNUSED(state);
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

	nevents = 3;
	seconds = 0;
	nanoseconds = 750000000;

	isc_interval_set(&interval, seconds, nanoseconds);
	isc_time_settoepoch(&expires);

	setup_test(isc_timertype_ticker, &expires, &interval, test_reset);
}

static int startflag;
static int shutdownflag;
static isc_timer_t *tickertimer = NULL;
static isc_timer_t *oncetimer = NULL;
static isc_task_t *task1 = NULL;
static isc_task_t *task2 = NULL;

/*
 * task1 blocks on mx while events accumulate
 * in its queue, until signaled by task2.
 */

static void
start_event(isc_task_t *task, isc_event_t *event) {
	UNUSED(task);

Evan Hunt's avatar
Evan Hunt committed
406
407
408
	if (verbose) {
		print_message("# start_event\n");
	}
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427

	LOCK(&mx);
	while (! startflag) {
		(void) isc_condition_wait(&cv, &mx);
	}
	UNLOCK(&mx);

	isc_event_free(&event);
}

static void
tick_event(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;
	isc_time_t expires;
	isc_interval_t interval;

	UNUSED(task);

	++eventcnt;
Evan Hunt's avatar
Evan Hunt committed
428
429
430
	if (verbose) {
		print_message("# tick_event %d\n", eventcnt);
	}
431
432
433
434
435
436
437
438
439

	/*
	 * On the first tick, purge all remaining tick events
	 * and then shut down the task.
	 */
	if (eventcnt == 1) {
		isc_time_settoepoch(&expires);
		isc_interval_set(&interval, seconds, 0);
		result = isc_timer_reset(tickertimer, isc_timertype_ticker,
440
					 &expires, &interval, true);
Evan Hunt's avatar
Evan Hunt committed
441
		assert_int_equal(result, ISC_R_SUCCESS);
442
443
444
445
446
447
448
449
450
451
452

		isc_task_shutdown(task);
	}

	isc_event_free(&event);
}

static void
once_event(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;

Evan Hunt's avatar
Evan Hunt committed
453
454
455
	if (verbose) {
		print_message("# once_event\n");
	}
456
457
458
459
460
461
462
463

	/*
	 * Allow task1 to start processing events.
	 */
	LOCK(&mx);
	startflag = 1;

	result = isc_condition_broadcast(&cv);
Evan Hunt's avatar
Evan Hunt committed
464
	assert_int_equal(result, ISC_R_SUCCESS);
465
466
467
468
469
470
471
472
473
474
475
476
477
	UNLOCK(&mx);

	isc_event_free(&event);
	isc_task_shutdown(task);
}

static void
shutdown_purge(isc_task_t *task, isc_event_t *event) {
	isc_result_t result;

	UNUSED(task);
	UNUSED(event);

Evan Hunt's avatar
Evan Hunt committed
478
479
480
	if (verbose) {
		print_message("# shutdown_event\n");
	}
481
482
483
484
485
486
487
488

	/*
	 * Signal shutdown processing complete.
	 */
	LOCK(&mx);
	shutdownflag = 1;

	result = isc_condition_signal(&cv);
Evan Hunt's avatar
Evan Hunt committed
489
	assert_int_equal(result, ISC_R_SUCCESS);
490
491
492
493
494
	UNLOCK(&mx);

	isc_event_free(&event);
}

Evan Hunt's avatar
Evan Hunt committed
495
496
497
/* timer events purged */
static void
purge(void **state) {
498
499
500
501
502
	isc_result_t result;
	isc_event_t *event = NULL;
	isc_time_t expires;
	isc_interval_t interval;

Evan Hunt's avatar
Evan Hunt committed
503
	UNUSED(state);
504
505
506
507
508
509
510
511

	startflag = 0;
	shutdownflag = 0;
	eventcnt = 0;
	seconds = 1;
	nanoseconds = 0;

	result = isc_mutex_init(&mx);
Evan Hunt's avatar
Evan Hunt committed
512
	assert_int_equal(result, ISC_R_SUCCESS);
513
514

	result = isc_condition_init(&cv);
Evan Hunt's avatar
Evan Hunt committed
515
	assert_int_equal(result, ISC_R_SUCCESS);
516
517

	result = isc_task_create(taskmgr, 0, &task1);
Evan Hunt's avatar
Evan Hunt committed
518
	assert_int_equal(result, ISC_R_SUCCESS);
519
520

	result = isc_task_onshutdown(task1, shutdown_purge, NULL);
Evan Hunt's avatar
Evan Hunt committed
521
	assert_int_equal(result, ISC_R_SUCCESS);
522
523

	result = isc_task_create(taskmgr, 0, &task2);
Evan Hunt's avatar
Evan Hunt committed
524
	assert_int_equal(result, ISC_R_SUCCESS);
525
526
527
528
529

	LOCK(&mx);

	event = isc_event_allocate(mctx, (void *)1 , (isc_eventtype_t)1,
				   start_event, NULL, sizeof(*event));
Evan Hunt's avatar
Evan Hunt committed
530
	assert_non_null(event);
531
532
533
534
535
536
537
538
539
	isc_task_send(task1, &event);

	isc_time_settoepoch(&expires);
	isc_interval_set(&interval, seconds, 0);

	tickertimer = NULL;
	result = isc_timer_create(timermgr, isc_timertype_ticker,
				  &expires, &interval, task1,
				  tick_event, NULL, &tickertimer);
Evan Hunt's avatar
Evan Hunt committed
540
	assert_int_equal(result, ISC_R_SUCCESS);
541
542
543
544
545

	oncetimer = NULL;

	isc_interval_set(&interval, (seconds * 2) + 1, 0);
	result = isc_time_nowplusinterval(&expires, &interval);
Evan Hunt's avatar
Evan Hunt committed
546
	assert_int_equal(result, ISC_R_SUCCESS);
547
548
549
550
551

	isc_interval_set(&interval, 0, 0);
	result = isc_timer_create(timermgr, isc_timertype_once,
				      &expires, &interval, task2,
				      once_event, NULL, &oncetimer);
Evan Hunt's avatar
Evan Hunt committed
552
	assert_int_equal(result, ISC_R_SUCCESS);
553
554
555
556
557
558

	/*
	 * Wait for shutdown processing to complete.
	 */
	while (! shutdownflag) {
		result = isc_condition_wait(&cv, &mx);
Evan Hunt's avatar
Evan Hunt committed
559
		assert_int_equal(result, ISC_R_SUCCESS);
560
561
562
563
	}

	UNLOCK(&mx);

Evan Hunt's avatar
Evan Hunt committed
564
	assert_int_equal(eventcnt, 1);
565
566
567
568
569
570

	isc_timer_detach(&tickertimer);
	isc_timer_detach(&oncetimer);
	isc_task_destroy(&task1);
	isc_task_destroy(&task2);
	DESTROYLOCK(&mx);
571
}
572
#endif
573

Evan Hunt's avatar
Evan Hunt committed
574
575
int
main(int argc, char **argv) {
576
#ifdef ISC_PLATFORM_USETHREADS
Petr Menšík's avatar
Petr Menšík committed
577
	const struct CMUnitTest tests[] = {
Evan Hunt's avatar
Evan Hunt committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
		cmocka_unit_test_setup_teardown(ticker, _setup, _teardown),
		cmocka_unit_test_setup_teardown(once_life, _setup, _teardown),
		cmocka_unit_test_setup_teardown(once_idle, _setup, _teardown),
		cmocka_unit_test_setup_teardown(reset, _setup, _teardown),
		cmocka_unit_test_setup_teardown(purge, _setup, _teardown),
	};
	int c;

	while ((c = isc_commandline_parse(argc, argv, "v")) != -1) {
		switch (c) {
		case 'v':
			verbose = true;
			break;
		default:
			break;
		}
	}
595

Evan Hunt's avatar
Evan Hunt committed
596
	return (cmocka_run_group_tests(tests, NULL, NULL));
Petr Menšík's avatar
Petr Menšík committed
597
598
599
600
601
602
603
604
#else
	UNUSED(argc);
	UNUSED(argv);
	UNUSED(verbose);

	printf("1..0 # Skipped: timer test requires threads\n");
	return (0);
#endif
605
}
Evan Hunt's avatar
Evan Hunt committed
606
607
608
609
610
611
612
613
614
615
616
617

#else /* HAVE_CMOCKA */

#include <stdio.h>

int
main(void) {
	printf("1..0 # Skipped: cmocka not available\n");
	return (0);
}

#endif