541 lines · cpp
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-exception6//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 47namespace LIBC_NAMESPACE_DECL {48namespace rwlock {49class RwLockTester {50public:51 static constexpr int full_reader_state() {52 return (~0) & (~RwState::PENDING_MASK) & (~RwState::ACTIVE_WRITER_BIT);53 }54};55} // namespace rwlock56} // namespace LIBC_NAMESPACE_DECL57 58static 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 75static 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 85static 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 102static 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 111static 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 reader131// counts.132static 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 165static 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 206static 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 240static 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 299struct 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 307enum 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 = 8317};318 319LIBC_NAMESPACE::RawMutex *io_mutex;320struct 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 = static_cast<pid_t>(LIBC_NAMESPACE::syscall_impl(SYS_getpid));328 pid_t tid = static_cast<pid_t>(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 338static 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 it341 // 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 439static void440randomized_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 469static 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 485static 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 520TEST_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