diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake index 2eb0f066e19e0..042350546b1c1 100644 --- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake +++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake @@ -170,6 +170,19 @@ function(_get_common_compile_options output_var flags) list(APPEND compile_options "-idirafter${LIBC_KERNEL_HEADERS}") endif() endif() + if(LIBC_TARGET_OS_IS_DARWIN) + execute_process( + COMMAND xcrun --sdk macosx --show-sdk-path + OUTPUT_VARIABLE MACOSX_SDK_PATH + RESULT_VARIABLE MACOSX_SDK_PATH_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(MACOSX_SDK_PATH_RESULT EQUAL 0) + list(APPEND compile_options "-I" "${MACOSX_SDK_PATH}/usr/include") + else() + message(WARNING "Could not find macOS SDK path. `xcrun --sdk macosx --show-sdk-path` failed.") + endif() + endif() endif() if(LIBC_COMPILER_HAS_FIXED_POINT) diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt index e3c6c2b30c415..3909417f9730d 100644 --- a/libc/config/darwin/aarch64/entrypoints.txt +++ b/libc/config/darwin/aarch64/entrypoints.txt @@ -111,6 +111,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.setjmp.setjmp libc.src.setjmp.siglongjmp libc.src.setjmp.sigsetjmp + libc.src.stdlib._Exit ) endif() diff --git a/libc/include/llvm-libc-macros/darwin/CMakeLists.txt b/libc/include/llvm-libc-macros/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..ea08c63c00301 --- /dev/null +++ b/libc/include/llvm-libc-macros/darwin/CMakeLists.txt @@ -0,0 +1,5 @@ +add_header( + time_macros + HDR + time-macros.h +) diff --git a/libc/include/llvm-libc-macros/darwin/time-macros.h b/libc/include/llvm-libc-macros/darwin/time-macros.h new file mode 100644 index 0000000000000..477dfa8eda85f --- /dev/null +++ b/libc/include/llvm-libc-macros/darwin/time-macros.h @@ -0,0 +1,14 @@ +//===-- Definition of macros from time.h ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_MACROS_LINUX_TIME_MACROS_H +#define LLVM_LIBC_MACROS_LINUX_TIME_MACROS_H + +#include <_time.h> + +#endif // LLVM_LIBC_MACROS_LINUX_TIME_MACROS_H diff --git a/libc/include/llvm-libc-macros/time-macros.h b/libc/include/llvm-libc-macros/time-macros.h index 30e0a310a5485..c026df29b1e7f 100644 --- a/libc/include/llvm-libc-macros/time-macros.h +++ b/libc/include/llvm-libc-macros/time-macros.h @@ -7,6 +7,8 @@ #include "linux/time-macros.h" #elif defined(__ELF__) #include "baremetal/time-macros.h" +#elif defined(__APPLE__) +#include "darwin/time-macros.h" #else #define CLOCKS_PER_SEC 1000000 #endif diff --git a/libc/include/llvm-libc-types/clockid_t.h b/libc/include/llvm-libc-types/clockid_t.h index 4b059599502c4..926948717c664 100644 --- a/libc/include/llvm-libc-types/clockid_t.h +++ b/libc/include/llvm-libc-types/clockid_t.h @@ -9,6 +9,12 @@ #ifndef LLVM_LIBC_TYPES_CLOCKID_T_H #define LLVM_LIBC_TYPES_CLOCKID_T_H +#if defined(__APPLE__) +// Darwin provides its own defintion for clockid_t . Use that to prevent +// redeclaration errors and correctness. +#include <_time.h> +#else typedef int clockid_t; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_CLOCKID_T_H diff --git a/libc/include/llvm-libc-types/struct_timespec.h b/libc/include/llvm-libc-types/struct_timespec.h index 28b5a571f6790..8993ecc7db8f0 100644 --- a/libc/include/llvm-libc-types/struct_timespec.h +++ b/libc/include/llvm-libc-types/struct_timespec.h @@ -9,6 +9,11 @@ #ifndef LLVM_LIBC_TYPES_STRUCT_TIMESPEC_H #define LLVM_LIBC_TYPES_STRUCT_TIMESPEC_H +#if defined(__APPLE__) +// Darwin provides its own definition for struct timespec. Include it directly +// to ensure type compatibility and avoid redefinition errors. +#include +#else #include "time_t.h" struct timespec { @@ -16,5 +21,6 @@ struct timespec { /* TODO: BIG_ENDIAN may require padding. */ long tv_nsec; /* Nanoseconds. */ }; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_STRUCT_TIMESPEC_H diff --git a/libc/include/llvm-libc-types/struct_timeval.h b/libc/include/llvm-libc-types/struct_timeval.h index 9595d85a46c8f..41f0b4e92932e 100644 --- a/libc/include/llvm-libc-types/struct_timeval.h +++ b/libc/include/llvm-libc-types/struct_timeval.h @@ -12,9 +12,15 @@ #include "suseconds_t.h" #include "time_t.h" +#if defined(__APPLE__) +// Darwin provides its own definition for struct timeval. Include it directly +// to ensure type compatibility and avoid redefinition errors. +#include +#else struct timeval { time_t tv_sec; // Seconds suseconds_t tv_usec; // Micro seconds }; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_STRUCT_TIMEVAL_H diff --git a/libc/include/llvm-libc-types/suseconds_t.h b/libc/include/llvm-libc-types/suseconds_t.h index 8e926e8401f5c..acc1822cb59e1 100644 --- a/libc/include/llvm-libc-types/suseconds_t.h +++ b/libc/include/llvm-libc-types/suseconds_t.h @@ -14,6 +14,12 @@ // types...] and suseconds_t are no greater than the width of type long. // The kernel expects 64 bit suseconds_t at least on x86_64. +#if defined(__APPLE__) +// Darwin provides its own definition for suseconds_t. Include it directly +// to ensure type compatibility and avoid redefinition errors. +#include +#else typedef long suseconds_t; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_SUSECONDS_T_H diff --git a/libc/include/llvm-libc-types/time_t_32.h b/libc/include/llvm-libc-types/time_t_32.h index 2c415f6fa9dca..8d7a81e5ce7f7 100644 --- a/libc/include/llvm-libc-types/time_t_32.h +++ b/libc/include/llvm-libc-types/time_t_32.h @@ -9,6 +9,12 @@ #ifndef LLVM_LIBC_TYPES_TIME_T_32_H #define LLVM_LIBC_TYPES_TIME_T_32_H +#if defined(__APPLE__) +// Darwin provides its own definition for time_t. Include it directly +// to ensure type compatibility and avoid redefinition errors. +#include +#else typedef __INT32_TYPE__ time_t; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_TIME_T_32_H diff --git a/libc/include/llvm-libc-types/time_t_64.h b/libc/include/llvm-libc-types/time_t_64.h index 8f7fd3233646e..c8267abe31289 100644 --- a/libc/include/llvm-libc-types/time_t_64.h +++ b/libc/include/llvm-libc-types/time_t_64.h @@ -9,6 +9,12 @@ #ifndef LLVM_LIBC_TYPES_TIME_T_64_H #define LLVM_LIBC_TYPES_TIME_T_64_H +#if defined(__APPLE__) +// Darwin provides its own definition for time_t. Include it directly +// to ensure type compatibility and avoid redefinition errors. +#include +#else typedef __INT64_TYPE__ time_t; +#endif // __APPLE__ #endif // LLVM_LIBC_TYPES_TIME_T_64_H diff --git a/libc/include/sys/syscall.h.def b/libc/include/sys/syscall.h.def index 60e5024e500e3..112fdd2d80369 100644 --- a/libc/include/sys/syscall.h.def +++ b/libc/include/sys/syscall.h.def @@ -9,7 +9,11 @@ #ifndef LLVM_LIBC_SYS_SYSCALL_H #define LLVM_LIBC_SYS_SYSCALL_H -//TODO: Handle non-linux syscalls +#if defined(__APPLE__) + +#include + +#elif defined(__linux__) #include @@ -2361,5 +2365,6 @@ #define SYS_writev __NR_writev #endif +#endif // __linux__ #endif // LLVM_LIBC_SYS_SYSCALL_H diff --git a/libc/src/__support/OSUtil/darwin/CMakeLists.txt b/libc/src/__support/OSUtil/darwin/CMakeLists.txt index 4241bb37684f7..9e69bf7d0cbab 100644 --- a/libc/src/__support/OSUtil/darwin/CMakeLists.txt +++ b/libc/src/__support/OSUtil/darwin/CMakeLists.txt @@ -4,13 +4,16 @@ endif() add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) -add_header_library( +add_object_library( darwin_util + SRCS + exit.cpp HDRS io.h syscall.h DEPENDS - .${LIBC_TARGET_ARCHITECTURE}.darwin_util + .${LIBC_TARGET_ARCHITECTURE}.darwin_${LIBC_TARGET_ARCHITECTURE}_util libc.src.__support.common libc.src.__support.CPP.string_view + libc.include.sys_syscall ) diff --git a/libc/src/__support/OSUtil/darwin/aarch64/CMakeLists.txt b/libc/src/__support/OSUtil/darwin/aarch64/CMakeLists.txt index 5ab95b01758c8..b36fe22017f34 100644 --- a/libc/src/__support/OSUtil/darwin/aarch64/CMakeLists.txt +++ b/libc/src/__support/OSUtil/darwin/aarch64/CMakeLists.txt @@ -1,5 +1,5 @@ add_header_library( - darwin_util + darwin_aarch64_util HDRS syscall.h DEPENDS diff --git a/libc/src/__support/OSUtil/darwin/exit.cpp b/libc/src/__support/OSUtil/darwin/exit.cpp new file mode 100644 index 0000000000000..7439db2ef38b0 --- /dev/null +++ b/libc/src/__support/OSUtil/darwin/exit.cpp @@ -0,0 +1,24 @@ +//===------------ MacOS implementation of an exit function ------*- C++ -*-===// +// +// 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 "src/__support/OSUtil/darwin/syscall.h" // syscall_impl +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "sys/syscall.h" // For syscall numbers. + +namespace LIBC_NAMESPACE_DECL { +namespace internal { + +[[noreturn]] void exit(int status) { + for (;;) { + LIBC_NAMESPACE::syscall_impl(SYS_exit, status); + } +} + +} // namespace internal +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/threads/darwin/CMakeLists.txt b/libc/src/__support/threads/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..2a7ce0676f68f --- /dev/null +++ b/libc/src/__support/threads/darwin/CMakeLists.txt @@ -0,0 +1,11 @@ +if(NOT TARGET libc.src.__support.OSUtil.osutil) + return() +endif() + +add_header_library( + mutex + HDRS + mutex.h + DEPENDS + libc.src.__support.threads.mutex_common +) diff --git a/libc/src/__support/threads/darwin/mutex.h b/libc/src/__support/threads/darwin/mutex.h new file mode 100644 index 0000000000000..f48803ebe1e7d --- /dev/null +++ b/libc/src/__support/threads/darwin/mutex.h @@ -0,0 +1,132 @@ +//===--- Implementation of a Darwin mutex class ------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_MUTEX_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_MUTEX_H + +#include "src/__support/libc_assert.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/mutex_common.h" +#include "src/__support/threads/sleep.h" // For sleep_briefly +#include "src/__support/time/linux/abs_timeout.h" + +#include // For mach_thread_self +#include // For mach_port_t and MACH_PORT_NULL +#include // For os_unfair_lock +#include // For clock_gettime + +namespace LIBC_NAMESPACE_DECL { + +// This file is an implementation of `LIBC_NAMESPACE::mutex` for Darwin-based +// platforms. It is a wrapper around `os_unfair_lock`, which is a low-level, +// high-performance locking primitive provided by the kernel. +// +// `os_unfair_lock` is a non-recursive, thread-owned lock that blocks waiters +// efficiently in the kernel. As the name implies, it is "unfair," meaning +// it does not guarantee the order in which waiting threads acquire the lock. +// This trade-off allows for higher performance in contended scenarios. +// +// The lock must be unlocked from the same thread that locked it. Attempting +// to unlock from a different thread will result in a runtime error. +// +// This implementation is suitable for simple critical sections where fairness +// and reentrancy are not concerns. + +class Mutex final { + os_unfair_lock_s lock_val = OS_UNFAIR_LOCK_INIT; + mach_port_t owner = MACH_PORT_NULL; + + // API compatibility fields. + unsigned char timed; + unsigned char recursive; + unsigned char robust; + unsigned char pshared; + +public: + LIBC_INLINE constexpr Mutex(bool is_timed, bool is_recursive, bool is_robust, + bool is_pshared) + : owner(MACH_PORT_NULL), timed(is_timed), recursive(is_recursive), + robust(is_robust), pshared(is_pshared) {} + + LIBC_INLINE constexpr Mutex() + : owner(MACH_PORT_NULL), timed(0), recursive(0), robust(0), pshared(0) {} + + LIBC_INLINE static MutexError init(Mutex *mutex, bool is_timed, bool is_recur, + bool is_robust, bool is_pshared) { + mutex->lock_val = OS_UNFAIR_LOCK_INIT; + mutex->owner = MACH_PORT_NULL; + mutex->timed = is_timed; + mutex->recursive = is_recur; + mutex->robust = is_robust; + mutex->pshared = is_pshared; + return MutexError::NONE; + } + + LIBC_INLINE static MutexError destroy(Mutex *lock) { + LIBC_ASSERT(lock->owner == MACH_PORT_NULL && + "Mutex destroyed while locked."); + return MutexError::NONE; + } + + LIBC_INLINE MutexError lock() { + os_unfair_lock_lock(&lock_val); + owner = mach_thread_self(); + return MutexError::NONE; + } + + LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) { + while (true) { + if (try_lock() == MutexError::NONE) { + return MutexError::NONE; + } + + // Manually check if the timeout has expired. + struct timespec now; + // The clock used here must match the clock used to create the + // absolute timeout. + clock_gettime(abs_time.is_realtime() ? CLOCK_REALTIME : CLOCK_MONOTONIC, + &now); + const timespec &target_ts = abs_time.get_timespec(); + + if (now.tv_sec > target_ts.tv_sec || (now.tv_sec == target_ts.tv_sec && + now.tv_nsec >= target_ts.tv_nsec)) { + // We might have acquired the lock between the last try_lock() and now. + // To avoid returning TIMEOUT incorrectly, we do one last try_lock(). + if (try_lock() == MutexError::NONE) + return MutexError::NONE; + return MutexError::TIMEOUT; + } + + sleep_briefly(); + } + } + + LIBC_INLINE MutexError unlock() { + // This check is crucial. It prevents both double-unlocks and unlocks + // by threads that do not own the mutex. + if (owner != mach_thread_self()) { + return MutexError::UNLOCK_WITHOUT_LOCK; + } + owner = MACH_PORT_NULL; + os_unfair_lock_unlock(&lock_val); + return MutexError::NONE; + } + + LIBC_INLINE MutexError try_lock() { + if (os_unfair_lock_trylock(&lock_val)) { + owner = mach_thread_self(); + return MutexError::NONE; + } + return MutexError::BUSY; + } +}; + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_MUTEX_H diff --git a/libc/src/__support/threads/mutex.h b/libc/src/__support/threads/mutex.h index f64f7e7b40082..e2401507d86ac 100644 --- a/libc/src/__support/threads/mutex.h +++ b/libc/src/__support/threads/mutex.h @@ -42,7 +42,9 @@ #if defined(__linux__) #include "src/__support/threads/linux/mutex.h" -#endif // __linux__ +#elif defined(__APPLE__) +#include "src/__support/threads/darwin/mutex.h" +#endif #elif LIBC_THREAD_MODE == LIBC_THREAD_MODE_SINGLE diff --git a/libc/src/__support/time/darwin/CMakeLists.txt b/libc/src/__support/time/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..a06a41289a41c --- /dev/null +++ b/libc/src/__support/time/darwin/CMakeLists.txt @@ -0,0 +1,12 @@ +add_object_library( + clock_gettime + SRCS + clock_gettime.cpp + HDRS + ../clock_gettime.h + DEPENDS + libc.src.__support.common + libc.src.__support.error_or + libc.hdr.types.struct_timeval + libc.hdr.types.struct_timespec +) diff --git a/libc/src/__support/time/darwin/clock_gettime.cpp b/libc/src/__support/time/darwin/clock_gettime.cpp new file mode 100644 index 0000000000000..158737a6251a0 --- /dev/null +++ b/libc/src/__support/time/darwin/clock_gettime.cpp @@ -0,0 +1,44 @@ +//===-- Darwin implementation of internal clock_gettime -------------------===// +// +// 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 "src/__support/time/clock_gettime.h" +#include "hdr/errno_macros.h" // For EINVAL +#include "hdr/time_macros.h" +#include "hdr/types/struct_timespec.h" +#include "hdr/types/struct_timeval.h" +#include "src/__support/OSUtil/syscall.h" // For syscall_impl +#include "src/__support/common.h" +#include "src/__support/error_or.h" +#include // For SYS_gettimeofday +#include // For struct timezone + +namespace LIBC_NAMESPACE_DECL { +namespace internal { + +ErrorOr clock_gettime(clockid_t clockid, struct timespec *ts) { + if (clockid != CLOCK_REALTIME) { + return Error(EINVAL); + } + struct timeval tv; + // The second argument to gettimeofday is a timezone pointer + // The third argument is mach_absolute_time + // Both of these, we don't need here, so they are 0 + long ret = LIBC_NAMESPACE::syscall_impl( + SYS_gettimeofday, reinterpret_cast(&tv), 0, 0); + if (ret != 0) { + // The syscall returns -1 on error and sets errno. + return Error(EINVAL); + } + + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return 0; +} + +} // namespace internal +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/darwin/CMakeLists.txt b/libc/src/time/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..6d68086c72584 --- /dev/null +++ b/libc/src/time/darwin/CMakeLists.txt @@ -0,0 +1,10 @@ +add_entrypoint_object( + clock_gettime + SRCS + clock_gettime.cpp + HDRS + # The public header is part of the parent directory's library. + DEPENDS + libc.src.__support.time.clock_gettime + libc.src.errno.errno +) diff --git a/libc/src/time/darwin/clock_gettime.cpp b/libc/src/time/darwin/clock_gettime.cpp new file mode 100644 index 0000000000000..f717d05451aca --- /dev/null +++ b/libc/src/time/darwin/clock_gettime.cpp @@ -0,0 +1,29 @@ +//===---------- Darwin implementation of the POSIX clock_gettime function +//--===// +// +// 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 "src/time/clock_gettime.h" + +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/__support/time/clock_gettime.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, clock_gettime, + (clockid_t clockid, struct timespec *ts)) { + auto result = internal::clock_gettime(clockid, ts); + if (!result.has_value()) { + libc_errno = result.error(); + return -1; + } + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt index 31d1e9dce8204..028e8e3a2ea67 100644 --- a/libc/test/UnitTest/CMakeLists.txt +++ b/libc/test/UnitTest/CMakeLists.txt @@ -83,7 +83,7 @@ add_unittest_framework_library( ) set(libc_death_test_srcs LibcDeathTestExecutors.cpp) -if(${LIBC_TARGET_OS} STREQUAL "linux") +if(${LIBC_TARGET_OS} STREQUAL "linux" OR ${LIBC_TARGET_OS} STREQUAL "darwin") list(APPEND libc_death_test_srcs ExecuteFunctionUnix.cpp) endif() diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp index c0e85c2144005..7c2eb7c6e887c 100644 --- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp +++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp @@ -57,9 +57,7 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) { } ::close(pipe_fds[1]); - struct pollfd poll_fd { - pipe_fds[0], 0, 0 - }; + struct pollfd poll_fd{pipe_fds[0], POLLIN, 0}; // No events requested so this call will only return after the timeout or if // the pipes peer was closed, signaling the process exited. if (::poll(&poll_fd, 1, timeout_ms) == -1) { diff --git a/libc/test/src/__support/threads/darwin/CMakeLists.txt b/libc/test/src/__support/threads/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..50b8259a9fde9 --- /dev/null +++ b/libc/test/src/__support/threads/darwin/CMakeLists.txt @@ -0,0 +1,9 @@ +add_libc_test( + mutex_test + SUITE + libc-support-threads-tests + SRCS + mutex_test.cpp + DEPENDS + libc.src.__support.threads.darwin.mutex +) diff --git a/libc/test/src/__support/threads/darwin/mutex_test.cpp b/libc/test/src/__support/threads/darwin/mutex_test.cpp new file mode 100644 index 0000000000000..e8b46fdb7ebe3 --- /dev/null +++ b/libc/test/src/__support/threads/darwin/mutex_test.cpp @@ -0,0 +1,78 @@ +//===-- Unittests for Darwin's Mutex ------------------------------------===// +// +// 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 "src/__support/threads/darwin/mutex.h" +#include "src/__support/threads/mutex_common.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) { + LIBC_NAMESPACE::Mutex mutex; + ASSERT_EQ(mutex.lock(), LIBC_NAMESPACE::MutexError::NONE); + ASSERT_EQ(mutex.unlock(), LIBC_NAMESPACE::MutexError::NONE); + ASSERT_EQ(mutex.try_lock(), LIBC_NAMESPACE::MutexError::NONE); + ASSERT_EQ(mutex.try_lock(), LIBC_NAMESPACE::MutexError::BUSY); + ASSERT_EQ(mutex.unlock(), LIBC_NAMESPACE::MutexError::NONE); + ASSERT_EQ(mutex.unlock(), LIBC_NAMESPACE::MutexError::UNLOCK_WITHOUT_LOCK); +} + +// 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)); +// } diff --git a/libc/test/src/__support/time/darwin/CMakeLists.txt b/libc/test/src/__support/time/darwin/CMakeLists.txt new file mode 100644 index 0000000000000..ee1247b354173 --- /dev/null +++ b/libc/test/src/__support/time/darwin/CMakeLists.txt @@ -0,0 +1,8 @@ +add_libc_test( + clock_gettime + SUITE libc-support-time-tests + SRCS clock_gettime.cpp + DEPENDS + libc.src.__support.CPP.expected + libc.src.__support.time.darwin.clock_gettime +) diff --git a/libc/test/src/__support/time/darwin/clock_gettime.cpp b/libc/test/src/__support/time/darwin/clock_gettime.cpp new file mode 100644 index 0000000000000..d593c5d02744a --- /dev/null +++ b/libc/test/src/__support/time/darwin/clock_gettime.cpp @@ -0,0 +1,20 @@ +//===-- unit tests for darwin's time utilities --------------------------===// +// +// 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 "src/__support/time/clock_gettime.h" +#include "src/__support/CPP/expected.h" +#include "test/UnitTest/Test.h" + +template +using expected = LIBC_NAMESPACE::cpp::expected; + +TEST(LlvmLibcSupportDarwinClockGetTime, BasicGetTime) { + struct timespec ts; + auto result = LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts); + ASSERT_TRUE(result.has_value()); +} diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt index 0eb373c3fa061..9765e42095aa5 100644 --- a/libc/test/src/stdlib/CMakeLists.txt +++ b/libc/test/src/stdlib/CMakeLists.txt @@ -397,6 +397,19 @@ if(LLVM_LIBC_FULL_BUILD) libc-stdlib-tests SRCS _Exit_test.cpp + DEPENDS + libc.src.__support.OSUtil.osutil + libc.src.stdlib._Exit + ) + + add_libc_test( + exit_test + # The EXPECT_EXITS test is only availible for unit tests. + UNIT_TEST_ONLY + SUITE + libc-stdlib-tests + SRCS + exit_test.cpp DEPENDS libc.src.stdlib._Exit libc.src.stdlib.exit diff --git a/libc/test/src/stdlib/_Exit_test.cpp b/libc/test/src/stdlib/_Exit_test.cpp index 333277dc01dca..57c432828c2f3 100644 --- a/libc/test/src/stdlib/_Exit_test.cpp +++ b/libc/test/src/stdlib/_Exit_test.cpp @@ -7,13 +7,9 @@ //===----------------------------------------------------------------------===// #include "src/stdlib/_Exit.h" -#include "src/stdlib/exit.h" #include "test/UnitTest/Test.h" TEST(LlvmLibcStdlib, _Exit) { EXPECT_EXITS([] { LIBC_NAMESPACE::_Exit(1); }, 1); EXPECT_EXITS([] { LIBC_NAMESPACE::_Exit(65); }, 65); - - EXPECT_EXITS([] { LIBC_NAMESPACE::exit(1); }, 1); - EXPECT_EXITS([] { LIBC_NAMESPACE::exit(65); }, 65); } diff --git a/libc/test/src/stdlib/exit_test.cpp b/libc/test/src/stdlib/exit_test.cpp new file mode 100644 index 0000000000000..5c82d8303036a --- /dev/null +++ b/libc/test/src/stdlib/exit_test.cpp @@ -0,0 +1,15 @@ +//===-- Unittests for exit -----------------------------------------------===// +// +// 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 "src/stdlib/exit.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcStdlib, exit) { + EXPECT_EXITS([] { LIBC_NAMESPACE::exit(1); }, 1); + EXPECT_EXITS([] { LIBC_NAMESPACE::exit(65); }, 65); +}