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 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 52 clock_rwlock_destroy(void *arg) 53 { 54 VERIFY0(pthread_rwlock_destroy(arg)); 55 free(arg); 56 } 57 58 static void 59 clock_rwlock_wrlock(void *arg) 60 { 61 VERIFY0(pthread_rwlock_trywrlock(arg)); 62 } 63 64 static void 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 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 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 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 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 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 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 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