1*8b6b46dcSRobert Mustacchi /*
2*8b6b46dcSRobert Mustacchi * This file and its contents are supplied under the terms of the
3*8b6b46dcSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*8b6b46dcSRobert Mustacchi * You may only use this file in accordance with the terms of version
5*8b6b46dcSRobert Mustacchi * 1.0 of the CDDL.
6*8b6b46dcSRobert Mustacchi *
7*8b6b46dcSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*8b6b46dcSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*8b6b46dcSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*8b6b46dcSRobert Mustacchi */
11*8b6b46dcSRobert Mustacchi
12*8b6b46dcSRobert Mustacchi /*
13*8b6b46dcSRobert Mustacchi * Copyright 2024 Oxide Computer Company
14*8b6b46dcSRobert Mustacchi */
15*8b6b46dcSRobert Mustacchi
16*8b6b46dcSRobert Mustacchi /*
17*8b6b46dcSRobert Mustacchi * Test the various pthreads related clock based locking routines. These all
18*8b6b46dcSRobert Mustacchi * attempt to take some form of lock and utilize a timeout that can be specified
19*8b6b46dcSRobert Mustacchi * in terms of a given clock (i.e. CLOCK_REALTIME and CLOCK_HIGHRES). In
20*8b6b46dcSRobert Mustacchi * particular we want to cover:
21*8b6b46dcSRobert Mustacchi *
22*8b6b46dcSRobert Mustacchi * - Invalid clock sources
23*8b6b46dcSRobert Mustacchi * - Invalid timeouts ignored when acquired
24*8b6b46dcSRobert Mustacchi * - Invalid timeouts caught when used
25*8b6b46dcSRobert Mustacchi * - We can successfully get an ETIMEDOUT and that time has advanced at least
26*8b6b46dcSRobert Mustacchi * that much
27*8b6b46dcSRobert Mustacchi */
28*8b6b46dcSRobert Mustacchi
29*8b6b46dcSRobert Mustacchi #include <err.h>
30*8b6b46dcSRobert Mustacchi #include <stdlib.h>
31*8b6b46dcSRobert Mustacchi #include <pthread.h>
32*8b6b46dcSRobert Mustacchi #include <sys/debug.h>
33*8b6b46dcSRobert Mustacchi #include <sys/sysmacros.h>
34*8b6b46dcSRobert Mustacchi #include <stdbool.h>
35*8b6b46dcSRobert Mustacchi #include <errno.h>
36*8b6b46dcSRobert Mustacchi #include <string.h>
37*8b6b46dcSRobert Mustacchi
38*8b6b46dcSRobert Mustacchi #include "clock_lock.h"
39*8b6b46dcSRobert Mustacchi
40*8b6b46dcSRobert Mustacchi /*
41*8b6b46dcSRobert Mustacchi * This is a generic 100ms timeout that we can use. We use it for some tests
42*8b6b46dcSRobert Mustacchi * that require an absolute timeout that is in the future but we don't want to
43*8b6b46dcSRobert Mustacchi * bother computing.
44*8b6b46dcSRobert Mustacchi */
45*8b6b46dcSRobert Mustacchi const struct timespec clock_to_100ms = { 0, MSEC2NSEC(100) };
46*8b6b46dcSRobert Mustacchi
47*8b6b46dcSRobert Mustacchi /*
48*8b6b46dcSRobert Mustacchi * A series of invalid clocks. The first is usable for both relative and
49*8b6b46dcSRobert Mustacchi * absolute operations. The others which use negative times should only fail for
50*8b6b46dcSRobert Mustacchi * the relative operations at this time.
51*8b6b46dcSRobert Mustacchi */
52*8b6b46dcSRobert Mustacchi const struct timespec clock_to_invns = { 0, NANOSEC * 2 };
53*8b6b46dcSRobert Mustacchi const struct timespec clock_to_invnegs = { -12345, 0 };
54*8b6b46dcSRobert Mustacchi const struct timespec clock_to_invnegns = { 100, -0x23 };
55*8b6b46dcSRobert Mustacchi
56*8b6b46dcSRobert Mustacchi void
clock_rel_to_abs(clockid_t clock,const struct timespec * restrict rel,struct timespec * restrict abs)57*8b6b46dcSRobert Mustacchi clock_rel_to_abs(clockid_t clock, const struct timespec *restrict rel,
58*8b6b46dcSRobert Mustacchi struct timespec *restrict abs)
59*8b6b46dcSRobert Mustacchi {
60*8b6b46dcSRobert Mustacchi if (clock_gettime(clock, abs) != 0) {
61*8b6b46dcSRobert Mustacchi err(EXIT_FAILURE, "failed to get absolute time for clock %d",
62*8b6b46dcSRobert Mustacchi clock);
63*8b6b46dcSRobert Mustacchi }
64*8b6b46dcSRobert Mustacchi
65*8b6b46dcSRobert Mustacchi abs->tv_nsec += rel->tv_nsec;
66*8b6b46dcSRobert Mustacchi abs->tv_sec += rel->tv_sec;
67*8b6b46dcSRobert Mustacchi if (abs->tv_nsec > NANOSEC) {
68*8b6b46dcSRobert Mustacchi abs->tv_sec += abs->tv_nsec / NANOSEC;
69*8b6b46dcSRobert Mustacchi abs->tv_nsec %= NANOSEC;
70*8b6b46dcSRobert Mustacchi }
71*8b6b46dcSRobert Mustacchi }
72*8b6b46dcSRobert Mustacchi
73*8b6b46dcSRobert Mustacchi bool
clock_abs_after(clockid_t clock,const struct timespec * to)74*8b6b46dcSRobert Mustacchi clock_abs_after(clockid_t clock, const struct timespec *to)
75*8b6b46dcSRobert Mustacchi {
76*8b6b46dcSRobert Mustacchi struct timespec now;
77*8b6b46dcSRobert Mustacchi
78*8b6b46dcSRobert Mustacchi if (clock_gettime(clock, &now) != 0) {
79*8b6b46dcSRobert Mustacchi err(EXIT_FAILURE, "failed to get absolute time for clock %d",
80*8b6b46dcSRobert Mustacchi clock);
81*8b6b46dcSRobert Mustacchi }
82*8b6b46dcSRobert Mustacchi
83*8b6b46dcSRobert Mustacchi if (now.tv_sec > to->tv_sec)
84*8b6b46dcSRobert Mustacchi return (true);
85*8b6b46dcSRobert Mustacchi
86*8b6b46dcSRobert Mustacchi return (now.tv_sec == to->tv_sec && now.tv_nsec > to->tv_nsec);
87*8b6b46dcSRobert Mustacchi }
88*8b6b46dcSRobert Mustacchi
89*8b6b46dcSRobert Mustacchi bool
clock_rel_after(clockid_t clock,const struct timespec * start,const struct timespec * to)90*8b6b46dcSRobert Mustacchi clock_rel_after(clockid_t clock, const struct timespec *start,
91*8b6b46dcSRobert Mustacchi const struct timespec *to)
92*8b6b46dcSRobert Mustacchi {
93*8b6b46dcSRobert Mustacchi struct timespec now, absto;
94*8b6b46dcSRobert Mustacchi
95*8b6b46dcSRobert Mustacchi if (clock_gettime(clock, &now) != 0) {
96*8b6b46dcSRobert Mustacchi err(EXIT_FAILURE, "failed to get absolute time for clock %d",
97*8b6b46dcSRobert Mustacchi clock);
98*8b6b46dcSRobert Mustacchi }
99*8b6b46dcSRobert Mustacchi
100*8b6b46dcSRobert Mustacchi absto.tv_nsec = start->tv_nsec + to->tv_nsec;
101*8b6b46dcSRobert Mustacchi absto.tv_sec = start->tv_sec + to->tv_sec;
102*8b6b46dcSRobert Mustacchi if (absto.tv_nsec > NANOSEC) {
103*8b6b46dcSRobert Mustacchi absto.tv_sec += absto.tv_nsec / NANOSEC;
104*8b6b46dcSRobert Mustacchi absto.tv_nsec %= NANOSEC;
105*8b6b46dcSRobert Mustacchi }
106*8b6b46dcSRobert Mustacchi
107*8b6b46dcSRobert Mustacchi if (now.tv_sec > absto.tv_sec)
108*8b6b46dcSRobert Mustacchi return (true);
109*8b6b46dcSRobert Mustacchi
110*8b6b46dcSRobert Mustacchi return (now.tv_sec == absto.tv_sec && now.tv_nsec > absto.tv_nsec);
111*8b6b46dcSRobert Mustacchi }
112*8b6b46dcSRobert Mustacchi
113*8b6b46dcSRobert Mustacchi typedef struct {
114*8b6b46dcSRobert Mustacchi const clock_test_t *cthr_test;
115*8b6b46dcSRobert Mustacchi void *cthr_prim;
116*8b6b46dcSRobert Mustacchi bool cthr_ret;
117*8b6b46dcSRobert Mustacchi } clock_test_thr_t;
118*8b6b46dcSRobert Mustacchi
119*8b6b46dcSRobert Mustacchi static void *
clock_test_thr(void * arg)120*8b6b46dcSRobert Mustacchi clock_test_thr(void *arg)
121*8b6b46dcSRobert Mustacchi {
122*8b6b46dcSRobert Mustacchi clock_test_thr_t *thr = arg;
123*8b6b46dcSRobert Mustacchi thr->cthr_ret = thr->cthr_test->ct_test(thr->cthr_test,
124*8b6b46dcSRobert Mustacchi thr->cthr_prim);
125*8b6b46dcSRobert Mustacchi return (NULL);
126*8b6b46dcSRobert Mustacchi }
127*8b6b46dcSRobert Mustacchi
128*8b6b46dcSRobert Mustacchi static bool
clock_test_one(const clock_test_t * test)129*8b6b46dcSRobert Mustacchi clock_test_one(const clock_test_t *test)
130*8b6b46dcSRobert Mustacchi {
131*8b6b46dcSRobert Mustacchi void *prim;
132*8b6b46dcSRobert Mustacchi bool ret;
133*8b6b46dcSRobert Mustacchi
134*8b6b46dcSRobert Mustacchi test->ct_ops->lo_create(test->ct_desc, &prim);
135*8b6b46dcSRobert Mustacchi
136*8b6b46dcSRobert Mustacchi /*
137*8b6b46dcSRobert Mustacchi * If the test requires that the lock be held in some way, then we spawn
138*8b6b46dcSRobert Mustacchi * the test to run in another thread to avoid any issues with recursive
139*8b6b46dcSRobert Mustacchi * actions. Otherwise we let it run locally.
140*8b6b46dcSRobert Mustacchi */
141*8b6b46dcSRobert Mustacchi if (test->ct_enter) {
142*8b6b46dcSRobert Mustacchi clock_test_thr_t thr_test;
143*8b6b46dcSRobert Mustacchi pthread_t thr;
144*8b6b46dcSRobert Mustacchi int pret;
145*8b6b46dcSRobert Mustacchi
146*8b6b46dcSRobert Mustacchi test->ct_ops->lo_lock(prim);
147*8b6b46dcSRobert Mustacchi thr_test.cthr_test = test;
148*8b6b46dcSRobert Mustacchi thr_test.cthr_prim = prim;
149*8b6b46dcSRobert Mustacchi thr_test.cthr_ret = false;
150*8b6b46dcSRobert Mustacchi
151*8b6b46dcSRobert Mustacchi if ((pret = pthread_create(&thr, NULL, clock_test_thr,
152*8b6b46dcSRobert Mustacchi &thr_test)) != 0) {
153*8b6b46dcSRobert Mustacchi errc(EXIT_FAILURE, pret, "TEST FAILED: %s: internal "
154*8b6b46dcSRobert Mustacchi "error creating test thread", test->ct_desc);
155*8b6b46dcSRobert Mustacchi }
156*8b6b46dcSRobert Mustacchi
157*8b6b46dcSRobert Mustacchi if ((pret = pthread_join(thr, NULL)) != 0) {
158*8b6b46dcSRobert Mustacchi errc(EXIT_FAILURE, pret, "TEST FAILED: %s: internal "
159*8b6b46dcSRobert Mustacchi "error joining test thread", test->ct_desc);
160*8b6b46dcSRobert Mustacchi }
161*8b6b46dcSRobert Mustacchi ret = thr_test.cthr_ret;
162*8b6b46dcSRobert Mustacchi test->ct_ops->lo_unlock(prim);
163*8b6b46dcSRobert Mustacchi } else {
164*8b6b46dcSRobert Mustacchi ret = test->ct_test(test, prim);
165*8b6b46dcSRobert Mustacchi }
166*8b6b46dcSRobert Mustacchi
167*8b6b46dcSRobert Mustacchi test->ct_ops->lo_destroy(prim);
168*8b6b46dcSRobert Mustacchi
169*8b6b46dcSRobert Mustacchi if (ret) {
170*8b6b46dcSRobert Mustacchi (void) printf("TEST PASSED: %s\n", test->ct_desc);
171*8b6b46dcSRobert Mustacchi }
172*8b6b46dcSRobert Mustacchi
173*8b6b46dcSRobert Mustacchi return (ret);
174*8b6b46dcSRobert Mustacchi }
175*8b6b46dcSRobert Mustacchi
176*8b6b46dcSRobert Mustacchi int
main(void)177*8b6b46dcSRobert Mustacchi main(void)
178*8b6b46dcSRobert Mustacchi {
179*8b6b46dcSRobert Mustacchi int ret = EXIT_SUCCESS;
180*8b6b46dcSRobert Mustacchi
181*8b6b46dcSRobert Mustacchi for (size_t i = 0; i < clock_mutex_ntests; i++) {
182*8b6b46dcSRobert Mustacchi if (!clock_test_one(&clock_mutex_tests[i])) {
183*8b6b46dcSRobert Mustacchi ret = EXIT_FAILURE;
184*8b6b46dcSRobert Mustacchi }
185*8b6b46dcSRobert Mustacchi }
186*8b6b46dcSRobert Mustacchi
187*8b6b46dcSRobert Mustacchi for (size_t i = 0; i < clock_rwlock_ntests; i++) {
188*8b6b46dcSRobert Mustacchi if (!clock_test_one(&clock_rwlock_tests[i])) {
189*8b6b46dcSRobert Mustacchi ret = EXIT_FAILURE;
190*8b6b46dcSRobert Mustacchi }
191*8b6b46dcSRobert Mustacchi }
192*8b6b46dcSRobert Mustacchi
193*8b6b46dcSRobert Mustacchi for (size_t i = 0; i < clock_sem_ntests; i++) {
194*8b6b46dcSRobert Mustacchi if (!clock_test_one(&clock_sem_tests[i])) {
195*8b6b46dcSRobert Mustacchi ret = EXIT_FAILURE;
196*8b6b46dcSRobert Mustacchi }
197*8b6b46dcSRobert Mustacchi }
198*8b6b46dcSRobert Mustacchi
199*8b6b46dcSRobert Mustacchi for (size_t i = 0; i < clock_cond_ntests; i++) {
200*8b6b46dcSRobert Mustacchi if (!clock_test_one(&clock_cond_tests[i])) {
201*8b6b46dcSRobert Mustacchi ret = EXIT_FAILURE;
202*8b6b46dcSRobert Mustacchi }
203*8b6b46dcSRobert Mustacchi }
204*8b6b46dcSRobert Mustacchi
205*8b6b46dcSRobert Mustacchi
206*8b6b46dcSRobert Mustacchi return (ret);
207*8b6b46dcSRobert Mustacchi }
208