xref: /llvm-project/libc/test/src/__support/threads/linux/raw_mutex_test.cpp (revision e6cf5d2863b77895ae7183952514bedd9e8dde16)
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