xref: /illumos-gate/usr/src/test/libc-tests/tests/clocklock/clock_lock.c (revision 8b6b46dcb073dba71917d6a7309f0df7bad798a2)
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