[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