brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.3 KiB · 205e9f7 Raw
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