xref: /aosp_15_r20/external/llvm-libc/test/integration/src/pthread/pthread_rwlock_test.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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:
full_reader_state()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 
smoke_test()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 
deadlock_detection_test()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 
try_lock_test()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 
destroy_before_unlock_test()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 
nullptr_test()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.
high_reader_count_test()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 
unusual_timespec_test()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 
timedlock_with_deadlock_test()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 
attributed_initialization_test()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;
pushThreadGuard323   void push(Operation op) { record[cursor++] = op; }
~ThreadGuardThreadGuard324   ~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 
randomized_thread_operation(SharedData * data,ThreadGuard & guard)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
randomized_process_operation(SharedData & data,LIBC_NAMESPACE::cpp::Atomic<int> & finish_count,int expected_count)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 
single_process_test(int preference)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 
multiple_process_test(int preference)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 
TEST_MAIN()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