xref: /illumos-gate/usr/src/test/libc-tests/tests/clocklock/clock_lock_sem.c (revision 90c28423f62864b8d977369710d815fd4227b5f0)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * semaphore-specific tests
18  */
19 
20 
21 #include <err.h>
22 #include <stdlib.h>
23 #include <semaphore.h>
24 #include <sys/debug.h>
25 #include <sys/sysmacros.h>
26 #include <stdbool.h>
27 #include <errno.h>
28 #include <string.h>
29 
30 #include "clock_lock.h"
31 
32 static void
clock_sem_create(const char * desc,void ** argp)33 clock_sem_create(const char *desc, void **argp)
34 {
35 	int ret;
36 	sem_t *sem;
37 
38 	sem = calloc(1, sizeof (sem_t));
39 	if (sem == NULL) {
40 		err(EXIT_FAILURE, "TEST FAILED: %s: failed to allocate memory "
41 		    "for a semaphore", desc);
42 	}
43 
44 	if ((ret = sem_init(sem, 0, 1)) != 0) {
45 		errc(EXIT_FAILURE, ret, "TEST FAILED: %s: failed to create "
46 		    "semaphore", desc);
47 	}
48 
49 	*argp = sem;
50 }
51 
52 static void
clock_sem_destroy(void * arg)53 clock_sem_destroy(void *arg)
54 {
55 	VERIFY0(sem_destroy(arg));
56 	free(arg);
57 }
58 
59 static void
clock_sem_lock(void * arg)60 clock_sem_lock(void *arg)
61 {
62 	VERIFY0(sem_trywait(arg));
63 }
64 
65 static void
clock_sem_unlock(void * arg)66 clock_sem_unlock(void *arg)
67 {
68 	VERIFY0(sem_post(arg));
69 }
70 
71 const lock_ops_t clock_lock_sem_ops = {
72 	.lo_create = clock_sem_create,
73 	.lo_destroy = clock_sem_destroy,
74 	.lo_lock = clock_sem_lock,
75 	.lo_unlock = clock_sem_unlock
76 };
77 
78 static bool
clock_test_sem_invalid_source(const clock_test_t * test,void * prim)79 clock_test_sem_invalid_source(const clock_test_t *test, void *prim)
80 {
81 	bool ret = true;
82 	sem_t *sem = prim;
83 	const clockid_t clocks[] = { 0x7777, INT32_MAX, 0x23, CLOCK_VIRTUAL,
84 	    CLOCK_THREAD_CPUTIME_ID, CLOCK_PROCESS_CPUTIME_ID };
85 
86 	for (size_t i = 0; i < ARRAY_SIZE(clocks); i++) {
87 		clockid_t c = clocks[i];
88 
89 		if (sem_clockwait(sem, c, &clock_to_100ms) != -1 ||
90 		    errno != EINVAL) {
91 			warnx("TEST FAILED: %s: sem_clockwait with clock 0x%x "
92 			    "returned %s, not EINVAL", test->ct_desc, c,
93 			    strerrorname_np(errno));
94 			ret = false;
95 		}
96 
97 		if (sem_relclockwait_np(sem, c, &clock_to_100ms) !=
98 		    -1 || errno != EINVAL) {
99 			warnx("TEST FAILED: %s: sem_relclockwait_np with clock "
100 			    "0x%x returned %s, not EINVAL", test->ct_desc, c,
101 			    strerrorname_np(errno));
102 			ret = false;
103 		}
104 	}
105 
106 	return (ret);
107 }
108 
109 static bool
clock_test_sem_inv_to_ign_abs(const clock_test_t * test,void * prim)110 clock_test_sem_inv_to_ign_abs(const clock_test_t *test, void *prim)
111 {
112 	bool ret = true;
113 	sem_t *sem = prim;
114 
115 	if (sem_timedwait(sem, &clock_to_invns) != 0) {
116 		warnx("TEST FAILED: %s: sem_timedwait failed with an invalid "
117 		    "timeout when the lock when lock was available: expected "
118 		    "success, found %s", test->ct_desc, strerrorname_np(errno));
119 		ret = false;
120 	} else {
121 		test->ct_ops->lo_unlock(sem);
122 	}
123 
124 	if (sem_clockwait(sem, CLOCK_MONOTONIC, &clock_to_invns) != 0) {
125 		warnx("TEST FAILED: %s: sem_clockwait failed with an invalid "
126 		    "timeout when the lock when lock was available: expected "
127 		    "success, found %s", test->ct_desc, strerrorname_np(errno));
128 		ret = false;
129 	} else {
130 		test->ct_ops->lo_unlock(sem);
131 	}
132 
133 	return (ret);
134 }
135 
136 static bool
clock_test_sem_inv_to_abs(const clock_test_t * test,void * prim)137 clock_test_sem_inv_to_abs(const clock_test_t *test, void *prim)
138 {
139 	bool ret = true;
140 	sem_t *sem = prim;
141 
142 	if (sem_timedwait(sem, &clock_to_invns) != -1 || errno != EINVAL) {
143 		warnx("TEST FAILED: %s: sem_timedwait with invalid timeout "
144 		    "returned %s, not EINVAL", test->ct_desc,
145 		    strerrorname_np(errno));
146 		ret = false;
147 	}
148 
149 	if (sem_clockwait(sem, CLOCK_MONOTONIC, &clock_to_invns) != -1 ||
150 	    errno != EINVAL) {
151 		warnx("TEST FAILED: %s: sem_clockwait with invalid timeout "
152 		    "returned %s, not EINVAL", test->ct_desc,
153 		    strerrorname_np(errno));
154 		ret = false;
155 	}
156 
157 	return (ret);
158 }
159 
160 static bool
clock_test_sem_inv_to_ign_rel(const clock_test_t * test,void * prim)161 clock_test_sem_inv_to_ign_rel(const clock_test_t *test, void *prim)
162 {
163 	bool ret = true;
164 	sem_t *sem = prim;
165 	const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs,
166 	    &clock_to_invnegns };
167 	const char *descs[] = { "too many nanoseconds", "negative seconds",
168 	    "negative nanoseconds" };
169 
170 	for (size_t i = 0; i < ARRAY_SIZE(specs); i++) {
171 		if (sem_reltimedwait_np(sem, specs[i]) != 0) {
172 			warnx("TEST FAILED: %s: sem_reltimedwait_np "
173 			    "failed with invalid timeout %s when the lock when "
174 			    "lock was available: expected success, found %s",
175 			    test->ct_desc, descs[i], strerrorname_np(errno));
176 			ret = false;
177 		} else {
178 			test->ct_ops->lo_unlock(sem);
179 		}
180 
181 		if (sem_relclockwait_np(sem, CLOCK_MONOTONIC, specs[i]) != 0) {
182 			warnx("TEST FAILED: %s: sem_relclockwait_np "
183 			    "failed with invalid timeout %s when the lock when "
184 			    "lock was available: expected success, found %s",
185 			    test->ct_desc, descs[i], strerrorname_np(errno));
186 			ret = false;
187 		} else {
188 			test->ct_ops->lo_unlock(sem);
189 		}
190 	}
191 
192 	return (ret);
193 }
194 
195 static bool
clock_test_sem_inv_to_rel(const clock_test_t * test,void * prim)196 clock_test_sem_inv_to_rel(const clock_test_t *test, void *prim)
197 {
198 	bool ret = true;
199 	sem_t *sem = prim;
200 	const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs,
201 	    &clock_to_invnegns };
202 	const char *descs[] = { "too many nanoseconds", "negative seconds",
203 	    "negative nanoseconds" };
204 
205 	for (size_t i = 0; i < ARRAY_SIZE(specs); i++) {
206 		if (sem_reltimedwait_np(sem, specs[i]) != -1 ||
207 		    errno != EINVAL) {
208 			warnx("TEST FAILED: %s: sem_reltimedwait_np "
209 			    "with invalid timeout %s returned %s, not EINVAL",
210 			    test->ct_desc, descs[i], strerrorname_np(errno));
211 			ret = false;
212 		}
213 
214 		if (sem_relclockwait_np(sem, CLOCK_MONOTONIC, specs[i]) != -1 ||
215 		    errno != EINVAL) {
216 			warnx("TEST FAILED: %s: sem_relclockwait_np "
217 			    "with invalid timeout %s returned %s, not EINVAL",
218 			    test->ct_desc, descs[i], strerrorname_np(errno));
219 			ret = false;
220 		}
221 	}
222 
223 	return (ret);
224 }
225 
226 static bool
clock_test_sem_to_abs(const clock_test_t * test,void * prim)227 clock_test_sem_to_abs(const clock_test_t *test, void *prim)
228 {
229 	sem_t *sem = prim;
230 	struct timespec to;
231 	int p;
232 	bool ret = true, elapse;
233 
234 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
235 	p = sem_timedwait(sem, &to);
236 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
237 	if (p != -1 && errno != ETIMEDOUT) {
238 		warnx("TEST FAILED: %s sem_timedwait on locked semaphore "
239 		    "returned %s, not ETIMEDOUT", test->ct_desc,
240 		    strerrorname_np(errno));
241 		ret = false;
242 	}
243 	if (!elapse) {
244 		warnx("TEST FAILED: %s: sem_timedwait on locked semaphore "
245 		    "did not block long enough!", test->ct_desc);
246 		ret = false;
247 	}
248 
249 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
250 	p = sem_clockwait(sem, CLOCK_REALTIME, &to);
251 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
252 	if (p != -1 && errno != ETIMEDOUT) {
253 		warnx("TEST FAILED: %s: sem_clockwait on locked semaphore "
254 		    "with CLOCK_REALTIME returned %s, not ETIMEDOUT",
255 		    test->ct_desc, strerrorname_np(errno));
256 		ret = false;
257 	}
258 	if (!elapse) {
259 		warnx("TEST FAILED: %s: sem_clockwait on locked semaphore "
260 		    "with CLOCK_REALTIME did not block long enough!",
261 		    test->ct_desc);
262 		ret = false;
263 	}
264 
265 	clock_rel_to_abs(CLOCK_HIGHRES, &clock_to_100ms, &to);
266 	p = sem_clockwait(sem, CLOCK_HIGHRES, &to);
267 	elapse = clock_abs_after(CLOCK_HIGHRES, &to);
268 	if (p != -1 && errno != ETIMEDOUT) {
269 		warnx("TEST FAILED: %s: sem_clockwait on locked semaphore "
270 		    "with CLOCK_HIGHRES returned %s, not ETIMEDOUT",
271 		    test->ct_desc, strerrorname_np(errno));
272 		ret = false;
273 	}
274 	if (!elapse) {
275 		warnx("TEST FAILED: %s: sem_clockwait on locked semaphore "
276 		    "with CLOCK_HIGHRES did not block long enough!",
277 		    test->ct_desc);
278 		ret = false;
279 	}
280 
281 	return (ret);
282 }
283 
284 static bool
clock_test_sem_to_rel(const clock_test_t * test,void * prim)285 clock_test_sem_to_rel(const clock_test_t *test, void *prim)
286 {
287 	sem_t *sem = prim;
288 	struct timespec start;
289 	int p;
290 	bool ret = true, elapse;
291 
292 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
293 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
294 	}
295 	p = sem_reltimedwait_np(sem, &clock_to_100ms);
296 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
297 	if (p != -1 && errno != ETIMEDOUT) {
298 		warnx("TEST FAILED: %s: sem_reltimedwait_np on locked "
299 		    "sempahore returned %s, not ETIMEDOUT", test->ct_desc,
300 		    strerrorname_np(errno));
301 		ret = false;
302 	}
303 	if (!elapse) {
304 		warnx("TEST FAILED: %s: sem_reltimedwait_np on locked "
305 		    "sempahore did not block long enough!", test->ct_desc);
306 		ret = false;
307 	}
308 
309 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
310 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
311 	}
312 	p = sem_relclockwait_np(sem, CLOCK_REALTIME,
313 	    &clock_to_100ms);
314 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
315 	if (p != -1 && errno != ETIMEDOUT) {
316 		warnx("TEST FAILED: %s: sem_relclockwait_np on locked "
317 		    "semaphore with CLOCK_REALTIME returned %s, not ETIMEDOUT",
318 		    test->ct_desc, strerrorname_np(errno));
319 		ret = false;
320 	}
321 	if (!elapse) {
322 		warnx("TEST FAILED: %s: sem_relclockwait_np on locked "
323 		    "sempahore with CLOCK_REALTIME did not block long enough!",
324 		    test->ct_desc);
325 		ret = false;
326 	}
327 
328 	if (clock_gettime(CLOCK_HIGHRES, &start) != 0) {
329 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_HIGHRES);
330 	}
331 	p = sem_relclockwait_np(sem, CLOCK_HIGHRES,
332 	    &clock_to_100ms);
333 	elapse = clock_rel_after(CLOCK_HIGHRES, &start, &clock_to_100ms);
334 	if (p != -1 && errno != ETIMEDOUT) {
335 		warnx("TEST FAILED: %s: sem_relclockwait_np on locked "
336 		    "semaphore with CLOCK_HIGHRES returned %s, not ETIMEDOUT",
337 		    test->ct_desc, strerrorname_np(errno));
338 		ret = false;
339 	}
340 	if (!elapse) {
341 		warnx("TEST FAILED: %s: sem_relclockwait_np on locked "
342 		    "semaphore with CLOCK_HIGHRES did not block long enough!",
343 		    test->ct_desc);
344 		ret = false;
345 	}
346 
347 	return (ret);
348 }
349 
350 const clock_test_t clock_sem_tests[] = { {
351 	.ct_desc = "sem: invalid and unsupported clock sources",
352 	.ct_ops = &clock_lock_sem_ops,
353 	.ct_test = clock_test_sem_invalid_source
354 }, {
355 	.ct_desc = "sem: invalid timeout works if lock available (absolute)",
356 	.ct_ops = &clock_lock_sem_ops,
357 	.ct_test = clock_test_sem_inv_to_ign_abs
358 }, {
359 	.ct_desc = "sem: invalid timeout works if lock available (relative)",
360 	.ct_ops = &clock_lock_sem_ops,
361 	.ct_test = clock_test_sem_inv_to_ign_rel
362 }, {
363 	.ct_desc = "sem: invalid timeout fails if lock taken (absolute)",
364 	.ct_ops = &clock_lock_sem_ops,
365 	.ct_test = clock_test_sem_inv_to_abs,
366 	.ct_enter = true
367 }, {
368 	.ct_desc = "sem: invalid timeout fails if lock taken (relative)",
369 	.ct_ops = &clock_lock_sem_ops,
370 	.ct_test = clock_test_sem_inv_to_rel,
371 	.ct_enter = true
372 }, {
373 	.ct_desc = "sem: timeout fires correctly (absolute)",
374 	.ct_ops = &clock_lock_sem_ops,
375 	.ct_test = clock_test_sem_to_abs,
376 	.ct_enter = true
377 }, {
378 	.ct_desc = "sem: timeout fires correctly (relative)",
379 	.ct_ops = &clock_lock_sem_ops,
380 	.ct_test = clock_test_sem_to_rel,
381 	.ct_enter = true
382 } };
383 
384 size_t clock_sem_ntests = ARRAY_SIZE(clock_sem_tests);
385