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