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