xref: /openbsd-src/regress/sys/kern/clock_gettime/monotonicrelapse.c (revision 49a6e16f2c2c8e509184b1f777366d1a6f337e1c)
1 /* $OpenBSD: monotonicrelapse.c,v 1.3 2021/12/13 16:56:49 deraadt Exp $ */
2 /*
3  * Scott Cheloha <scottcheloha@gmail.com>, 2019.  Public Domain.
4  */
5 
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8 #include <sys/time.h>
9 
10 #include <err.h>
11 #include <pthread.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <time.h>
16 #include <unistd.h>
17 
18 void report_relapse(int, struct timespec *, struct timespec *);
19 void *thread_func(void *);
20 void thread_spawn(pthread_t **, int, void *(*)(void *));
21 
22 /*
23  * Is the active timecounter monotonic?
24  *
25  * Spawn the given number of threads and measure the monotonic clock
26  * across nanosleep(2) calls.
27  *
28  * Threads have a tendency to wake up on new CPUs.  If the active
29  * timecounter is not synchronized across CPUs this will be detected
30  * relatively quickly and the test will fail.
31  */
32 
33 int
main(int argc,char * argv[])34 main(int argc, char *argv[])
35 {
36 	const char *errstr;
37 	pthread_t *thread;
38 	int error, i, nthreads;
39 
40 	if (argc != 2) {
41 		fprintf(stderr, "usage: %s nthreads\n", getprogname());
42 		return 1;
43 	}
44 	nthreads = strtonum(argv[1], 1, INT_MAX, &errstr);
45 	if (errstr != NULL)
46 		errx(1, "nthreads is %s: %s", errstr, argv[1]);
47 
48 	thread = calloc(nthreads, sizeof(*thread));
49 	if (thread == NULL)
50 		err(1, NULL);
51 
52 	for (i = 0; i < nthreads; i++) {
53 		error = pthread_create(&thread[i], NULL, thread_func,
54 		    (void *)((uintptr_t)i + 1));
55 		if (error)
56 			errc(1, error, "pthread_create");
57 	}
58 
59 	sleep(10);
60 
61 	return 0;
62 }
63 
64 void
report_relapse(int num,struct timespec * before,struct timespec * after)65 report_relapse(int num, struct timespec *before, struct timespec *after)
66 {
67 	static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER;
68 	struct timespec relapsed;
69 	int error;
70 
71 	error = pthread_mutex_lock(&report_mutex);
72 	if (error)
73 		errc(1, error, "T%d: pthread_mutex_lock", num);
74 
75 	timespecsub(before, after, &relapsed);
76 	errx(1, "T%d: monotonic clock relapsed %.9f seconds: %.9f -> %.9f",
77 	    num, relapsed.tv_sec + relapsed.tv_nsec / 1000000000.0,
78 	    before->tv_sec + before->tv_nsec / 1000000000.0,
79 	    after->tv_sec + after->tv_nsec / 1000000000.0);
80 }
81 
82 void *
thread_func(void * arg)83 thread_func(void *arg)
84 {
85 	struct timespec after, before, timeout;
86 	int num;
87 
88 	timeout.tv_sec = 0;
89 	timeout.tv_nsec = 1;
90 	num = (int)arg;
91 
92 	for (;;) {
93 		clock_gettime(CLOCK_MONOTONIC, &before);
94 		if (nanosleep(&timeout, NULL) == -1)
95 			err(1, "T%d: nanosleep", num);
96 		clock_gettime(CLOCK_MONOTONIC, &after);
97 		if (timespeccmp(&after, &before, <))
98 			report_relapse(num, &before, &after);
99 	}
100 
101 	return NULL;
102 }
103