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;
}
|