//===-- Unittests for Linux's RawMutex ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "hdr/signal_macros.h" #include "include/llvm-libc-macros/linux/time-macros.h" #include "src/__support/CPP/atomic.h" #include "src/__support/OSUtil/syscall.h" #include "src/__support/threads/linux/raw_mutex.h" #include "src/__support/threads/sleep.h" #include "src/__support/time/clock_gettime.h" #include "src/stdlib/exit.h" #include "src/sys/mman/mmap.h" #include "src/sys/mman/munmap.h" #include "test/UnitTest/Test.h" #include TEST(LlvmLibcSupportThreadsRawMutexTest, SmokeTest) { LIBC_NAMESPACE::RawMutex mutex; ASSERT_TRUE(mutex.lock()); ASSERT_TRUE(mutex.unlock()); ASSERT_TRUE(mutex.try_lock()); ASSERT_FALSE(mutex.try_lock()); ASSERT_TRUE(mutex.unlock()); ASSERT_FALSE(mutex.unlock()); } TEST(LlvmLibcSupportThreadsRawMutexTest, Timeout) { LIBC_NAMESPACE::RawMutex mutex; ASSERT_TRUE(mutex.lock()); timespec ts; LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_sec += 1; // Timeout will be respected when deadlock happens. auto timeout = LIBC_NAMESPACE::internal::AbsTimeout::from_timespec(ts, false); ASSERT_TRUE(timeout.has_value()); // The following will timeout ASSERT_FALSE(mutex.lock(*timeout)); ASSERT_TRUE(mutex.unlock()); // Test that the mutex works after the timeout. ASSERT_TRUE(mutex.lock()); ASSERT_TRUE(mutex.unlock()); // If a lock can be acquired directly, expired timeout will not count. // Notice that the timeout is already reached during preivous deadlock. ASSERT_TRUE(mutex.lock(*timeout)); ASSERT_TRUE(mutex.unlock()); } TEST(LlvmLibcSupportThreadsRawMutexTest, PSharedLock) { struct SharedData { LIBC_NAMESPACE::RawMutex mutex; LIBC_NAMESPACE::cpp::Atomic finished; int data; }; void *addr = LIBC_NAMESPACE::mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); ASSERT_NE(addr, MAP_FAILED); auto *shared = reinterpret_cast(addr); shared->data = 0; LIBC_NAMESPACE::RawMutex::init(&shared->mutex); // Avoid pull in our own implementation of pthread_t. #ifdef SYS_fork long pid = LIBC_NAMESPACE::syscall_impl(SYS_fork); #elif defined(SYS_clone) long pid = LIBC_NAMESPACE::syscall_impl(SYS_clone, SIGCHLD, 0); #endif for (int i = 0; i < 10000; ++i) { shared->mutex.lock(LIBC_NAMESPACE::cpp::nullopt, true); shared->data++; shared->mutex.unlock(true); } // Mark the thread as finished. shared->finished.fetch_add(1); // let the child exit early to avoid output pollution if (pid == 0) LIBC_NAMESPACE::exit(0); while (shared->finished.load() != 2) LIBC_NAMESPACE::sleep_briefly(); ASSERT_EQ(shared->data, 20000); LIBC_NAMESPACE::munmap(addr, sizeof(SharedData)); }