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