[libc-commits] [libc] [libc] Add Darwin mutex support via os_sync primitives (PR #167722)
Shreeyash Pandey via libc-commits
libc-commits at lists.llvm.org
Fri Nov 28 09:50:11 PST 2025
https://github.com/bojle updated https://github.com/llvm/llvm-project/pull/167722
>From b03f12115e6912bd39ad388b486257d83a540ae1 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Sat, 8 Nov 2025 23:04:03 +0530
Subject: [PATCH 01/10] [libc] Add clock_gettime for Darwin
This patch adds support for clock_gettime for Darwin. Darwin syscall
'gettimeofday' is used to query the time from the system.
Many headers in llvm-libc-types, namely clockid_t, struct_timespec,
struct_timeval, suseconds_t, time_t_32, time_t_64, are modified to include
header guards as Darwin has its own implementation of primitive types.
---
.../llvm-libc-macros/darwin/CMakeLists.txt | 5 +++
.../llvm-libc-macros/darwin/time-macros.h | 14 ++++++
libc/include/llvm-libc-macros/time-macros.h | 2 +
libc/include/llvm-libc-types/clockid_t.h | 6 +++
.../include/llvm-libc-types/struct_timespec.h | 6 +++
libc/include/llvm-libc-types/struct_timeval.h | 6 +++
libc/include/llvm-libc-types/suseconds_t.h | 6 +++
libc/include/llvm-libc-types/time_t_32.h | 6 +++
libc/include/llvm-libc-types/time_t_64.h | 6 +++
libc/src/__support/time/darwin/CMakeLists.txt | 12 +++++
.../__support/time/darwin/clock_gettime.cpp | 44 +++++++++++++++++++
libc/src/time/darwin/CMakeLists.txt | 10 +++++
libc/src/time/darwin/clock_gettime.cpp | 29 ++++++++++++
.../src/__support/time/darwin/CMakeLists.txt | 8 ++++
.../__support/time/darwin/clock_gettime.cpp | 20 +++++++++
15 files changed, 180 insertions(+)
create mode 100644 libc/include/llvm-libc-macros/darwin/CMakeLists.txt
create mode 100644 libc/include/llvm-libc-macros/darwin/time-macros.h
create mode 100644 libc/src/__support/time/darwin/CMakeLists.txt
create mode 100644 libc/src/__support/time/darwin/clock_gettime.cpp
create mode 100644 libc/src/time/darwin/CMakeLists.txt
create mode 100644 libc/src/time/darwin/clock_gettime.cpp
create mode 100644 libc/test/src/__support/time/darwin/CMakeLists.txt
create mode 100644 libc/test/src/__support/time/darwin/clock_gettime.cpp
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 <sys/_types/_timespec.h>
+#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 <sys/_types/_timeval.h>
+#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 <sys/_types/_suseconds_t.h>
+#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 <sys/_types/_time_t.h>
+#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 <sys/_types/_time_t.h>
+#else
typedef __INT64_TYPE__ time_t;
+#endif // __APPLE__
#endif // LLVM_LIBC_TYPES_TIME_T_64_H
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 <sys/syscall.h> // For SYS_gettimeofday
+#include <sys/time.h> // For struct timezone
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+ErrorOr<int> 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<long>(
+ SYS_gettimeofday, reinterpret_cast<long>(&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/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 <class T, class E>
+using expected = LIBC_NAMESPACE::cpp::expected<T, E>;
+
+TEST(LlvmLibcSupportDarwinClockGetTime, BasicGetTime) {
+ struct timespec ts;
+ auto result = LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts);
+ ASSERT_TRUE(result.has_value());
+}
>From 2d5ba2f69fd7a431d4b2fe45fc003c1d0b469c44 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Tue, 11 Nov 2025 19:56:55 +0530
Subject: [PATCH 02/10] [libc] fix minor nits
---
libc/src/__support/OSUtil/darwin/exit.cpp | 3 +--
libc/src/__support/time/darwin/clock_gettime.cpp | 6 ++----
libc/src/time/darwin/clock_gettime.cpp | 5 ++---
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/libc/src/__support/OSUtil/darwin/exit.cpp b/libc/src/__support/OSUtil/darwin/exit.cpp
index 7439db2ef38b0..a5fa4a7522189 100644
--- a/libc/src/__support/OSUtil/darwin/exit.cpp
+++ b/libc/src/__support/OSUtil/darwin/exit.cpp
@@ -15,9 +15,8 @@ namespace LIBC_NAMESPACE_DECL {
namespace internal {
[[noreturn]] void exit(int status) {
- for (;;) {
+ for (;;)
LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, status);
- }
}
} // namespace internal
diff --git a/libc/src/__support/time/darwin/clock_gettime.cpp b/libc/src/__support/time/darwin/clock_gettime.cpp
index 158737a6251a0..aa483aa9a01d2 100644
--- a/libc/src/__support/time/darwin/clock_gettime.cpp
+++ b/libc/src/__support/time/darwin/clock_gettime.cpp
@@ -21,19 +21,17 @@ namespace LIBC_NAMESPACE_DECL {
namespace internal {
ErrorOr<int> clock_gettime(clockid_t clockid, struct timespec *ts) {
- if (clockid != CLOCK_REALTIME) {
+ 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<long>(
SYS_gettimeofday, reinterpret_cast<long>(&tv), 0, 0);
- if (ret != 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;
diff --git a/libc/src/time/darwin/clock_gettime.cpp b/libc/src/time/darwin/clock_gettime.cpp
index f717d05451aca..ecf116bbc5521 100644
--- a/libc/src/time/darwin/clock_gettime.cpp
+++ b/libc/src/time/darwin/clock_gettime.cpp
@@ -1,11 +1,10 @@
-//===---------- Darwin implementation of the POSIX clock_gettime function
-//--===//
+//===---- 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"
>From f51a80ea55b6db9317f4332f464779aaaaf38342 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Sat, 8 Nov 2025 23:22:33 +0530
Subject: [PATCH 03/10] [libc][darwin] add basic mutex support for darwin
This patch adds a basic implementation/wrapper for mutex using the underlying
os_unfair_lock API that macos provides.
---
.../__support/threads/darwin/CMakeLists.txt | 11 ++
libc/src/__support/threads/darwin/mutex.h | 132 ++++++++++++++++++
libc/src/__support/threads/mutex.h | 4 +-
.../__support/threads/darwin/CMakeLists.txt | 9 ++
.../__support/threads/darwin/mutex_test.cpp | 78 +++++++++++
5 files changed, 233 insertions(+), 1 deletion(-)
create mode 100644 libc/src/__support/threads/darwin/CMakeLists.txt
create mode 100644 libc/src/__support/threads/darwin/mutex.h
create mode 100644 libc/test/src/__support/threads/darwin/CMakeLists.txt
create mode 100644 libc/test/src/__support/threads/darwin/mutex_test.cpp
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 <mach/mach_init.h> // For mach_thread_self
+#include <mach/mach_port.h> // For mach_port_t and MACH_PORT_NULL
+#include <os/lock.h> // For os_unfair_lock
+#include <time.h> // 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/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<size_t> 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<SharedData *>(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<long>(SYS_fork);
+// #elif defined(SYS_clone)
+// long pid = LIBC_NAMESPACE::syscall_impl<long>(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));
+// }
>From ab6263003dd1a0169c0278286e6ed8e0a3d7b883 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Wed, 12 Nov 2025 16:28:34 +0530
Subject: [PATCH 04/10] [libc] make clock_conversion.h common and document it
clock_conversion.h implements convert_clock which shifts a timestamp from
one clock domain to another. It naturally does not depend on any OS specific
interface. Making it generic will allow common use.
---
.../time/{linux => }/clock_conversion.h | 26 +++++++++++++++++--
libc/src/__support/time/linux/monotonicity.h | 2 +-
2 files changed, 25 insertions(+), 3 deletions(-)
rename libc/src/__support/time/{linux => }/clock_conversion.h (54%)
diff --git a/libc/src/__support/time/linux/clock_conversion.h b/libc/src/__support/time/clock_conversion.h
similarity index 54%
rename from libc/src/__support/time/linux/clock_conversion.h
rename to libc/src/__support/time/clock_conversion.h
index ac5357d308d7c..acf0b21516467 100644
--- a/libc/src/__support/time/linux/clock_conversion.h
+++ b/libc/src/__support/time/clock_conversion.h
@@ -1,10 +1,10 @@
-//===--- clock conversion linux implementation ------------------*- C++ -*-===//
+//===--- clock conversion implementation ------------------*- 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_TIME_LINUX_CLOCK_CONVERSION_H
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
@@ -16,6 +16,28 @@
namespace LIBC_NAMESPACE_DECL {
namespace internal {
+/**
+ * @brief Convert a timespec value from one clock domain to another.
+ *
+ * The function takes a timestamp that is expressed in terms of the clock
+ * identified by param from and returns an equivalent timestamp expressed
+ * in terms of the clock identified by param to.
+ *
+ * Internally it obtains the current time of both clocks with
+ * clock_gettime, then subtracts the source clock’s value and
+ * adds the target clock’s value. The result is normalised so that
+ * the nanoseconds field is always in the range [0, 1 s).
+ *
+ * This is useful, for example, for converting a value obtained from
+ * CLOCK_MONOTONIC to CLOCK_REALTIME (or vice‑versa) so that the
+ * timestamp can be displayed to a user or stored in a format that
+ * is independent of the original clock domain.
+ *
+ * @param input The timestamp to convert
+ * @param from Clock ID of the original timestamp (e.g. CLOCK_MONOTONIC).
+ * @param to Clock ID of the desired timestamp (e.g. CLOCK_REALTIME).
+ * @return The converted timespec
+ */
LIBC_INLINE timespec convert_clock(timespec input, clockid_t from,
clockid_t to) {
using namespace time_units;
diff --git a/libc/src/__support/time/linux/monotonicity.h b/libc/src/__support/time/linux/monotonicity.h
index c7234db2e64c4..2ee260ee44ba9 100644
--- a/libc/src/__support/time/linux/monotonicity.h
+++ b/libc/src/__support/time/linux/monotonicity.h
@@ -12,8 +12,8 @@
#include "hdr/time_macros.h"
#include "src/__support/libc_assert.h"
#include "src/__support/macros/config.h"
+#include "src/__support/time/clock_conversion.h"
#include "src/__support/time/linux/abs_timeout.h"
-#include "src/__support/time/linux/clock_conversion.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
// This function is separated from abs_timeout.
>From fb9e1d2c1e1128d5e6c8bb109d8372355a2c0945 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Wed, 12 Nov 2025 16:34:32 +0530
Subject: [PATCH 05/10] [libc] add darwin errno support
Adds support for darwin's errno headers/macros by including it from
<sys/errno.h> and modifying header guards in hdr/ and include/.
---
libc/hdr/errno_macros.h | 4 +++-
libc/include/errno.h.def | 8 +++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/libc/hdr/errno_macros.h b/libc/hdr/errno_macros.h
index 27ea49977d8c8..ce3453d16bce1 100644
--- a/libc/hdr/errno_macros.h
+++ b/libc/hdr/errno_macros.h
@@ -15,7 +15,9 @@
#include <linux/errno.h>
#include "include/llvm-libc-macros/error-number-macros.h"
-#else // __linux__
+#elif __APPLE__
+#include <sys/errno.h>
+#else // __APPLE__
#include "include/llvm-libc-macros/generic-error-number-macros.h"
#endif
diff --git a/libc/include/errno.h.def b/libc/include/errno.h.def
index aa1f6c9e48444..ba9864439369a 100644
--- a/libc/include/errno.h.def
+++ b/libc/include/errno.h.def
@@ -21,8 +21,14 @@
#include "llvm-libc-macros/linux/error-number-macros.h"
-#else // __linux__
+#elif __APPLE__
+
+#include <sys/errno.h>
+
+#else // __APPLE__
+
#include "llvm-libc-macros/generic-error-number-macros.h"
+
#endif
__BEGIN_C_DECLS
>From fcb81a33cb8eb21a1f3fbb37f54180ad63957611 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Wed, 12 Nov 2025 21:08:37 +0530
Subject: [PATCH 06/10] [libc] move abs_timesout and monotonicity out of linux
dir
---
.../src/__support/threads/linux/futex_utils.h | 2 +-
libc/src/__support/threads/linux/rwlock.h | 2 +-
libc/src/__support/time/CMakeLists.txt | 29 +++++++++
.../__support/time/{linux => }/abs_timeout.h | 10 +--
libc/src/__support/time/linux/CMakeLists.txt | 29 ---------
.../__support/time/{linux => }/monotonicity.h | 12 ++--
.../pthread/pthread_rwlock_clockwrlock.cpp | 2 +-
.../pthread/pthread_rwlock_timedrdlock.cpp | 2 +-
.../pthread/pthread_rwlock_timedwrlock.cpp | 2 +-
.../__support/threads/darwin/CMakeLists.txt | 4 +-
.../__support/threads/darwin/mutex_test.cpp | 62 ++-----------------
11 files changed, 52 insertions(+), 104 deletions(-)
rename libc/src/__support/time/{linux => }/abs_timeout.h (85%)
rename libc/src/__support/time/{linux => }/monotonicity.h (82%)
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index 943a99ab38c8c..e1cfa36566555 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -16,7 +16,7 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/futex_word.h"
-#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/abs_timeout.h"
#include <linux/errno.h>
#include <linux/futex.h>
diff --git a/libc/src/__support/threads/linux/rwlock.h b/libc/src/__support/threads/linux/rwlock.h
index f7aeb5b709aa3..165e17239bbd5 100644
--- a/libc/src/__support/threads/linux/rwlock.h
+++ b/libc/src/__support/threads/linux/rwlock.h
@@ -35,7 +35,7 @@
#endif
#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
-#include "src/__support/time/linux/monotonicity.h"
+#include "src/__support/time/monotonicity.h"
#endif
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/__support/time/CMakeLists.txt b/libc/src/__support/time/CMakeLists.txt
index 3851037e4161f..b90f977b00fe5 100644
--- a/libc/src/__support/time/CMakeLists.txt
+++ b/libc/src/__support/time/CMakeLists.txt
@@ -28,3 +28,32 @@ if(TARGET libc.src.__support.time.${LIBC_TARGET_OS}.clock_settime)
libc.src.__support.time.${LIBC_TARGET_OS}.clock_settime
)
endif()
+
+add_header_library(
+ abs_timeout
+ HDRS
+ abs_timeout.h
+ DEPENDS
+ libc.hdr.types.struct_timespec
+ libc.src.__support.time.units
+ libc.src.__support.CPP.expected
+)
+
+add_header_library(
+ clock_conversion
+ HDRS
+ clock_conversion.h
+ DEPENDS
+ .clock_gettime
+ libc.src.__support.time.units
+)
+
+add_header_library(
+ monotonicity
+ HDRS
+ monotonicity.h
+ DEPENDS
+ .clock_conversion
+ .abs_timeout
+ libc.hdr.time_macros
+)
diff --git a/libc/src/__support/time/linux/abs_timeout.h b/libc/src/__support/time/abs_timeout.h
similarity index 85%
rename from libc/src/__support/time/linux/abs_timeout.h
rename to libc/src/__support/time/abs_timeout.h
index 37e602672208f..23ad52f530545 100644
--- a/libc/src/__support/time/linux/abs_timeout.h
+++ b/libc/src/__support/time/abs_timeout.h
@@ -1,13 +1,13 @@
-//===--- Linux absolute timeout ---------------------------------*- C++ -*-===//
+//===--- absolute timeout ---------------------------------*- 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_TIME_LINUX_ABS_TIMEOUT_H
-#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
+#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_ABS_TIMEOUT_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_ABS_TIMEOUT_H
#include "hdr/time_macros.h"
#include "hdr/types/struct_timespec.h"
@@ -47,4 +47,4 @@ class AbsTimeout {
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
-#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_ABS_TIMEOUT_H
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_ABS_TIMEOUT_H
diff --git a/libc/src/__support/time/linux/CMakeLists.txt b/libc/src/__support/time/linux/CMakeLists.txt
index 478529502b403..a7bdb7b36be49 100644
--- a/libc/src/__support/time/linux/CMakeLists.txt
+++ b/libc/src/__support/time/linux/CMakeLists.txt
@@ -28,32 +28,3 @@ add_object_library(
libc.src.__support.error_or
libc.src.__support.OSUtil.osutil
)
-
-add_header_library(
- clock_conversion
- HDRS
- clock_conversion.h
- DEPENDS
- .clock_gettime
- libc.src.__support.time.units
-)
-
-add_header_library(
- abs_timeout
- HDRS
- abs_timeout.h
- DEPENDS
- libc.hdr.types.struct_timespec
- libc.src.__support.time.units
- libc.src.__support.CPP.expected
-)
-
-add_header_library(
- monotonicity
- HDRS
- monotonicity.h
- DEPENDS
- .clock_conversion
- .abs_timeout
- libc.hdr.time_macros
-)
diff --git a/libc/src/__support/time/linux/monotonicity.h b/libc/src/__support/time/monotonicity.h
similarity index 82%
rename from libc/src/__support/time/linux/monotonicity.h
rename to libc/src/__support/time/monotonicity.h
index 2ee260ee44ba9..4fee76da2a62e 100644
--- a/libc/src/__support/time/linux/monotonicity.h
+++ b/libc/src/__support/time/monotonicity.h
@@ -1,19 +1,19 @@
-//===--- timeout linux implementation ---------------------------*- C++ -*-===//
+//===--- timeout implementation ---------------------------*- 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_TIME_LINUX_MONOTONICITY_H
-#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_MONOTONICITY_H
+#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_MONOTONICITY_H
+#define LLVM_LIBC_SRC___SUPPORT_TIME_MONOTONICITY_H
#include "hdr/time_macros.h"
#include "src/__support/libc_assert.h"
#include "src/__support/macros/config.h"
+#include "src/__support/time/abs_timeout.h"
#include "src/__support/time/clock_conversion.h"
-#include "src/__support/time/linux/abs_timeout.h"
namespace LIBC_NAMESPACE_DECL {
namespace internal {
// This function is separated from abs_timeout.
@@ -41,4 +41,4 @@ LIBC_INLINE void ensure_monotonicity(AbsTimeout &timeout) {
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
-#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_MONOTONICITY_H
+#endif // LLVM_LIBC_SRC___SUPPORT_TIME_MONOTONICITY_H
diff --git a/libc/src/pthread/pthread_rwlock_clockwrlock.cpp b/libc/src/pthread/pthread_rwlock_clockwrlock.cpp
index 8f58c7f24bb10..787a1b1484df7 100644
--- a/libc/src/pthread/pthread_rwlock_clockwrlock.cpp
+++ b/libc/src/pthread/pthread_rwlock_clockwrlock.cpp
@@ -12,7 +12,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/rwlock.h"
-#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/abs_timeout.h"
#include <pthread.h>
diff --git a/libc/src/pthread/pthread_rwlock_timedrdlock.cpp b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp
index fcddfed224906..745da508cf140 100644
--- a/libc/src/pthread/pthread_rwlock_timedrdlock.cpp
+++ b/libc/src/pthread/pthread_rwlock_timedrdlock.cpp
@@ -13,7 +13,7 @@
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/threads/linux/rwlock.h"
-#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/abs_timeout.h"
#include <pthread.h>
diff --git a/libc/src/pthread/pthread_rwlock_timedwrlock.cpp b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp
index d2dc70e992e82..9666fc5b47284 100644
--- a/libc/src/pthread/pthread_rwlock_timedwrlock.cpp
+++ b/libc/src/pthread/pthread_rwlock_timedwrlock.cpp
@@ -13,7 +13,7 @@
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/threads/linux/rwlock.h"
-#include "src/__support/time/linux/abs_timeout.h"
+#include "src/__support/time/abs_timeout.h"
#include <pthread.h>
diff --git a/libc/test/src/__support/threads/darwin/CMakeLists.txt b/libc/test/src/__support/threads/darwin/CMakeLists.txt
index 50b8259a9fde9..4c399b4258d66 100644
--- a/libc/test/src/__support/threads/darwin/CMakeLists.txt
+++ b/libc/test/src/__support/threads/darwin/CMakeLists.txt
@@ -5,5 +5,7 @@ add_libc_test(
SRCS
mutex_test.cpp
DEPENDS
- libc.src.__support.threads.darwin.mutex
+ 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
index e8b46fdb7ebe3..399942b9b5a04 100644
--- a/libc/test/src/__support/threads/darwin/mutex_test.cpp
+++ b/libc/test/src/__support/threads/darwin/mutex_test.cpp
@@ -6,12 +6,13 @@
//
//===----------------------------------------------------------------------===//
-#include "src/__support/threads/darwin/mutex.h"
+#include "src/__support/threads/mutex.h"
+#include "src/__support/threads/raw_mutex.h"
#include "src/__support/threads/mutex_common.h"
#include "test/UnitTest/Test.h"
TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) {
- LIBC_NAMESPACE::Mutex mutex;
+ 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);
@@ -20,59 +21,4 @@ TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) {
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<size_t> 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<SharedData *>(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<long>(SYS_fork);
-// #elif defined(SYS_clone)
-// long pid = LIBC_NAMESPACE::syscall_impl<long>(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));
-// }
+// TODO(bojle): add other tests a la linux
>From 19c7023dba91e32989e05ce643a368fa9a86ac09 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Fri, 14 Nov 2025 18:13:33 +0530
Subject: [PATCH 07/10] [libc] formatting
---
libc/test/src/__support/threads/darwin/mutex_test.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libc/test/src/__support/threads/darwin/mutex_test.cpp b/libc/test/src/__support/threads/darwin/mutex_test.cpp
index 399942b9b5a04..f135aaf66b001 100644
--- a/libc/test/src/__support/threads/darwin/mutex_test.cpp
+++ b/libc/test/src/__support/threads/darwin/mutex_test.cpp
@@ -7,12 +7,13 @@
//===----------------------------------------------------------------------===//
#include "src/__support/threads/mutex.h"
-#include "src/__support/threads/raw_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);;
+ 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);
>From b8d7e50f55b2d111d0230ecba75e6933cdd1e022 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Fri, 14 Nov 2025 20:29:49 +0530
Subject: [PATCH 08/10] [libc] linux/abs_timeout.h -> abs_timesout.h
---
libc/src/__support/threads/darwin/mutex.h | 2 +-
libc/src/__support/threads/linux/raw_mutex.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/threads/darwin/mutex.h b/libc/src/__support/threads/darwin/mutex.h
index f48803ebe1e7d..1ad09ba1cdf49 100644
--- a/libc/src/__support/threads/darwin/mutex.h
+++ b/libc/src/__support/threads/darwin/mutex.h
@@ -14,7 +14,7 @@
#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 "src/__support/time/abs_timeout.h"
#include <mach/mach_init.h> // For mach_thread_self
#include <mach/mach_port.h> // For mach_port_t and MACH_PORT_NULL
diff --git a/libc/src/__support/threads/linux/raw_mutex.h b/libc/src/__support/threads/linux/raw_mutex.h
index 47f0aa70f1c46..8f69da2ceab07 100644
--- a/libc/src/__support/threads/linux/raw_mutex.h
+++ b/libc/src/__support/threads/linux/raw_mutex.h
@@ -17,7 +17,7 @@
#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/linux/abs_timeout.h"
+#include "src/__support/time/abs_timeout.h"
#ifndef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
#define LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY 1
>From ce329c416a6ebaaebdca3e84b34fa1ad77e33c3c Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Wed, 12 Nov 2025 21:16:22 +0530
Subject: [PATCH 09/10] [libc] Add Darwin mutex support via os_sync primitives
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch implements the generic mutex and raw_mutex interfaces on macOS.
A new Futex class is provided that relies on os_sync_wait and
os_sync_wake to emulate futex‑like wait and wake semantics.
The OS‑specific part is moved into futex_utils, which now contains the
Darwin implementation.
---
libc/src/__support/threads/CMakeLists.txt | 29 +++++++-
.../__support/threads/darwin/CMakeLists.txt | 9 ++-
.../__support/threads/darwin/futex_utils.h | 66 +++++++++++++++++++
libc/src/__support/threads/darwin/mutex.h | 5 +-
.../__support/threads/linux/CMakeLists.txt | 6 +-
libc/src/__support/threads/mutex.h | 6 +-
.../__support/threads/{linux => }/raw_mutex.h | 26 +++++---
.../threads/{linux/mutex.h => unix_mutex.h} | 9 ++-
8 files changed, 127 insertions(+), 29 deletions(-)
create mode 100644 libc/src/__support/threads/darwin/futex_utils.h
rename libc/src/__support/threads/{linux => }/raw_mutex.h (89%)
rename libc/src/__support/threads/{linux/mutex.h => unix_mutex.h} (90%)
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index f8a44937721b4..b2abf19c53a50 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -23,13 +23,36 @@ 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(
mutex
HDRS
- mutex.h
+ mutex.h
+ DEPENDS
+ .unix_mutex
+ )
+ add_header_library(
+ unix_mutex
+ HDRS
+ unix_mutex.h
+ DEPENDS
+ .raw_mutex
+ )
+
+ add_header_library(
+ raw_mutex
+ HDRS
+ raw_mutex.h
DEPENDS
- .${LIBC_TARGET_OS}.mutex
+ .${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
+ COMPILE_OPTIONS
+ -DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}
+ ${monotonicity_flags}
)
add_object_library(
diff --git a/libc/src/__support/threads/darwin/CMakeLists.txt b/libc/src/__support/threads/darwin/CMakeLists.txt
index 2a7ce0676f68f..9c651d8c3b0f5 100644
--- a/libc/src/__support/threads/darwin/CMakeLists.txt
+++ b/libc/src/__support/threads/darwin/CMakeLists.txt
@@ -3,9 +3,14 @@ if(NOT TARGET libc.src.__support.OSUtil.osutil)
endif()
add_header_library(
- mutex
+ futex_utils
HDRS
- mutex.h
+ 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..180083b7b2c68
--- /dev/null
+++ b/libc/src/__support/threads/darwin/futex_utils.h
@@ -0,0 +1,66 @@
+//===--- 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 <os/os_sync_wait_on_address.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+using FutexWordType = uint32_t;
+
+struct Futex : public cpp::Atomic<FutexWordType> {
+ using cpp::Atomic<FutexWordType>::Atomic;
+ using Timeout = internal::AbsTimeout;
+
+ // The Darwin futex API does not return a value on timeout, so we have to
+ // check for it manually. This means we can't use the return value to
+ // distinguish between a timeout and a successful wake-up.
+ int wait(FutexWordType val, cpp::optional<Timeout> timeout, bool) {
+ if (timeout) {
+ struct timespec now;
+ clock_gettime(timeout->is_realtime() ? CLOCK_REALTIME : CLOCK_MONOTONIC,
+ &now);
+ const timespec &target_ts = timeout->get_timespec();
+
+ if (now.tv_sec > target_ts.tv_sec ||
+ (now.tv_sec == target_ts.tv_sec && now.tv_nsec >= target_ts.tv_nsec))
+ return ETIMEDOUT;
+ }
+
+ os_sync_wait_on_address(reinterpret_cast<void *>(this),
+ static_cast<uint64_t>(val), sizeof(FutexWordType),
+ OS_SYNC_WAIT_ON_ADDRESS_NONE);
+ return 0;
+ }
+
+ void notify_one(bool) {
+ os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
+ sizeof(FutexWordType),
+ OS_SYNC_WAKE_BY_ADDRESS_NONE);
+ }
+
+ void notify_all(bool) {
+ // os_sync_wake_by_address_all is not available, so we use notify_one.
+ // This is not ideal, but it's the best we can do with the available API.
+ os_sync_wake_by_address_any(reinterpret_cast<void *>(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/darwin/mutex.h b/libc/src/__support/threads/darwin/mutex.h
index 1ad09ba1cdf49..44ba14cc986a2 100644
--- a/libc/src/__support/threads/darwin/mutex.h
+++ b/libc/src/__support/threads/darwin/mutex.h
@@ -1,11 +1,10 @@
-//===--- Implementation of a Darwin mutex class ------------------*- C++
-//-*-===//
+//===--- 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
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 39d2c6fef0fed..cc596d217d7d2 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -21,7 +21,7 @@ add_header_library(
libc.src.__support.CPP.atomic
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
- libc.src.__support.time.linux.abs_timeout
+ libc.src.__support.time.abs_timeout
)
set(monotonicity_flags)
@@ -38,8 +38,8 @@ add_header_library(
DEPENDS
.futex_utils
libc.src.__support.threads.sleep
- libc.src.__support.time.linux.abs_timeout
- libc.src.__support.time.linux.monotonicity
+ libc.src.__support.time.abs_timeout
+ libc.src.__support.time.monotonicity
libc.src.__support.CPP.optional
libc.hdr.types.pid_t
COMPILE_OPTIONS
diff --git a/libc/src/__support/threads/mutex.h b/libc/src/__support/threads/mutex.h
index e2401507d86ac..25feea891e429 100644
--- a/libc/src/__support/threads/mutex.h
+++ b/libc/src/__support/threads/mutex.h
@@ -40,10 +40,8 @@
// 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"
-#elif defined(__APPLE__)
-#include "src/__support/threads/darwin/mutex.h"
+#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 89%
rename from libc/src/__support/threads/linux/raw_mutex.h
rename to libc/src/__support/threads/raw_mutex.h
index 8f69da2ceab07..fb3f5c0f70ed6 100644
--- a/libc/src/__support/threads/linux/raw_mutex.h
+++ b/libc/src/__support/threads/raw_mutex.h
@@ -1,12 +1,12 @@
-//===--- 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 "src/__support/CPP/optional.h"
#include "src/__support/common.h"
@@ -14,17 +14,23 @@
#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 "sys/errno.h"
+
+#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/linux/monotonicity.h"
+#include "src/__support/time/monotonicity.h"
#endif
#ifndef LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT
@@ -93,7 +99,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,7 +130,7 @@ 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
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..31acd582d4c6c 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 {
>From ec57b3169779b894134fe305b35c89f6875696c6 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shreeyash335 at gmail.com>
Date: Fri, 28 Nov 2025 23:19:18 +0530
Subject: [PATCH 10/10] [libc][darwin] use os_sync_*_timeouts and clear tests
---
.../__support/threads/darwin/futex_utils.h | 72 +++++++++++--------
libc/src/__support/threads/raw_mutex.h | 2 +
.../__support/threads/darwin/mutex_test.cpp | 22 +++++-
3 files changed, 65 insertions(+), 31 deletions(-)
diff --git a/libc/src/__support/threads/darwin/futex_utils.h b/libc/src/__support/threads/darwin/futex_utils.h
index 180083b7b2c68..380be69b2a66a 100644
--- a/libc/src/__support/threads/darwin/futex_utils.h
+++ b/libc/src/__support/threads/darwin/futex_utils.h
@@ -1,11 +1,10 @@
-//===--- Futex utils for Darwin -----------------------------------*- C++
-//-*-===//
+//===--- 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
@@ -25,39 +24,52 @@ struct Futex : public cpp::Atomic<FutexWordType> {
using cpp::Atomic<FutexWordType>::Atomic;
using Timeout = internal::AbsTimeout;
- // The Darwin futex API does not return a value on timeout, so we have to
- // check for it manually. This means we can't use the return value to
- // distinguish between a timeout and a successful wake-up.
- int wait(FutexWordType val, cpp::optional<Timeout> timeout, bool) {
- if (timeout) {
- struct timespec now;
- clock_gettime(timeout->is_realtime() ? CLOCK_REALTIME : CLOCK_MONOTONIC,
- &now);
- const timespec &target_ts = timeout->get_timespec();
+ LIBC_INLINE long wait(FutexWordType val, cpp::optional<Timeout> 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.
- if (now.tv_sec > target_ts.tv_sec ||
- (now.tv_sec == target_ts.tv_sec && now.tv_nsec >= target_ts.tv_nsec))
- return ETIMEDOUT;
+ 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
+ uint64_t tnsec = timeout->get_timespec().tv_sec * 1000000000 +
+ timeout->get_timespec().tv_nsec;
+ ret = os_sync_wait_on_address_with_timeout(
+ reinterpret_cast<void *>(this), static_cast<uint64_t>(val),
+ sizeof(FutexWordType), OS_SYNC_WAIT_ON_ADDRESS_NONE,
+ OS_CLOCK_MACH_ABSOLUTE_TIME, tnsec);
+ } else {
+ ret = os_sync_wait_on_address(
+ reinterpret_cast<void *>(this), static_cast<uint64_t>(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;
}
-
- os_sync_wait_on_address(reinterpret_cast<void *>(this),
- static_cast<uint64_t>(val), sizeof(FutexWordType),
- OS_SYNC_WAIT_ON_ADDRESS_NONE);
- return 0;
}
- void notify_one(bool) {
- os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
- sizeof(FutexWordType),
- OS_SYNC_WAKE_BY_ADDRESS_NONE);
+ LIBC_INLINE long notify_one(bool /* is_shared */) {
+ // TODO(bojle): deal with is_shared
+ return os_sync_wake_by_address_any(reinterpret_cast<void *>(this),
+ sizeof(FutexWordType),
+ OS_SYNC_WAKE_BY_ADDRESS_NONE);
}
- void notify_all(bool) {
- // os_sync_wake_by_address_all is not available, so we use notify_one.
- // This is not ideal, but it's the best we can do with the available API.
- os_sync_wake_by_address_any(reinterpret_cast<void *>(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<void *>(this),
+ sizeof(FutexWordType),
+ OS_SYNC_WAKE_BY_ADDRESS_NONE);
}
};
diff --git a/libc/src/__support/threads/raw_mutex.h b/libc/src/__support/threads/raw_mutex.h
index fb3f5c0f70ed6..c8f35a5785fd7 100644
--- a/libc/src/__support/threads/raw_mutex.h
+++ b/libc/src/__support/threads/raw_mutex.h
@@ -18,6 +18,8 @@
#include "src/__support/time/abs_timeout.h"
#include "sys/errno.h"
+#include <stdio.h>
+
#if defined(__linux__)
#include "src/__support/threads/linux/futex_utils.h"
#elif defined(__APPLE__)
diff --git a/libc/test/src/__support/threads/darwin/mutex_test.cpp b/libc/test/src/__support/threads/darwin/mutex_test.cpp
index f135aaf66b001..b87da8670e422 100644
--- a/libc/test/src/__support/threads/darwin/mutex_test.cpp
+++ b/libc/test/src/__support/threads/darwin/mutex_test.cpp
@@ -13,7 +13,6 @@
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);
@@ -22,4 +21,25 @@ TEST(LlvmLibcSupportThreadsMutexTest, SmokeTest) {
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): add other tests a la linux
More information about the libc-commits
mailing list