1 | //===-- Unittests for Linux's RawMutex ------------------------------------===// |
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/signal_macros.h" |
10 | #include "include/llvm-libc-macros/linux/time-macros.h" |
11 | #include "src/__support/CPP/atomic.h" |
12 | #include "src/__support/OSUtil/syscall.h" |
13 | #include "src/__support/threads/linux/raw_mutex.h" |
14 | #include "src/__support/threads/sleep.h" |
15 | #include "src/__support/time/clock_gettime.h" |
16 | #include "src/stdlib/exit.h" |
17 | #include "src/sys/mman/mmap.h" |
18 | #include "src/sys/mman/munmap.h" |
19 | #include "test/UnitTest/Test.h" |
20 | #include <sys/syscall.h> |
21 | |
22 | TEST(LlvmLibcSupportThreadsRawMutexTest, SmokeTest) { |
23 | LIBC_NAMESPACE::RawMutex mutex; |
24 | ASSERT_TRUE(mutex.lock()); |
25 | ASSERT_TRUE(mutex.unlock()); |
26 | ASSERT_TRUE(mutex.try_lock()); |
27 | ASSERT_FALSE(mutex.try_lock()); |
28 | ASSERT_TRUE(mutex.unlock()); |
29 | ASSERT_FALSE(mutex.unlock()); |
30 | } |
31 | |
32 | TEST(LlvmLibcSupportThreadsRawMutexTest, Timeout) { |
33 | LIBC_NAMESPACE::RawMutex mutex; |
34 | ASSERT_TRUE(mutex.lock()); |
35 | timespec ts; |
36 | LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts); |
37 | ts.tv_sec += 1; |
38 | // Timeout will be respected when deadlock happens. |
39 | auto timeout = LIBC_NAMESPACE::internal::AbsTimeout::from_timespec(ts, false); |
40 | ASSERT_TRUE(timeout.has_value()); |
41 | // The following will timeout |
42 | ASSERT_FALSE(mutex.lock(*timeout)); |
43 | ASSERT_TRUE(mutex.unlock()); |
44 | // Test that the mutex works after the timeout. |
45 | ASSERT_TRUE(mutex.lock()); |
46 | ASSERT_TRUE(mutex.unlock()); |
47 | // If a lock can be acquired directly, expired timeout will not count. |
48 | // Notice that the timeout is already reached during preivous deadlock. |
49 | ASSERT_TRUE(mutex.lock(*timeout)); |
50 | ASSERT_TRUE(mutex.unlock()); |
51 | } |
52 | |
53 | TEST(LlvmLibcSupportThreadsRawMutexTest, PSharedLock) { |
54 | struct SharedData { |
55 | LIBC_NAMESPACE::RawMutex mutex; |
56 | LIBC_NAMESPACE::cpp::Atomic<size_t> finished; |
57 | int data; |
58 | }; |
59 | void *addr = |
60 | LIBC_NAMESPACE::mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, |
61 | MAP_ANONYMOUS | MAP_SHARED, -1, 0); |
62 | ASSERT_NE(addr, MAP_FAILED); |
63 | auto *shared = reinterpret_cast<SharedData *>(addr); |
64 | shared->data = 0; |
65 | LIBC_NAMESPACE::RawMutex::init(&shared->mutex); |
66 | // Avoid pull in our own implementation of pthread_t. |
67 | #ifdef SYS_fork |
68 | long pid = LIBC_NAMESPACE::syscall_impl<long>(SYS_fork); |
69 | #elif defined(SYS_clone) |
70 | long pid = LIBC_NAMESPACE::syscall_impl<long>(SYS_clone, SIGCHLD, 0); |
71 | #endif |
72 | for (int i = 0; i < 10000; ++i) { |
73 | shared->mutex.lock(LIBC_NAMESPACE::cpp::nullopt, true); |
74 | shared->data++; |
75 | shared->mutex.unlock(true); |
76 | } |
77 | // Mark the thread as finished. |
78 | shared->finished.fetch_add(1); |
79 | // let the child exit early to avoid output pollution |
80 | if (pid == 0) |
81 | LIBC_NAMESPACE::exit(0); |
82 | while (shared->finished.load() != 2) |
83 | LIBC_NAMESPACE::sleep_briefly(); |
84 | ASSERT_EQ(shared->data, 20000); |
85 | LIBC_NAMESPACE::munmap(addr, sizeof(SharedData)); |
86 | } |
87 | |