diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake index 619b53f828705..5caf840a4cfeb 100644 --- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake +++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake @@ -119,6 +119,10 @@ function(_get_compile_options_from_config output_var) list(APPEND config_options "-DLIBC_TRAP_ON_RAISE_FP_EXCEPT") endif() + if(LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT) + list(APPEND config_options "-DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}") + endif() + set(${output_var} ${config_options} PARENT_SCOPE) endfunction(_get_compile_options_from_config) diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h index 3652e448c7f5a..e4b2ab425a168 100644 --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -257,7 +257,15 @@ class File { return error_unlocked(); } + // TODO: https://github.com/llvm/llvm-project/issues/172302 + // MacOS defines clearerr_unlocked as a macro. While pre-processing, the + // identifier below is substituted for the definition in the SDK, which leads + // to compile time errors due to ill-formed statements. This is a workaround + // for the pre-processor. +#pragma push_macro("clearerr_unlocked") +#undef clearerr_unlocked void clearerr_unlocked() { err = false; } +#pragma pop_macro("clearerr_unlocked") void clearerr() { FileLock l(this); diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt index f8a44937721b4..dc1445634da2b 100644 --- a/libc/src/__support/threads/CMakeLists.txt +++ b/libc/src/__support/threads/CMakeLists.txt @@ -23,31 +23,54 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) add_subdirectory(${LIBC_TARGET_OS}) endif() -if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex) +if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils) + add_header_library( + raw_mutex + HDRS + raw_mutex.h + COMPILE_OPTIONS + ${monotonicity_flags} + DEPENDS + .${LIBC_TARGET_OS}.futex_utils + libc.src.__support.threads.sleep + libc.src.__support.time.abs_timeout + libc.src.__support.time.monotonicity + libc.src.__support.CPP.optional + libc.hdr.types.pid_t + ) + + add_header_library( + unix_mutex + HDRS + unix_mutex.h + DEPENDS + .raw_mutex + ) + add_header_library( mutex HDRS - mutex.h + mutex.h DEPENDS - .${LIBC_TARGET_OS}.mutex + .unix_mutex ) add_object_library( fork_callbacks SRCS - fork_callbacks.cpp + fork_callbacks.cpp HDRS - fork_callbacks.h + fork_callbacks.h DEPENDS - .mutex - libc.src.__support.CPP.mutex + .mutex + libc.src.__support.CPP.mutex ) elseif(NOT (LIBC_CONF_THREAD_MODE STREQUAL LIBC_THREAD_MODE_PLATFORM)) add_header_library( mutex - HDRS + HDRS mutex.h - DEPENDS + DEPENDS .mutex_common ) endif() diff --git a/libc/src/__support/threads/CndVar.h b/libc/src/__support/threads/CndVar.h index 7b2a7126ca09c..901b652c553d8 100644 --- a/libc/src/__support/threads/CndVar.h +++ b/libc/src/__support/threads/CndVar.h @@ -12,8 +12,8 @@ #include "hdr/stdint_proxy.h" // uint32_t #include "src/__support/macros/config.h" #include "src/__support/threads/linux/futex_utils.h" // Futex -#include "src/__support/threads/linux/raw_mutex.h" // RawMutex #include "src/__support/threads/mutex.h" // Mutex +#include "src/__support/threads/raw_mutex.h" // RawMutex 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..9c651d8c3b0f5 --- /dev/null +++ b/libc/src/__support/threads/darwin/CMakeLists.txt @@ -0,0 +1,16 @@ +if(NOT TARGET libc.src.__support.OSUtil.osutil) + return() +endif() + +add_header_library( + futex_utils + HDRS + futex_utils.h + DEPENDS + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.__support.CPP.atomic + libc.src.__support.CPP.limits + libc.src.__support.CPP.optional + libc.src.__support.threads.mutex_common +) diff --git a/libc/src/__support/threads/darwin/futex_utils.h b/libc/src/__support/threads/darwin/futex_utils.h new file mode 100644 index 0000000000000..65ac1ce0ab351 --- /dev/null +++ b/libc/src/__support/threads/darwin/futex_utils.h @@ -0,0 +1,79 @@ +//===--- Futex utils for Darwin ----------------------------------*- 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_FUTEX_UTILS_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H + +#include "src/__support/CPP/atomic.h" +#include "src/__support/CPP/optional.h" +#include "src/__support/time/abs_timeout.h" +#include "src/__support/time/clock_conversion.h" +#include "src/__support/time/units.h" + +#include + +namespace LIBC_NAMESPACE_DECL { + +using FutexWordType = uint32_t; + +struct Futex : public cpp::Atomic { + using cpp::Atomic::Atomic; + using Timeout = internal::AbsTimeout; + + LIBC_INLINE long wait(FutexWordType val, cpp::optional timeout, + bool /* is_shared */) { + // TODO(bojle): consider using OS_SYNC_WAIT_ON_ADDRESS_SHARED to sync + // betweeen processes. Catch: it is recommended to only be used by shared + // processes, not threads of a same process. + + for (;;) { + if (this->load(cpp::MemoryOrder::RELAXED) != val) + return 0; + long ret = 0; + if (timeout) { + // Assuming, OS_CLOCK_MACH_ABSOLUTE_TIME is equivalent to CLOCK_REALTIME + using namespace time_units; + uint64_t tnsec = timeout->get_timespec().tv_sec * 1_s_ns + + timeout->get_timespec().tv_nsec; + ret = os_sync_wait_on_address_with_timeout( + reinterpret_cast(this), static_cast(val), + sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE, + OS_CLOCK_MACH_ABSOLUTE_TIME, tnsec); + } else { + ret = os_sync_wait_on_address( + reinterpret_cast(this), static_cast(val), + sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE); + } + if ((ret < 0) && (errno == ETIMEDOUT)) + return -ETIMEDOUT; + // case when os_sync returns early with an error. retry. + if ((ret < 0) && ((errno == EINTR) || (errno == EFAULT))) { + continue; + } + return ret; + } + } + + LIBC_INLINE long notify_one(bool /* is_shared */) { + // TODO(bojle): deal with is_shared + return os_sync_wake_by_address_any(reinterpret_cast(this), + sizeof(FutexWordType), + OS_SYNC_WAKE_BY_ADDRESS_NONE); + } + + LIBC_INLINE long notify_all(bool /* is_shared */) { + // TODO(bojle): deal with is_shared + return os_sync_wake_by_address_all(reinterpret_cast(this), + sizeof(FutexWordType), + OS_SYNC_WAKE_BY_ADDRESS_NONE); + } +}; + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_DARWIN_FUTEX_UTILS_H diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt index cc596d217d7d2..200a3b1529fec 100644 --- a/libc/src/__support/threads/linux/CMakeLists.txt +++ b/libc/src/__support/threads/linux/CMakeLists.txt @@ -31,29 +31,13 @@ else() set(monotonicity_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY=0) endif() -add_header_library( - raw_mutex - HDRS - mutex.h - DEPENDS - .futex_utils - libc.src.__support.threads.sleep - libc.src.__support.time.abs_timeout - libc.src.__support.time.monotonicity - libc.src.__support.CPP.optional - libc.hdr.types.pid_t - COMPILE_OPTIONS - -DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT} - ${monotonicity_flags} -) - add_header_library( rwlock HDRS rwlock.h DEPENDS .futex_utils - .raw_mutex + libc.src.__support.threads.raw_mutex libc.src.__support.common libc.src.__support.OSUtil.osutil libc.src.__support.CPP.limits @@ -63,16 +47,6 @@ add_header_library( ${monotonicity_flags} ) -add_header_library( - mutex - HDRS - mutex.h - DEPENDS - .futex_utils - .raw_mutex - libc.src.__support.threads.mutex_common -) - add_object_library( thread SRCS @@ -119,7 +93,7 @@ add_object_library( libc.src.__support.OSUtil.osutil libc.src.__support.threads.linux.futex_word_type libc.src.__support.threads.mutex - libc.src.__support.threads.linux.raw_mutex + libc.src.__support.threads.raw_mutex libc.src.__support.CPP.mutex ) diff --git a/libc/src/__support/threads/linux/CndVar.cpp b/libc/src/__support/threads/linux/CndVar.cpp index be74c18dddf31..60424673e819c 100644 --- a/libc/src/__support/threads/linux/CndVar.cpp +++ b/libc/src/__support/threads/linux/CndVar.cpp @@ -8,11 +8,11 @@ #include "src/__support/threads/CndVar.h" #include "src/__support/CPP/mutex.h" -#include "src/__support/OSUtil/syscall.h" // syscall_impl +#include "src/__support/OSUtil/syscall.h" // syscall_impl #include "src/__support/macros/config.h" #include "src/__support/threads/linux/futex_word.h" // FutexWordType -#include "src/__support/threads/linux/raw_mutex.h" // RawMutex #include "src/__support/threads/mutex.h" // Mutex +#include "src/__support/threads/raw_mutex.h" // RawMutex #include // For syscall numbers. diff --git a/libc/src/__support/threads/linux/rwlock.h b/libc/src/__support/threads/linux/rwlock.h index 165e17239bbd5..9fb3ff972b588 100644 --- a/libc/src/__support/threads/linux/rwlock.h +++ b/libc/src/__support/threads/linux/rwlock.h @@ -22,7 +22,7 @@ #include "src/__support/threads/identifier.h" #include "src/__support/threads/linux/futex_utils.h" #include "src/__support/threads/linux/futex_word.h" -#include "src/__support/threads/linux/raw_mutex.h" +#include "src/__support/threads/raw_mutex.h" #include "src/__support/threads/sleep.h" #ifndef LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT diff --git a/libc/src/__support/threads/mutex.h b/libc/src/__support/threads/mutex.h index f64f7e7b40082..25feea891e429 100644 --- a/libc/src/__support/threads/mutex.h +++ b/libc/src/__support/threads/mutex.h @@ -40,9 +40,9 @@ // few global locks. So, to avoid static initialization order fiasco, we // want the constructors of the Mutex classes to be constexprs. -#if defined(__linux__) -#include "src/__support/threads/linux/mutex.h" -#endif // __linux__ +#if defined(__linux__) || defined(__APPLE__) +#include "src/__support/threads/unix_mutex.h" +#endif #elif LIBC_THREAD_MODE == LIBC_THREAD_MODE_SINGLE diff --git a/libc/src/__support/threads/linux/raw_mutex.h b/libc/src/__support/threads/raw_mutex.h similarity index 88% rename from libc/src/__support/threads/linux/raw_mutex.h rename to libc/src/__support/threads/raw_mutex.h index 94d6129bbf69b..e68469ad531cc 100644 --- a/libc/src/__support/threads/linux/raw_mutex.h +++ b/libc/src/__support/threads/raw_mutex.h @@ -1,28 +1,36 @@ -//===--- Implementation of a Linux RawMutex class ---------------*- C++ -*-===// +//===--- Implementation of the RawMutex 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_LINUX_RAW_MUTEX_H -#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RAW_MUTEX_H +#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_MUTEX_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_MUTEX_H +#include "hdr/errno_macros.h" #include "src/__support/CPP/optional.h" #include "src/__support/common.h" #include "src/__support/libc_assert.h" #include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" -#include "src/__support/threads/linux/futex_utils.h" -#include "src/__support/threads/linux/futex_word.h" #include "src/__support/threads/sleep.h" #include "src/__support/time/abs_timeout.h" +#include + +#if defined(__linux__) +#include "src/__support/threads/linux/futex_utils.h" +#elif defined(__APPLE__) +#include "src/__support/threads/darwin/futex_utils.h" +#endif + #ifndef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY #define LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY 1 #endif +// TODO(bojle): check this for darwin impl #if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY #include "src/__support/time/monotonicity.h" #endif @@ -93,7 +101,9 @@ class RawMutex { LIBC_INLINE void wake(bool is_pshared) { futex.notify_one(is_pshared); } public: - LIBC_INLINE static void init(RawMutex *mutex) { mutex->futex = UNLOCKED; } + LIBC_INLINE static void init(RawMutex *mutex) { + mutex->futex.store(UNLOCKED); + } LIBC_INLINE constexpr RawMutex() : futex(UNLOCKED) {} [[nodiscard]] LIBC_INLINE bool try_lock() { FutexWordType expected = UNLOCKED; @@ -122,8 +132,8 @@ class RawMutex { LIBC_ASSERT(lock->futex == UNLOCKED && "Mutex destroyed while used."); } LIBC_INLINE Futex &get_raw_futex() { return futex; } - LIBC_INLINE void reset() { futex = UNLOCKED; } + LIBC_INLINE void reset() { futex.store(UNLOCKED); } }; } // namespace LIBC_NAMESPACE_DECL -#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RAW_MUTEX_H +#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_RAW_MUTEX_H diff --git a/libc/src/__support/threads/linux/mutex.h b/libc/src/__support/threads/unix_mutex.h similarity index 90% rename from libc/src/__support/threads/linux/mutex.h rename to libc/src/__support/threads/unix_mutex.h index 0c4b1ae09af6f..626cee9d0913d 100644 --- a/libc/src/__support/threads/linux/mutex.h +++ b/libc/src/__support/threads/unix_mutex.h @@ -1,4 +1,4 @@ -//===--- Implementation of a Linux mutex class ------------------*- C++ -*-===// +//===--- Implementation of a Unix 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. @@ -6,16 +6,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_MUTEX_H -#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_MUTEX_H +#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_UNIX_MUTEX_H +#define LLVM_LIBC_SRC___SUPPORT_THREADS_UNIX_MUTEX_H #include "hdr/types/pid_t.h" #include "src/__support/CPP/optional.h" #include "src/__support/libc_assert.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/futex_utils.h" -#include "src/__support/threads/linux/raw_mutex.h" #include "src/__support/threads/mutex_common.h" +#include "src/__support/threads/raw_mutex.h" namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/threads/linux/CMakeLists.txt b/libc/src/threads/linux/CMakeLists.txt index 6c8e0845faf4c..3cbf2f85f3f9d 100644 --- a/libc/src/threads/linux/CMakeLists.txt +++ b/libc/src/threads/linux/CMakeLists.txt @@ -9,7 +9,7 @@ add_header_library( libc.src.__support.CPP.mutex libc.src.__support.OSUtil.osutil libc.src.__support.threads.mutex - libc.src.__support.threads.linux.raw_mutex + libc.src.__support.threads.raw_mutex libc.src.__support.threads.linux.futex_utils ) diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt index 251b009994ab5..b7414f2bea120 100644 --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -62,7 +62,7 @@ add_integration_test( libc.src.pthread.pthread_rwlockattr_destroy libc.src.pthread.pthread_rwlockattr_setpshared libc.src.pthread.pthread_rwlockattr_setkind_np - libc.src.__support.threads.linux.raw_mutex + libc.src.__support.threads.raw_mutex libc.src.stdio.printf libc.src.stdlib.getenv libc.src.sys.mman.mmap diff --git a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp index 205e9f74ea9a1..dddfb282ce167 100644 --- a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp +++ b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp @@ -12,8 +12,8 @@ #include "src/__support/CPP/new.h" #include "src/__support/OSUtil/syscall.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/linux/raw_mutex.h" #include "src/__support/threads/linux/rwlock.h" +#include "src/__support/threads/raw_mutex.h" #include "src/__support/threads/sleep.h" #include "src/pthread/pthread_create.h" #include "src/pthread/pthread_join.h" 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..4c399b4258d66 --- /dev/null +++ b/libc/test/src/__support/threads/darwin/CMakeLists.txt @@ -0,0 +1,11 @@ +add_libc_test( + mutex_test + SUITE + libc-support-threads-tests + SRCS + mutex_test.cpp + DEPENDS + libc.src.__support.threads.mutex + libc.src.__support.threads.darwin.futex_utils + libc.src.__support.time.darwin.clock_gettime +) 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..a9df97bc94ebb --- /dev/null +++ b/libc/test/src/__support/threads/darwin/mutex_test.cpp @@ -0,0 +1,46 @@ +//===-- 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/mutex.h" +#include "src/__support/threads/mutex_common.h" +#include "src/__support/threads/raw_mutex.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) { + LIBC_NAMESPACE::Mutex mutex(0, 0, 0, 0); + 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()); +} + +// TODO(bojle): merge threads test for darwin and linux into one after +// adding support for shared locks in darwin diff --git a/libc/test/src/__support/threads/linux/CMakeLists.txt b/libc/test/src/__support/threads/linux/CMakeLists.txt index 4299a5617b8ff..a660e7ceb4490 100644 --- a/libc/test/src/__support/threads/linux/CMakeLists.txt +++ b/libc/test/src/__support/threads/linux/CMakeLists.txt @@ -5,7 +5,7 @@ add_libc_test( SRCS raw_mutex_test.cpp DEPENDS - libc.src.__support.threads.linux.raw_mutex + libc.src.__support.threads.raw_mutex libc.src.sys.mman.mmap libc.src.sys.mman.munmap libc.src.stdlib.exit diff --git a/libc/test/src/__support/threads/linux/raw_mutex_test.cpp b/libc/test/src/__support/threads/linux/raw_mutex_test.cpp index dadc706421d06..6522ec75e619c 100644 --- a/libc/test/src/__support/threads/linux/raw_mutex_test.cpp +++ b/libc/test/src/__support/threads/linux/raw_mutex_test.cpp @@ -10,7 +10,7 @@ #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/raw_mutex.h" #include "src/__support/threads/sleep.h" #include "src/__support/time/clock_gettime.h" #include "src/stdlib/exit.h"