summaryrefslogtreecommitdiff
path: root/regress/sys/kern/clock_gettime/monotonicrelapse.c
blob: 8a04d4a764f145b467dbabf44782c16f6083fb82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* $OpenBSD: monotonicrelapse.c,v 1.2 2019/06/17 08:19:30 anton Exp $ */
/*
 * Scott Cheloha <scottcheloha@gmail.com>, 2019.  Public Domain.
 */

#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/time.h>

#include <err.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

void report_relapse(int, struct timespec *, struct timespec *);
void *thread_func(void *);
void thread_spawn(pthread_t **, int, void *(*)(void *));

/*
 * Is the active timecounter monotonic?
 *
 * Spawn the given number of threads and measure the monotonic clock
 * across nanosleep(2) calls.
 *
 * Threads have a tendency to wake up on new CPUs.  If the active
 * timecounter is not synchronized across CPUs this will be detected
 * relatively quickly and the test will fail.
 */

int
main(int argc, char *argv[])
{
	const char *errstr;
	pthread_t *thread;
	int error, i, nthreads;

	if (argc != 2) {
		fprintf(stderr, "usage: %s nthreads\n", getprogname());
		return 1;
	}
	nthreads = strtonum(argv[1], 1, INT_MAX, &errstr);
	if (errstr != NULL)
		errx(1, "nthreads is %s: %s", errstr, argv[1]);

	thread = calloc(nthreads, sizeof(*thread));
	if (thread == NULL)
		err(1, NULL);

	for (i = 0; i < nthreads; i++) {
		error = pthread_create(&thread[i], NULL, thread_func,
		    (void *)((uintptr_t)i + 1));
		if (error)
			errc(1, error, "pthread_create");
	}

	sleep(10);

	return 0;
}

void
report_relapse(int num, struct timespec *before, struct timespec *after)
{
	static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER;
	struct timespec relapsed;
	int error;

	error = pthread_mutex_lock(&report_mutex);
	if (error)
		errc(1, error, "T%d: pthread_mutex_lock", num);

	timespecsub(before, after, &relapsed);
	errx(1, "T%d: monotonic clock relapsed %.9f seconds: %.9f -> %.9f",
	    num, relapsed.tv_sec + relapsed.tv_nsec / 1000000000.0,
	    before->tv_sec + before->tv_nsec / 1000000000.0,
	    after->tv_sec + after->tv_nsec / 1000000000.0);
}

void *
thread_func(void *arg)
{
	struct timespec after, before, timeout;
	int num;

	timeout.tv_sec = 0;
	timeout.tv_nsec = 1;
	num = (int)arg;

	for (;;) {
		clock_gettime(CLOCK_MONOTONIC, &before);
		if (nanosleep(&timeout, NULL) == -1)
			err(1, "T%d: nanosleep", num);
		clock_gettime(CLOCK_MONOTONIC, &after);
		if (timespeccmp(&after, &before, <))
			report_relapse(num, &before, &after);
	}

	return NULL;
}