1 //===-- Tests for pthread_rwlock ------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "hdr/errno_macros.h" 10 #include "hdr/time_macros.h" 11 #include "src/__support/CPP/atomic.h" 12 #include "src/__support/CPP/new.h" 13 #include "src/__support/OSUtil/syscall.h" 14 #include "src/__support/macros/config.h" 15 #include "src/__support/threads/linux/raw_mutex.h" 16 #include "src/__support/threads/linux/rwlock.h" 17 #include "src/__support/threads/sleep.h" 18 #include "src/pthread/pthread_create.h" 19 #include "src/pthread/pthread_join.h" 20 #include "src/pthread/pthread_rwlock_clockrdlock.h" 21 #include "src/pthread/pthread_rwlock_clockwrlock.h" 22 #include "src/pthread/pthread_rwlock_destroy.h" 23 #include "src/pthread/pthread_rwlock_init.h" 24 #include "src/pthread/pthread_rwlock_rdlock.h" 25 #include "src/pthread/pthread_rwlock_timedrdlock.h" 26 #include "src/pthread/pthread_rwlock_timedwrlock.h" 27 #include "src/pthread/pthread_rwlock_tryrdlock.h" 28 #include "src/pthread/pthread_rwlock_trywrlock.h" 29 #include "src/pthread/pthread_rwlock_unlock.h" 30 #include "src/pthread/pthread_rwlock_wrlock.h" 31 #include "src/pthread/pthread_rwlockattr_destroy.h" 32 #include "src/pthread/pthread_rwlockattr_init.h" 33 #include "src/pthread/pthread_rwlockattr_setkind_np.h" 34 #include "src/pthread/pthread_rwlockattr_setpshared.h" 35 #include "src/stdio/printf.h" 36 #include "src/stdlib/exit.h" 37 #include "src/stdlib/getenv.h" 38 #include "src/sys/mman/mmap.h" 39 #include "src/sys/mman/munmap.h" 40 #include "src/sys/random/getrandom.h" 41 #include "src/sys/wait/waitpid.h" 42 #include "src/time/clock_gettime.h" 43 #include "src/unistd/fork.h" 44 #include "test/IntegrationTest/test.h" 45 #include <pthread.h> 46 47 namespace LIBC_NAMESPACE_DECL { 48 namespace rwlock { 49 class RwLockTester { 50 public: 51 static constexpr int full_reader_state() { 52 return (~0) & (~RwState::PENDING_MASK) & (~RwState::ACTIVE_WRITER_BIT); 53 } 54 }; 55 } // namespace rwlock 56 } // namespace LIBC_NAMESPACE_DECL 57 58 static void smoke_test() { 59 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 60 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0); 61 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0); 62 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0); 63 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY); 64 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 65 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 66 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0); 67 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), EDEADLK); 68 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), EDEADLK); 69 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EBUSY); 70 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY); 71 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 72 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 73 } 74 75 static void deadlock_detection_test() { 76 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 77 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0); 78 // We only detect RAW, WAW deadlocks. 79 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0); 80 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), EDEADLK); 81 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 82 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 83 } 84 85 static void try_lock_test() { 86 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 87 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0); 88 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0); 89 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY); 90 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EBUSY); 91 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 92 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0); 93 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0); 94 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0); 95 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), EBUSY); 96 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 97 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 98 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 99 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 100 } 101 102 static void destroy_before_unlock_test() { 103 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 104 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, nullptr), 0); 105 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0); 106 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), EBUSY); 107 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 108 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 109 } 110 111 static void nullptr_test() { 112 timespec ts = {}; 113 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(nullptr), EINVAL); 114 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(nullptr), EINVAL); 115 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(nullptr, &ts), EINVAL); 116 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(nullptr, &ts), EINVAL); 117 ASSERT_EQ( 118 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(nullptr, CLOCK_MONOTONIC, &ts), 119 EINVAL); 120 ASSERT_EQ( 121 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(nullptr, CLOCK_MONOTONIC, &ts), 122 EINVAL); 123 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(nullptr), EINVAL); 124 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(nullptr), EINVAL); 125 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(nullptr), EINVAL); 126 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(nullptr), EINVAL); 127 } 128 129 // If you are a user reading this code, please do not do something like this. 130 // We manually modify the internal state of the rwlock to test high reader 131 // counts. 132 static void high_reader_count_test() { 133 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 134 rwlock.__state = LIBC_NAMESPACE::rwlock::RwLockTester::full_reader_state(); 135 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), EAGAIN); 136 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), EAGAIN); 137 // allocate 4 reader slots. 138 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 139 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 140 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 141 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 142 143 pthread_t threads[20]; 144 for (auto &i : threads) 145 ASSERT_EQ(LIBC_NAMESPACE::pthread_create( 146 &i, nullptr, 147 [](void *arg) -> void * { 148 pthread_rwlock_t *rwlock = 149 reinterpret_cast<pthread_rwlock_t *>(arg); 150 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(rwlock), 151 EBUSY); 152 while (LIBC_NAMESPACE::pthread_rwlock_rdlock(rwlock) == 153 EAGAIN) 154 LIBC_NAMESPACE::sleep_briefly(); 155 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(rwlock), 0); 156 return nullptr; 157 }, 158 &rwlock), 159 0); 160 161 for (auto &i : threads) 162 ASSERT_EQ(LIBC_NAMESPACE::pthread_join(i, nullptr), 0); 163 } 164 165 static void unusual_timespec_test() { 166 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 167 timespec ts = {0, -1}; 168 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL); 169 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL); 170 ASSERT_EQ( 171 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts), 172 EINVAL); 173 ASSERT_EQ( 174 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts), 175 EINVAL); 176 ts.tv_nsec = 1'000'000'000; 177 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), EINVAL); 178 ASSERT_EQ( 179 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts), 180 EINVAL); 181 ASSERT_EQ( 182 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts), 183 EINVAL); 184 ts.tv_nsec += 1; 185 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), EINVAL); 186 ASSERT_EQ( 187 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts), 188 EINVAL); 189 ASSERT_EQ( 190 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts), 191 EINVAL); 192 ts.tv_nsec = 0; 193 ts.tv_sec = -1; 194 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), 195 ETIMEDOUT); 196 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), 197 ETIMEDOUT); 198 ASSERT_EQ( 199 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_MONOTONIC, &ts), 200 ETIMEDOUT); 201 ASSERT_EQ( 202 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_MONOTONIC, &ts), 203 ETIMEDOUT); 204 } 205 206 static void timedlock_with_deadlock_test() { 207 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 208 timespec ts{}; 209 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0); 210 LIBC_NAMESPACE::clock_gettime(CLOCK_REALTIME, &ts); 211 ts.tv_nsec += 50'000; 212 if (ts.tv_nsec >= 1'000'000'000) { 213 ts.tv_nsec -= 1'000'000'000; 214 ts.tv_sec += 1; 215 } 216 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&rwlock, &ts), 217 ETIMEDOUT); 218 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&rwlock, &ts), 0); 219 ASSERT_EQ( 220 LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&rwlock, CLOCK_REALTIME, &ts), 221 ETIMEDOUT); 222 ASSERT_EQ( 223 LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&rwlock, CLOCK_REALTIME, &ts), 224 0); 225 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 226 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 227 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 228 // notice that ts is already expired, but the following should still succeed. 229 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&rwlock), 0); 230 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 231 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_trywrlock(&rwlock), 0); 232 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 233 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_rdlock(&rwlock), 0); 234 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 235 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_wrlock(&rwlock), 0); 236 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_unlock(&rwlock), 0); 237 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 238 } 239 240 static void attributed_initialization_test() { 241 pthread_rwlockattr_t attr{}; 242 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0); 243 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np( 244 &attr, PTHREAD_RWLOCK_PREFER_READER_NP), 245 0); 246 { 247 pthread_rwlock_t rwlock{}; 248 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0); 249 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 250 } 251 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np( 252 &attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP), 253 0); 254 { 255 pthread_rwlock_t rwlock{}; 256 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0); 257 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 258 } 259 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np( 260 &attr, PTHREAD_RWLOCK_PREFER_WRITER_NP), 261 0); 262 { 263 pthread_rwlock_t rwlock{}; 264 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL); 265 } 266 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np( 267 &attr, PTHREAD_RWLOCK_PREFER_READER_NP), 268 0); 269 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared( 270 &attr, PTHREAD_PROCESS_PRIVATE), 271 0); 272 { 273 pthread_rwlock_t rwlock{}; 274 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0); 275 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 276 } 277 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared( 278 &attr, PTHREAD_PROCESS_SHARED), 279 0); 280 { 281 pthread_rwlock_t rwlock{}; 282 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), 0); 283 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&rwlock), 0); 284 } 285 attr.pref = -1; 286 { 287 pthread_rwlock_t rwlock{}; 288 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL); 289 } 290 attr.pref = PTHREAD_RWLOCK_PREFER_READER_NP; 291 attr.pshared = -1; 292 { 293 pthread_rwlock_t rwlock{}; 294 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&rwlock, &attr), EINVAL); 295 } 296 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_destroy(&attr), 0); 297 } 298 299 struct SharedData { 300 pthread_rwlock_t lock; 301 int data; 302 LIBC_NAMESPACE::cpp::Atomic<int> reader_count; 303 bool writer_flag; 304 LIBC_NAMESPACE::cpp::Atomic<int> total_writer_count; 305 }; 306 307 enum class Operation : int { 308 READ = 0, 309 WRITE = 1, 310 TIMED_READ = 2, 311 TIMED_WRITE = 3, 312 CLOCK_READ = 4, 313 CLOCK_WRITE = 5, 314 TRY_READ = 6, 315 TRY_WRITE = 7, 316 COUNT = 8 317 }; 318 319 LIBC_NAMESPACE::RawMutex *io_mutex; 320 struct ThreadGuard { 321 Operation record[64]{}; 322 size_t cursor = 0; 323 void push(Operation op) { record[cursor++] = op; } 324 ~ThreadGuard() { 325 if (!LIBC_NAMESPACE::getenv("LIBC_PTHREAD_RWLOCK_TEST_VERBOSE")) 326 return; 327 pid_t pid = LIBC_NAMESPACE::syscall_impl(SYS_getpid); 328 pid_t tid = LIBC_NAMESPACE::syscall_impl(SYS_gettid); 329 io_mutex->lock(LIBC_NAMESPACE::cpp::nullopt, true); 330 LIBC_NAMESPACE::printf("process %d thread %d: ", pid, tid); 331 for (size_t i = 0; i < cursor; ++i) 332 LIBC_NAMESPACE::printf("%d ", static_cast<int>(record[i])); 333 LIBC_NAMESPACE::printf("\n"); 334 io_mutex->unlock(true); 335 } 336 }; 337 338 static void randomized_thread_operation(SharedData *data, ThreadGuard &guard) { 339 int buffer; 340 // We cannot reason about thread order anyway, let's go wild and randomize it 341 // directly using getrandom. 342 LIBC_NAMESPACE::getrandom(&buffer, sizeof(buffer), 0); 343 constexpr int TOTAL = static_cast<int>(Operation::COUNT); 344 Operation op = static_cast<Operation>(((buffer % TOTAL) + TOTAL) % TOTAL); 345 guard.push(op); 346 auto read_ops = [data]() { 347 ASSERT_FALSE(data->writer_flag); 348 data->reader_count.fetch_add(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED); 349 for (int i = 0; i < 10; ++i) 350 LIBC_NAMESPACE::sleep_briefly(); 351 data->reader_count.fetch_sub(1, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED); 352 }; 353 auto write_ops = [data]() { 354 ASSERT_FALSE(data->writer_flag); 355 data->data += 1; 356 data->writer_flag = true; 357 for (int i = 0; i < 10; ++i) 358 LIBC_NAMESPACE::sleep_briefly(); 359 ASSERT_EQ(data->reader_count, 0); 360 data->writer_flag = false; 361 data->total_writer_count.fetch_add(1); 362 }; 363 auto get_ts = []() { 364 timespec ts{}; 365 LIBC_NAMESPACE::clock_gettime(CLOCK_REALTIME, &ts); 366 ts.tv_nsec += 5'000; 367 if (ts.tv_nsec >= 1'000'000'000) { 368 ts.tv_nsec -= 1'000'000'000; 369 ts.tv_sec += 1; 370 } 371 return ts; 372 }; 373 switch (op) { 374 case Operation::READ: { 375 LIBC_NAMESPACE::pthread_rwlock_rdlock(&data->lock); 376 read_ops(); 377 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 378 break; 379 } 380 case Operation::WRITE: { 381 LIBC_NAMESPACE::pthread_rwlock_wrlock(&data->lock); 382 write_ops(); 383 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 384 break; 385 } 386 case Operation::TIMED_READ: { 387 timespec ts = get_ts(); 388 if (LIBC_NAMESPACE::pthread_rwlock_timedrdlock(&data->lock, &ts) == 0) { 389 read_ops(); 390 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 391 } 392 break; 393 } 394 case Operation::TIMED_WRITE: { 395 timespec ts = get_ts(); 396 if (LIBC_NAMESPACE::pthread_rwlock_timedwrlock(&data->lock, &ts) == 0) { 397 write_ops(); 398 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 399 } 400 break; 401 } 402 case Operation::CLOCK_READ: { 403 timespec ts = get_ts(); 404 if (LIBC_NAMESPACE::pthread_rwlock_clockrdlock(&data->lock, CLOCK_MONOTONIC, 405 &ts) == 0) { 406 read_ops(); 407 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 408 } 409 break; 410 } 411 case Operation::CLOCK_WRITE: { 412 timespec ts = get_ts(); 413 if (LIBC_NAMESPACE::pthread_rwlock_clockwrlock(&data->lock, CLOCK_MONOTONIC, 414 &ts) == 0) { 415 write_ops(); 416 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 417 } 418 break; 419 } 420 case Operation::TRY_READ: { 421 if (LIBC_NAMESPACE::pthread_rwlock_tryrdlock(&data->lock) == 0) { 422 read_ops(); 423 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 424 } 425 break; 426 } 427 case Operation::TRY_WRITE: { 428 if (LIBC_NAMESPACE::pthread_rwlock_trywrlock(&data->lock) == 0) { 429 write_ops(); 430 LIBC_NAMESPACE::pthread_rwlock_unlock(&data->lock); 431 } 432 break; 433 } 434 case Operation::COUNT: 435 __builtin_trap(); 436 } 437 } 438 439 static void 440 randomized_process_operation(SharedData &data, 441 LIBC_NAMESPACE::cpp::Atomic<int> &finish_count, 442 int expected_count) { 443 pthread_t threads[32]; 444 for (auto &i : threads) 445 ASSERT_EQ(LIBC_NAMESPACE::pthread_create( 446 &i, nullptr, 447 [](void *arg) -> void * { 448 ThreadGuard guard{}; 449 for (int i = 0; i < 64; ++i) 450 randomized_thread_operation( 451 reinterpret_cast<SharedData *>(arg), guard); 452 return nullptr; 453 }, 454 &data), 455 0); 456 457 for (auto &i : threads) 458 ASSERT_EQ(LIBC_NAMESPACE::pthread_join(i, nullptr), 0); 459 460 finish_count.fetch_add(1); 461 while (finish_count.load() != expected_count) 462 LIBC_NAMESPACE::sleep_briefly(); 463 464 ASSERT_EQ(data.total_writer_count.load(), data.data); 465 ASSERT_FALSE(data.writer_flag); 466 ASSERT_EQ(data.reader_count, 0); 467 } 468 469 static void single_process_test(int preference) { 470 SharedData data{}; 471 data.data = 0; 472 data.reader_count = 0; 473 data.writer_flag = false; 474 data.total_writer_count.store(0); 475 pthread_rwlockattr_t attr{}; 476 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0); 477 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(&attr, preference), 478 0); 479 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&data.lock, nullptr), 0); 480 LIBC_NAMESPACE::cpp::Atomic<int> finish_count{0}; 481 randomized_process_operation(data, finish_count, 1); 482 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&data.lock), 0); 483 } 484 485 static void multiple_process_test(int preference) { 486 struct PShared { 487 SharedData data; 488 LIBC_NAMESPACE::cpp::Atomic<int> finish_count; 489 }; 490 PShared *shared_data = reinterpret_cast<PShared *>( 491 LIBC_NAMESPACE::mmap(nullptr, sizeof(PShared), PROT_READ | PROT_WRITE, 492 MAP_SHARED | MAP_ANONYMOUS, -1, 0)); 493 shared_data->data.data = 0; 494 shared_data->data.reader_count = 0; 495 shared_data->data.writer_flag = false; 496 shared_data->data.total_writer_count.store(0); 497 shared_data->finish_count.store(0); 498 pthread_rwlockattr_t attr{}; 499 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_init(&attr), 0); 500 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setkind_np(&attr, preference), 501 0); 502 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlockattr_setpshared( 503 &attr, PTHREAD_PROCESS_SHARED), 504 0); 505 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_init(&shared_data->data.lock, &attr), 506 0); 507 int pid = LIBC_NAMESPACE::fork(); 508 randomized_process_operation(shared_data->data, shared_data->finish_count, 2); 509 if (pid == 0) 510 LIBC_NAMESPACE::exit(0); 511 else { 512 int status; 513 LIBC_NAMESPACE::waitpid(pid, &status, 0); 514 ASSERT_EQ(status, 0); 515 } 516 ASSERT_EQ(LIBC_NAMESPACE::pthread_rwlock_destroy(&shared_data->data.lock), 0); 517 LIBC_NAMESPACE::munmap(shared_data, sizeof(PShared)); 518 } 519 520 TEST_MAIN() { 521 io_mutex = new (LIBC_NAMESPACE::mmap( 522 nullptr, sizeof(LIBC_NAMESPACE::RawMutex), PROT_READ | PROT_WRITE, 523 MAP_ANONYMOUS | MAP_SHARED, -1, 0)) LIBC_NAMESPACE::RawMutex(); 524 smoke_test(); 525 deadlock_detection_test(); 526 try_lock_test(); 527 destroy_before_unlock_test(); 528 nullptr_test(); 529 high_reader_count_test(); 530 unusual_timespec_test(); 531 timedlock_with_deadlock_test(); 532 attributed_initialization_test(); 533 single_process_test(PTHREAD_RWLOCK_PREFER_READER_NP); 534 single_process_test(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); 535 multiple_process_test(PTHREAD_RWLOCK_PREFER_READER_NP); 536 multiple_process_test(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); 537 io_mutex->~RawMutex(); 538 LIBC_NAMESPACE::munmap(io_mutex, sizeof(LIBC_NAMESPACE::RawMutex)); 539 return 0; 540 } 541