xref: /illumos-gate/usr/src/test/libc-tests/tests/clocklock/clock_lock_rwlock.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  * rwlock-specific tests and implementation
18  */
19 
20 #include <err.h>
21 #include <stdlib.h>
22 #include <pthread.h>
23 #include <sys/debug.h>
24 #include <sys/sysmacros.h>
25 #include <stdbool.h>
26 #include <errno.h>
27 #include <string.h>
28 
29 #include "clock_lock.h"
30 
31 static void
clock_rwlock_create(const char * desc,void ** argp)32 clock_rwlock_create(const char *desc, void **argp)
33 {
34 	int ret;
35 	pthread_rwlock_t *rw;
36 
37 	rw = calloc(1, sizeof (pthread_rwlock_t));
38 	if (rw == NULL) {
39 		err(EXIT_FAILURE, "TEST FAILED: %s: failed to allocate memory "
40 		    "for a rwlock", desc);
41 	}
42 
43 	if ((ret = pthread_rwlock_init(rw, NULL)) != 0) {
44 		errc(EXIT_FAILURE, ret, "TEST FAILED: %s: failed to create "
45 		    "rwlock", desc);
46 	}
47 
48 	*argp = rw;
49 }
50 
51 static void
clock_rwlock_destroy(void * arg)52 clock_rwlock_destroy(void *arg)
53 {
54 	VERIFY0(pthread_rwlock_destroy(arg));
55 	free(arg);
56 }
57 
58 static void
clock_rwlock_wrlock(void * arg)59 clock_rwlock_wrlock(void *arg)
60 {
61 	VERIFY0(pthread_rwlock_trywrlock(arg));
62 }
63 
64 static void
clock_rwlock_unlock(void * arg)65 clock_rwlock_unlock(void *arg)
66 {
67 	VERIFY0(pthread_rwlock_unlock(arg));
68 }
69 
70 /*
71  * While we have both read and write locks, we use a write lock for lo_lock()
72  * here as that'll ensure that any additional readers or writers will always
73  * block.
74  */
75 const lock_ops_t clock_lock_rwlock_ops = {
76 	.lo_create = clock_rwlock_create,
77 	.lo_destroy = clock_rwlock_destroy,
78 	.lo_lock = clock_rwlock_wrlock,
79 	.lo_unlock = clock_rwlock_unlock
80 };
81 
82 static bool
clock_test_rwlock_invalid_source(const clock_test_t * test,void * prim)83 clock_test_rwlock_invalid_source(const clock_test_t *test, void *prim)
84 {
85 	bool ret = true;
86 	pthread_rwlock_t *rwl = prim;
87 	const clockid_t clocks[] = { 0x7777, INT32_MAX, 0x23, CLOCK_VIRTUAL,
88 	    CLOCK_THREAD_CPUTIME_ID, CLOCK_PROCESS_CPUTIME_ID };
89 	int p;
90 
91 	for (size_t i = 0; i < ARRAY_SIZE(clocks); i++) {
92 		clockid_t c = clocks[i];
93 
94 		if ((p = pthread_rwlock_clockrdlock(rwl, c, &clock_to_100ms)) !=
95 		    EINVAL) {
96 			warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock "
97 			    "with clock 0x%x returned %s, not EINVAL",
98 			    test->ct_desc, c, strerrorname_np(p));
99 			ret = false;
100 		}
101 
102 		if ((p = pthread_rwlock_relclockrdlock_np(rwl, c,
103 		    &clock_to_100ms)) != EINVAL) {
104 			warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock"
105 			    "_np with clock 0x%x returned %s, not EINVAL",
106 			    test->ct_desc, c, strerrorname_np(p));
107 			ret = false;
108 		}
109 
110 		if ((p = pthread_rwlock_clockwrlock(rwl, c, &clock_to_100ms)) !=
111 		    EINVAL) {
112 			warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock "
113 			    "with clock 0x%x returned %s, not EINVAL",
114 			    test->ct_desc, c, strerrorname_np(p));
115 			ret = false;
116 		}
117 
118 		if ((p = pthread_rwlock_relclockwrlock_np(rwl, c,
119 		    &clock_to_100ms)) != EINVAL) {
120 			warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock"
121 			    "_np with clock 0x%x returned %s, not EINVAL",
122 			    test->ct_desc, c, strerrorname_np(p));
123 			ret = false;
124 		}
125 
126 	}
127 
128 	return (ret);
129 }
130 
131 static bool
clock_test_rwlock_inv_to_ign_abs(const clock_test_t * test,void * prim)132 clock_test_rwlock_inv_to_ign_abs(const clock_test_t *test, void *prim)
133 {
134 	bool ret = true;
135 	pthread_rwlock_t *rwl = prim;
136 	int p;
137 
138 	if ((p = pthread_rwlock_timedrdlock(rwl, &clock_to_invns)) != 0) {
139 		warnx("TEST FAILED: %s: pthread_rwlock_timedrdlock failed with "
140 		    "an invalid timeout when the lock when lock was available: "
141 		    "expected success, found %s", test->ct_desc,
142 		    strerrorname_np(p));
143 		ret = false;
144 	} else {
145 		test->ct_ops->lo_unlock(rwl);
146 	}
147 
148 	if ((p = pthread_rwlock_clockrdlock(rwl, CLOCK_MONOTONIC,
149 	    &clock_to_invns)) != 0) {
150 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock failed with "
151 		    "an invalid timeout when the lock when lock was available: "
152 		    "expected success, found %s", test->ct_desc,
153 		    strerrorname_np(p));
154 		ret = false;
155 	} else {
156 		test->ct_ops->lo_unlock(rwl);
157 	}
158 
159 	if ((p = pthread_rwlock_timedwrlock(rwl, &clock_to_invns)) != 0) {
160 		warnx("TEST FAILED: %s: pthread_rwlock_timedwrlock failed with "
161 		    "an invalid timeout when the lock when lock was available: "
162 		    "expected success, found %s", test->ct_desc,
163 		    strerrorname_np(p));
164 		ret = false;
165 	} else {
166 		test->ct_ops->lo_unlock(rwl);
167 	}
168 
169 	if ((p = pthread_rwlock_clockwrlock(rwl, CLOCK_MONOTONIC,
170 	    &clock_to_invns)) != 0) {
171 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock failed with "
172 		    "an invalid timeout when the lock when lock was available: "
173 		    "expected success, found %s", test->ct_desc,
174 		    strerrorname_np(p));
175 		ret = false;
176 	} else {
177 		test->ct_ops->lo_unlock(rwl);
178 	}
179 
180 	return (ret);
181 }
182 
183 static bool
clock_test_rwlock_inv_to_abs(const clock_test_t * test,void * prim)184 clock_test_rwlock_inv_to_abs(const clock_test_t *test, void *prim)
185 {
186 	bool ret = true;
187 	pthread_rwlock_t *rwl = prim;
188 	int p;
189 
190 	if ((p = pthread_rwlock_timedrdlock(rwl, &clock_to_invns)) != EINVAL) {
191 		warnx("TEST FAILED: %s: pthread_rwlock_timedrdlock with "
192 		    "invalid timeout returned %s, not EINVAL", test->ct_desc,
193 		    strerrorname_np(p));
194 		ret = false;
195 	}
196 
197 	if ((p = pthread_rwlock_clockrdlock(rwl, CLOCK_MONOTONIC,
198 	    &clock_to_invns)) != EINVAL) {
199 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock with "
200 		    "invalid timeout returned %s, not EINVAL", test->ct_desc,
201 		    strerrorname_np(p));
202 		ret = false;
203 	}
204 
205 	if ((p = pthread_rwlock_timedwrlock(rwl, &clock_to_invns)) != EINVAL) {
206 		warnx("TEST FAILED: %s: pthread_rwlock_timedwrlock with "
207 		    "invalid timeout returned %s, not EINVAL", test->ct_desc,
208 		    strerrorname_np(p));
209 		ret = false;
210 	}
211 
212 	if ((p = pthread_rwlock_clockwrlock(rwl, CLOCK_MONOTONIC,
213 	    &clock_to_invns)) != EINVAL) {
214 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock with "
215 		    "invalid timeout returned %s, not EINVAL", test->ct_desc,
216 		    strerrorname_np(p));
217 		ret = false;
218 	}
219 
220 	return (ret);
221 }
222 
223 static bool
clock_test_rwlock_inv_to_ign_rel(const clock_test_t * test,void * prim)224 clock_test_rwlock_inv_to_ign_rel(const clock_test_t *test, void *prim)
225 {
226 	bool ret = true;
227 	pthread_rwlock_t *rwl = prim;
228 	const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs,
229 	    &clock_to_invnegns };
230 	const char *descs[] = { "too many nanoseconds", "negative seconds",
231 	    "negative nanoseconds" };
232 	int p;
233 
234 	for (size_t i = 0; i < ARRAY_SIZE(specs); i++) {
235 		if ((p = pthread_rwlock_reltimedrdlock_np(rwl, specs[i])) !=
236 		    0) {
237 			warnx("TEST FAILED: %s: pthread_rwlock_reltimedrdlock"
238 			    "_np failed with invalid timeout %s when the lock "
239 			    "when lock was available: expected success, found "
240 			    "%s", test->ct_desc, descs[i], strerrorname_np(p));
241 			ret = false;
242 		} else {
243 			test->ct_ops->lo_unlock(rwl);
244 		}
245 
246 		if ((p = pthread_rwlock_relclockrdlock_np(rwl, CLOCK_MONOTONIC,
247 		    specs[i])) != 0) {
248 			warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock"
249 			    "_np failed with invalid timeout %s when the lock "
250 			    "when lock was available: expected success, found "
251 			    "%s", test->ct_desc, descs[i], strerrorname_np(p));
252 			ret = false;
253 		} else {
254 			test->ct_ops->lo_unlock(rwl);
255 		}
256 
257 		if ((p = pthread_rwlock_reltimedwrlock_np(rwl, specs[i])) !=
258 		    0) {
259 			warnx("TEST FAILED: %s: pthread_rwlock_reltimedwrlock"
260 			    "_np failed with invalid timeout %s when the lock "
261 			    "when lock was available: expected success, found "
262 			    "%s", test->ct_desc, descs[i], strerrorname_np(p));
263 			ret = false;
264 		} else {
265 			test->ct_ops->lo_unlock(rwl);
266 		}
267 
268 		if ((p = pthread_rwlock_relclockwrlock_np(rwl, CLOCK_MONOTONIC,
269 		    specs[i])) != 0) {
270 			warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock"
271 			    "_np failed with invalid timeout %s when the lock "
272 			    "when lock was available: expected success, found "
273 			    "%s", test->ct_desc, descs[i], strerrorname_np(p));
274 			ret = false;
275 		} else {
276 			test->ct_ops->lo_unlock(rwl);
277 		}
278 	}
279 
280 	return (ret);
281 }
282 
283 static bool
clock_test_rwlock_inv_to_rel(const clock_test_t * test,void * prim)284 clock_test_rwlock_inv_to_rel(const clock_test_t *test, void *prim)
285 {
286 	bool ret = true;
287 	pthread_rwlock_t *rwl = prim;
288 	const struct timespec *specs[] = { &clock_to_invns, &clock_to_invnegs,
289 	    &clock_to_invnegns };
290 	const char *descs[] = { "too many nanoseconds", "negative seconds",
291 	    "negative nanoseconds" };
292 	int p;
293 
294 	for (size_t i = 0; i < ARRAY_SIZE(specs); i++) {
295 		if ((p = pthread_rwlock_reltimedrdlock_np(rwl, specs[i])) !=
296 		    EINVAL) {
297 			warnx("TEST FAILED: %s: pthread_rwlock_reltimedrdlock"
298 			    "_np with invalid timeout %s returned %s, not "
299 			    "EINVAL", test->ct_desc, descs[i],
300 			    strerrorname_np(p));
301 			ret = false;
302 		}
303 
304 		if ((p = pthread_rwlock_relclockrdlock_np(rwl, CLOCK_MONOTONIC,
305 		    specs[i])) != EINVAL) {
306 			warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock"
307 			    "_np with invalid timeout %s returned %s, not "
308 			    "EINVAL", test->ct_desc, descs[i],
309 			    strerrorname_np(p));
310 			ret = false;
311 		}
312 
313 		if ((p = pthread_rwlock_reltimedwrlock_np(rwl, specs[i])) !=
314 		    EINVAL) {
315 			warnx("TEST FAILED: %s: pthread_rwlock_reltimedwrlock"
316 			    "_np with invalid timeout %s returned %s, not "
317 			    "EINVAL", test->ct_desc, descs[i],
318 			    strerrorname_np(p));
319 			ret = false;
320 		}
321 
322 		if ((p = pthread_rwlock_relclockwrlock_np(rwl, CLOCK_MONOTONIC,
323 		    specs[i])) != EINVAL) {
324 			warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock"
325 			    "_np with invalid timeout %s returned %s, not "
326 			    "EINVAL", test->ct_desc, descs[i],
327 			    strerrorname_np(p));
328 			ret = false;
329 		}
330 	}
331 
332 	return (ret);
333 }
334 
335 static bool
clock_test_rwlock_to_abs(const clock_test_t * test,void * prim)336 clock_test_rwlock_to_abs(const clock_test_t *test, void *prim)
337 {
338 	pthread_rwlock_t *rwl = prim;
339 	struct timespec to;
340 	int p;
341 	bool ret = true, elapse;
342 
343 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
344 	p = pthread_rwlock_timedrdlock(rwl, &to);
345 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
346 	if (p != ETIMEDOUT) {
347 		warnx("TEST FAILED: %s pthread_rwlock_timedrdlock on locked "
348 		    "rwlock returned %s, not ETIMEDOUT", test->ct_desc,
349 		    strerrorname_np(p));
350 		ret = false;
351 	}
352 	if (!elapse) {
353 		warnx("TEST FAILED: %s: pthread_rwlock_timedrdlock on locked "
354 		    "rwlock did not block long enough!", test->ct_desc);
355 		ret = false;
356 	}
357 
358 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
359 	p = pthread_rwlock_clockrdlock(rwl, CLOCK_REALTIME, &to);
360 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
361 	if (p != ETIMEDOUT) {
362 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock on locked "
363 		    "rwlock with CLOCK_REALTIME returned %s, not ETIMEDOUT",
364 		    test->ct_desc, strerrorname_np(p));
365 		ret = false;
366 	}
367 	if (!elapse) {
368 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock on locked "
369 		    "rwlock with CLOCK_REALTIME did not block long enough!",
370 		    test->ct_desc);
371 		ret = false;
372 	}
373 
374 	clock_rel_to_abs(CLOCK_HIGHRES, &clock_to_100ms, &to);
375 	p = pthread_rwlock_clockrdlock(rwl, CLOCK_HIGHRES, &to);
376 	elapse = clock_abs_after(CLOCK_HIGHRES, &to);
377 	if (p != ETIMEDOUT) {
378 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock on locked "
379 		    "rwlock with CLOCK_HIGHRES returned %s, not ETIMEDOUT",
380 		    test->ct_desc, strerrorname_np(p));
381 		ret = false;
382 	}
383 	if (!elapse) {
384 		warnx("TEST FAILED: %s: pthread_rwlock_clockrdlock on locked "
385 		    "rwlock with CLOCK_HIGHRES did not block long enough!",
386 		    test->ct_desc);
387 		ret = false;
388 	}
389 
390 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
391 	p = pthread_rwlock_timedwrlock(rwl, &to);
392 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
393 	if (p != ETIMEDOUT) {
394 		warnx("TEST FAILED: %s pthread_rwlock_timedwrlock on locked "
395 		    "rwlock returned %s, not ETIMEDOUT", test->ct_desc,
396 		    strerrorname_np(p));
397 		ret = false;
398 	}
399 	if (!elapse) {
400 		warnx("TEST FAILED: %s: pthread_rwlock_timedwrlock on locked "
401 		    "rwlock did not block long enough!", test->ct_desc);
402 		ret = false;
403 	}
404 
405 	clock_rel_to_abs(CLOCK_REALTIME, &clock_to_100ms, &to);
406 	p = pthread_rwlock_clockwrlock(rwl, CLOCK_REALTIME, &to);
407 	elapse = clock_abs_after(CLOCK_REALTIME, &to);
408 	if (p != ETIMEDOUT) {
409 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock on locked "
410 		    "rwlock with CLOCK_REALTIME returned %s, not ETIMEDOUT",
411 		    test->ct_desc, strerrorname_np(p));
412 		ret = false;
413 	}
414 	if (!elapse) {
415 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock on locked "
416 		    "rwlock with CLOCK_REALTIME did not block long enough!",
417 		    test->ct_desc);
418 		ret = false;
419 	}
420 
421 	clock_rel_to_abs(CLOCK_HIGHRES, &clock_to_100ms, &to);
422 	p = pthread_rwlock_clockwrlock(rwl, CLOCK_HIGHRES, &to);
423 	elapse = clock_abs_after(CLOCK_HIGHRES, &to);
424 	if (p != ETIMEDOUT) {
425 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock on locked "
426 		    "rwlock with CLOCK_HIGHRES returned %s, not ETIMEDOUT",
427 		    test->ct_desc, strerrorname_np(p));
428 		ret = false;
429 	}
430 	if (!elapse) {
431 		warnx("TEST FAILED: %s: pthread_rwlock_clockwrlock on locked "
432 		    "rwlock with CLOCK_HIGHRES did not block long enough!",
433 		    test->ct_desc);
434 		ret = false;
435 	}
436 
437 
438 	return (ret);
439 }
440 
441 static bool
clock_test_rwlock_to_rel(const clock_test_t * test,void * prim)442 clock_test_rwlock_to_rel(const clock_test_t *test, void *prim)
443 {
444 	pthread_rwlock_t *rwl = prim;
445 	struct timespec start;
446 	int p;
447 	bool ret = true, elapse;
448 
449 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
450 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
451 	}
452 	p = pthread_rwlock_reltimedrdlock_np(rwl, &clock_to_100ms);
453 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
454 	if (p != ETIMEDOUT) {
455 		warnx("TEST FAILED: %s: pthread_rwlock_reltimedrdlock_np on "
456 		    "locked rwlock returned %s, not ETIMEDOUT", test->ct_desc,
457 		    strerrorname_np(p));
458 		ret = false;
459 	}
460 	if (!elapse) {
461 		warnx("TEST FAILED: %s: pthread_rwlock_reltimedrdlock_np on "
462 		    "locked rwlock did not block long enough!", test->ct_desc);
463 		ret = false;
464 	}
465 
466 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
467 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
468 	}
469 	p = pthread_rwlock_relclockrdlock_np(rwl, CLOCK_REALTIME,
470 	    &clock_to_100ms);
471 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
472 	if (p != ETIMEDOUT) {
473 		warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock_np on "
474 		    "locked rwlock with CLOCK_REALTIME returned %s, not "
475 		    "ETIMEDOUT", test->ct_desc, strerrorname_np(p));
476 		ret = false;
477 	}
478 	if (!elapse) {
479 		warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock_np on "
480 		    "locked " "rwlock with CLOCK_REALTIME did not block long "
481 		    "enough!", test->ct_desc);
482 		ret = false;
483 	}
484 
485 	if (clock_gettime(CLOCK_HIGHRES, &start) != 0) {
486 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_HIGHRES);
487 	}
488 	p = pthread_rwlock_relclockrdlock_np(rwl, CLOCK_HIGHRES,
489 	    &clock_to_100ms);
490 	elapse = clock_rel_after(CLOCK_HIGHRES, &start, &clock_to_100ms);
491 	if (p != ETIMEDOUT) {
492 		warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock_np on "
493 		    "locked rwlock with CLOCK_HIGHRES returned %s, not "
494 		    "ETIMEDOUT", test->ct_desc, strerrorname_np(p));
495 		ret = false;
496 	}
497 	if (!elapse) {
498 		warnx("TEST FAILED: %s: pthread_rwlock_relclockrdlock_np on "
499 		    "locked rwlock with CLOCK_HIGHRES did not block long "
500 		    "enough!", test->ct_desc);
501 		ret = false;
502 	}
503 
504 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
505 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
506 	}
507 	p = pthread_rwlock_reltimedwrlock_np(rwl, &clock_to_100ms);
508 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
509 	if (p != ETIMEDOUT) {
510 		warnx("TEST FAILED: %s: pthread_rwlock_reltimedwrlock_np on "
511 		    "locked rwlock returned %s, not ETIMEDOUT", test->ct_desc,
512 		    strerrorname_np(p));
513 		ret = false;
514 	}
515 	if (!elapse) {
516 		warnx("TEST FAILED: %s: pthread_rwlock_reltimedwrlock_np on "
517 		    "locked " "rwlock did not block long enough!",
518 		    test->ct_desc);
519 		ret = false;
520 	}
521 
522 	if (clock_gettime(CLOCK_REALTIME, &start) != 0) {
523 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_REALTIME);
524 	}
525 	p = pthread_rwlock_relclockwrlock_np(rwl, CLOCK_REALTIME,
526 	    &clock_to_100ms);
527 	elapse = clock_rel_after(CLOCK_REALTIME, &start, &clock_to_100ms);
528 	if (p != ETIMEDOUT) {
529 		warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock_np on "
530 		    "locked rwlock with CLOCK_REALTIME returned %s, not "
531 		    "ETIMEDOUT", test->ct_desc, strerrorname_np(p));
532 		ret = false;
533 	}
534 	if (!elapse) {
535 		warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock_np on "
536 		    "locked " "rwlock with CLOCK_REALTIME did not block long "
537 		    "enough!", test->ct_desc);
538 		ret = false;
539 	}
540 
541 	if (clock_gettime(CLOCK_HIGHRES, &start) != 0) {
542 		err(EXIT_FAILURE, "failed to read clock %d", CLOCK_HIGHRES);
543 	}
544 	p = pthread_rwlock_relclockwrlock_np(rwl, CLOCK_HIGHRES,
545 	    &clock_to_100ms);
546 	elapse = clock_rel_after(CLOCK_HIGHRES, &start, &clock_to_100ms);
547 	if (p != ETIMEDOUT) {
548 		warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock_np on "
549 		    "locked rwlock with CLOCK_HIGHRES returned %s, not "
550 		    "ETIMEDOUT", test->ct_desc, strerrorname_np(p));
551 		ret = false;
552 	}
553 	if (!elapse) {
554 		warnx("TEST FAILED: %s: pthread_rwlock_relclockwrlock_np on "
555 		    "locked rwlock with CLOCK_HIGHRES did not block long "
556 		    "enough!", test->ct_desc);
557 		ret = false;
558 	}
559 
560 	return (ret);
561 }
562 const clock_test_t clock_rwlock_tests[] = { {
563 	.ct_desc = "rwlock: invalid and unsupported clock sources",
564 	.ct_ops = &clock_lock_rwlock_ops,
565 	.ct_test = clock_test_rwlock_invalid_source
566 }, {
567 	.ct_desc = "rwlock: invalid timeout works if lock available (absolute)",
568 	.ct_ops = &clock_lock_rwlock_ops,
569 	.ct_test = clock_test_rwlock_inv_to_ign_abs
570 }, {
571 	.ct_desc = "rwlock: invalid timeout works if lock available (relative)",
572 	.ct_ops = &clock_lock_rwlock_ops,
573 	.ct_test = clock_test_rwlock_inv_to_ign_rel
574 }, {
575 	.ct_desc = "rwlock: invalid timeout fails if lock taken (absolute)",
576 	.ct_ops = &clock_lock_rwlock_ops,
577 	.ct_test = clock_test_rwlock_inv_to_abs,
578 	.ct_enter = true
579 }, {
580 	.ct_desc = "rwlock: invalid timeout fails if lock taken (relative)",
581 	.ct_ops = &clock_lock_rwlock_ops,
582 	.ct_test = clock_test_rwlock_inv_to_rel,
583 	.ct_enter = true
584 }, {
585 	.ct_desc = "rwlock: timeout fires correctly (absolute)",
586 	.ct_ops = &clock_lock_rwlock_ops,
587 	.ct_test = clock_test_rwlock_to_abs,
588 	.ct_enter = true
589 }, {
590 	.ct_desc = "rwlock: timeout fires correctly (relative)",
591 	.ct_ops = &clock_lock_rwlock_ops,
592 	.ct_test = clock_test_rwlock_to_rel,
593 	.ct_enter = true
594 } };
595 
596 size_t clock_rwlock_ntests = ARRAY_SIZE(clock_rwlock_tests);
597