[libc-commits] [libc] [libc] implement recursive mutex and fix wrong initializer (PR #193992)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Fri Apr 24 08:32:17 PDT 2026


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/193992

>From c98e83145a800ddb2fa3c3fef2dac6f6edb6727f Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 24 Apr 2026 11:19:24 -0400
Subject: [PATCH 1/3] [libc] implement recursive mutex and fix wrong
 initializer

---
 .../include/llvm-libc-macros/pthread-macros.h | 14 +--
 libc/include/llvm-libc-types/CMakeLists.txt   |  2 +-
 libc/include/llvm-libc-types/__mutex_type.h   | 17 ++--
 libc/src/__support/threads/CMakeLists.txt     |  2 +
 libc/src/__support/threads/fork_callbacks.cpp |  4 +-
 libc/src/__support/threads/linux/barrier.cpp  |  7 +-
 libc/src/__support/threads/mutex_common.h     |  1 +
 libc/src/__support/threads/thread.cpp         |  8 +-
 libc/src/__support/threads/unix_mutex.h       | 96 ++++++++++++-------
 libc/src/pthread/pthread_mutex_init.cpp       | 38 ++++++--
 libc/src/stdlib/atexit.cpp                    |  3 +-
 libc/src/threads/mtx_init.cpp                 | 14 +--
 .../integration/src/pthread/CMakeLists.txt    |  3 +
 .../src/pthread/pthread_mutex_test.cpp        | 76 ++++++++++++++-
 .../test/integration/src/threads/mtx_test.cpp | 25 +++++
 15 files changed, 233 insertions(+), 77 deletions(-)

diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h
index 58019403bb333..5fb8857ef37ab 100644
--- a/libc/include/llvm-libc-macros/pthread-macros.h
+++ b/libc/include/llvm-libc-macros/pthread-macros.h
@@ -32,16 +32,18 @@
 #ifdef __linux__
 #define PTHREAD_MUTEX_INITIALIZER                                              \
   {                                                                            \
-      /* .__timed = */ 0,      /* .__recursive = */ 0,                         \
-      /* .__robust = */ 0,     /* .__owner = */ NULL,                          \
-      /* .__lock_count = */ 0, /* .__futex_word = */ {0},                      \
+      /* .__ftxw = */ {0},           /* .__priority_inherit = */ 0,            \
+      /* .__recursive = */ 0,        /* .__robust = */ 0,                      \
+      /* .__pshared = */ 0,          /* .__owner = */ 0,                       \
+      /* .__lock_count = */ 0,                                              \
   }
 #else
 #define PTHREAD_MUTEX_INITIALIZER                                              \
   {                                                                            \
-      /* .__timed = */ 0,      /* .__recursive = */ 0,                         \
-      /* .__robust = */ 0,     /* .__owner = */ NULL,                          \
-      /* .__lock_count = */ 0,                                                 \
+      /* .__ftxw = */ {0},           /* .__priority_inherit = */ 0,            \
+      /* .__recursive = */ 0,        /* .__robust = */ 0,                      \
+      /* .__pshared = */ 0,          /* .__owner = */ 0,                       \
+      /* .__lock_count = */ 0,                                              \
   }
 #endif
 
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 4cd889e4789a2..8eb9b540f6f3c 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -17,7 +17,7 @@ add_header(__exec_argv_t HDR __exec_argv_t.h)
 add_header(__exec_envp_t HDR __exec_envp_t.h)
 add_header(__futex_word HDR __futex_word.h)
 add_header(pid_t HDR pid_t.h)
-add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word .pid_t)
+add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word .pid_t .size_t)
 add_header(__barrier_type HDR __barrier_type.h)
 add_header(__pthread_once_func_t HDR __pthread_once_func_t.h)
 add_header(__pthread_start_t HDR __pthread_start_t.h)
diff --git a/libc/include/llvm-libc-types/__mutex_type.h b/libc/include/llvm-libc-types/__mutex_type.h
index 8355616261495..455eceb3c3955 100644
--- a/libc/include/llvm-libc-types/__mutex_type.h
+++ b/libc/include/llvm-libc-types/__mutex_type.h
@@ -10,20 +10,23 @@
 #define LLVM_LIBC_TYPES___MUTEX_TYPE_H
 
 #include "__futex_word.h"
+#include "pid_t.h"
+#include "size_t.h"
 
 typedef struct {
-  unsigned char __timed;
-  unsigned char __recursive;
-  unsigned char __robust;
-
-  void *__owner;
-  unsigned long long __lock_count;
-
 #ifdef __linux__
   __futex_word __ftxw;
 #else
 #error "Mutex type not defined for the target platform."
 #endif
+
+  unsigned int __priority_inherit : 1;
+  unsigned int __recursive : 1;
+  unsigned int __robust : 1;
+  unsigned int __pshared : 1;
+
+  pid_t __owner;
+  size_t __lock_count;
 } __mutex_type;
 
 #endif // LLVM_LIBC_TYPES___MUTEX_TYPE_H
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index b603a2cfbb0e1..0846a78bbf904 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -75,6 +75,7 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
       unix_mutex.h
     DEPENDS
       .raw_mutex
+      libc.src.__support.CPP.atomic
   )
 
   add_header_library(
@@ -162,6 +163,7 @@ if(TARGET libc.src.__support.threads.futex_utils)
       libc.src.__support.threads.sleep
       libc.src.__support.time.monotonicity
       libc.src.__support.time.abs_timeout
+      libc.src.__support.threads.identifier
   )
 endif()
 
diff --git a/libc/src/__support/threads/fork_callbacks.cpp b/libc/src/__support/threads/fork_callbacks.cpp
index 49fb41a975826..b8e6b2b1ba10a 100644
--- a/libc/src/__support/threads/fork_callbacks.cpp
+++ b/libc/src/__support/threads/fork_callbacks.cpp
@@ -34,8 +34,8 @@ class AtForkCallbackManager {
 
 public:
   constexpr AtForkCallbackManager()
-      : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
-            /*pshared=*/false),
+      : mtx(/*is_priority_inherit=*/false, /*is_recursive=*/false,
+            /*is_robust=*/false, /*is_pshared=*/false),
         next_index(0) {}
 
   bool register_triple(const ForkCallbackTriple &triple) {
diff --git a/libc/src/__support/threads/linux/barrier.cpp b/libc/src/__support/threads/linux/barrier.cpp
index 80f25f8ac7a21..882f6fa61887a 100644
--- a/libc/src/__support/threads/linux/barrier.cpp
+++ b/libc/src/__support/threads/linux/barrier.cpp
@@ -28,10 +28,9 @@ int Barrier::init(Barrier *b,
   new (&b->entering) CndVar(attr ? attr->pshared : false);
   new (&b->exiting) CndVar(attr ? attr->pshared : false);
 
-  auto mutex_err = Mutex::init(&b->m, false, false, false,
-                               /*pshared=*/attr ? attr->pshared : false);
-  if (mutex_err != MutexError::NONE)
-    return EAGAIN;
+  new (&b->m) Mutex(/*is_priority_inherit=*/false, /*is_recursive=*/false,
+                    /*is_robust=*/false,
+                    /*is_pshared=*/attr ? attr->pshared : false);
 
   return 0;
 }
diff --git a/libc/src/__support/threads/mutex_common.h b/libc/src/__support/threads/mutex_common.h
index 9913f69a6a61a..1c71c5c78bc24 100644
--- a/libc/src/__support/threads/mutex_common.h
+++ b/libc/src/__support/threads/mutex_common.h
@@ -19,6 +19,7 @@ enum class MutexError : int {
   TIMEOUT,
   UNLOCK_WITHOUT_LOCK,
   BAD_LOCK_STATE,
+  OVERFLOW,
 };
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/threads/thread.cpp b/libc/src/__support/threads/thread.cpp
index 9618d7829161a..ae179f068b88e 100644
--- a/libc/src/__support/threads/thread.cpp
+++ b/libc/src/__support/threads/thread.cpp
@@ -53,8 +53,8 @@ class TSSKeyMgr {
 
 public:
   constexpr TSSKeyMgr()
-      : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
-            /*pshared=*/false) {}
+      : mtx(/*is_priority_inherit=*/false, /*is_recursive=*/false,
+            /*is_robust=*/false, /*is_pshared=*/false) {}
 
   cpp::optional<unsigned int> new_key(TSSDtor *dtor) {
     cpp::lock_guard lock(mtx);
@@ -112,8 +112,8 @@ class ThreadAtExitCallbackMgr {
 
 public:
   constexpr ThreadAtExitCallbackMgr()
-      : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
-            /*pshared=*/false) {}
+      : mtx(/*is_priority_inherit=*/false, /*is_recursive=*/false,
+            /*is_robust=*/false, /*is_pshared=*/false) {}
 
   int add_callback(AtExitCallback *callback, void *obj) {
     cpp::lock_guard lock(mtx);
diff --git a/libc/src/__support/threads/unix_mutex.h b/libc/src/__support/threads/unix_mutex.h
index ac05e2528ddca..9f304c5e81159 100644
--- a/libc/src/__support/threads/unix_mutex.h
+++ b/libc/src/__support/threads/unix_mutex.h
@@ -10,9 +10,13 @@
 #define LLVM_LIBC_SRC___SUPPORT_THREADS_UNIX_MUTEX_H
 
 #include "hdr/types/pid_t.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/CPP/atomic.h"
 #include "src/__support/CPP/optional.h"
 #include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/threads/identifier.h"
 #include "src/__support/threads/mutex_common.h"
 #include "src/__support/threads/raw_mutex.h"
 
@@ -20,37 +24,44 @@ namespace LIBC_NAMESPACE_DECL {
 
 // TODO: support shared/recursive/robust mutexes.
 class Mutex final : private RawMutex {
-  // reserved timed, may be useful when combined with other flags.
-  unsigned char timed;
-  unsigned char recursive;
-  unsigned char robust;
-  unsigned char pshared;
+  // Use bitfields to allow encoding more attributes.
+  // TODO: also track errorchecking mutex type explicitly?
+  LIBC_PREFERED_TYPE(bool) unsigned int priority_inherit : 1;
+  LIBC_PREFERED_TYPE(bool) unsigned int recursive : 1;
+  LIBC_PREFERED_TYPE(bool) unsigned int robust : 1;
+  LIBC_PREFERED_TYPE(bool) unsigned int pshared : 1;
 
   // TLS address may not work across forked processes. Use thread id instead.
-  pid_t owner;
-  unsigned long long lock_count;
+  cpp::Atomic<pid_t> owner;
+  size_t lock_count;
 
   // CndVar needs to access Mutex as RawMutex
   friend class CndVar;
 
-public:
-  LIBC_INLINE constexpr Mutex(bool is_timed, bool is_recursive, bool is_robust,
-                              bool is_pshared)
-      : RawMutex(), timed(is_timed), recursive(is_recursive), robust(is_robust),
-        pshared(is_pshared), owner(0), lock_count(0) {}
-
-  LIBC_INLINE static MutexError init(Mutex *mutex, bool is_timed, bool isrecur,
-                                     bool isrobust, bool is_pshared) {
-    RawMutex::init(mutex);
-    mutex->timed = is_timed;
-    mutex->recursive = isrecur;
-    mutex->robust = isrobust;
-    mutex->pshared = is_pshared;
-    mutex->owner = 0;
-    mutex->lock_count = 0;
-    return MutexError::NONE;
+  template <class LockRoutine>
+  LIBC_INLINE MutexError lock_impl(LockRoutine do_lock) {
+    if (is_recursive() && owner == internal::gettid()) {
+      if (LIBC_UNLIKELY(lock_count == cpp::numeric_limits<size_t>::max()))
+        return MutexError::OVERFLOW;
+      lock_count++;
+      return MutexError::NONE;
+    }
+
+    MutexError res = do_lock();
+    if (is_recursive() && res == MutexError::NONE) {
+      owner = internal::gettid();
+      lock_count = 1;
+    }
+    return res;
   }
 
+public:
+  LIBC_INLINE constexpr Mutex(bool is_priority_inherit, bool is_recursive,
+                              bool is_robust, bool is_pshared)
+      : RawMutex(), priority_inherit(is_priority_inherit),
+        recursive(is_recursive), robust(is_robust), pshared(is_pshared),
+        owner(0), lock_count(0) {}
+
   LIBC_INLINE static MutexError destroy(Mutex *lock) {
     LIBC_ASSERT(lock->owner == 0 && lock->lock_count == 0 &&
                 "Mutex destroyed while being locked.");
@@ -60,20 +71,33 @@ class Mutex final : private RawMutex {
 
   // TODO: record owner and lock count.
   LIBC_INLINE MutexError lock() {
-    // Since timeout is not specified, we do not need to check the return value.
-    this->RawMutex::lock(
-        /* timeout=*/cpp::nullopt, this->pshared);
-    return MutexError::NONE;
+    return lock_impl([this] {
+      // Since timeout is not specified, we do not need to check the return
+      // value.
+      // TODO: check deadlock? POSIX made it optional.
+      this->RawMutex::lock(/* timeout=*/cpp::nullopt, this->pshared);
+      return MutexError::NONE;
+    });
   }
 
   // TODO: record owner and lock count.
   LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) {
-    if (this->RawMutex::lock(abs_time, this->pshared))
-      return MutexError::NONE;
-    return MutexError::TIMEOUT;
+    return lock_impl([this, abs_time] {
+      // TODO: check deadlock? POSIX made it optional.
+      if (this->RawMutex::lock(abs_time, this->pshared))
+        return MutexError::NONE;
+      return MutexError::TIMEOUT;
+    });
   }
 
   LIBC_INLINE MutexError unlock() {
+    if (is_recursive() && owner == internal::gettid()) {
+      lock_count--;
+      if (lock_count == 0)
+        owner = 0;
+      else
+        return MutexError::NONE;
+    }
     if (this->RawMutex::unlock(this->pshared))
       return MutexError::NONE;
     return MutexError::UNLOCK_WITHOUT_LOCK;
@@ -81,16 +105,20 @@ class Mutex final : private RawMutex {
 
   // TODO: record owner and lock count.
   LIBC_INLINE MutexError try_lock() {
-    if (this->RawMutex::try_lock())
-      return MutexError::NONE;
-    return MutexError::BUSY;
+    return lock_impl([this] {
+      if (this->RawMutex::try_lock())
+        return MutexError::NONE;
+      return MutexError::BUSY;
+    });
   }
 
   LIBC_INLINE bool can_be_requeued() const {
-    return !this->pshared && !this->robust;
+    return !this->pshared && !this->robust && !this->recursive &&
+           !this->priority_inherit;
   }
 
   LIBC_INLINE bool is_robust() const { return this->robust; }
+  LIBC_INLINE bool is_recursive() const { return this->recursive; }
 };
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_mutex_init.cpp b/libc/src/pthread/pthread_mutex_init.cpp
index 94052e53d8c0a..0fa6f80161c4d 100644
--- a/libc/src/pthread/pthread_mutex_init.cpp
+++ b/libc/src/pthread/pthread_mutex_init.cpp
@@ -9,6 +9,7 @@
 #include "pthread_mutex_init.h"
 #include "pthread_mutexattr.h"
 
+#include "src/__support/CPP/new.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/threads/mutex.h"
@@ -17,20 +18,41 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-static_assert(sizeof(Mutex) <= sizeof(pthread_mutex_t),
-              "The public pthread_mutex_t type cannot accommodate the internal "
+static_assert(sizeof(Mutex) == sizeof(pthread_mutex_t) &&
+                  alignof(Mutex) == alignof(pthread_mutex_t),
+              "The public pthread_mutex_t type must exactly match the internal "
               "mutex type.");
 
 LLVM_LIBC_FUNCTION(int, pthread_mutex_init,
                    (pthread_mutex_t * m,
                     const pthread_mutexattr_t *__restrict attr)) {
   auto mutexattr = attr == nullptr ? DEFAULT_MUTEXATTR : *attr;
-  auto err =
-      Mutex::init(reinterpret_cast<Mutex *>(m), /*is_timed=*/true,
-                  get_mutexattr_type(mutexattr) & PTHREAD_MUTEX_RECURSIVE,
-                  get_mutexattr_robust(mutexattr) & PTHREAD_MUTEX_ROBUST,
-                  get_mutexattr_pshared(mutexattr) & PTHREAD_PROCESS_SHARED);
-  return err == MutexError::NONE ? 0 : EAGAIN;
+  bool is_recursive = false;
+  switch (get_mutexattr_type(mutexattr)) {
+  case PTHREAD_MUTEX_NORMAL:
+  case PTHREAD_MUTEX_ERRORCHECK:
+    break;
+  case PTHREAD_MUTEX_RECURSIVE:
+    is_recursive = true;
+    break;
+  }
+
+  bool is_robust = false;
+  if (get_mutexattr_robust(mutexattr) == PTHREAD_MUTEX_ROBUST)
+    is_robust = true;
+
+  bool is_pshared = false;
+  switch (get_mutexattr_pshared(mutexattr)) {
+  case PTHREAD_PROCESS_PRIVATE:
+    break;
+  case PTHREAD_PROCESS_SHARED:
+    is_pshared = true;
+    break;
+  }
+
+  new (m) Mutex(/*is_priority_inherit=*/false, /*is_recursive=*/is_recursive,
+                /*is_robust=*/is_robust, /*is_pshared=*/is_pshared);
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/atexit.cpp b/libc/src/stdlib/atexit.cpp
index c8a15dd3cfef2..5b924be3a63ed 100644
--- a/libc/src/stdlib/atexit.cpp
+++ b/libc/src/stdlib/atexit.cpp
@@ -15,7 +15,8 @@
 namespace LIBC_NAMESPACE_DECL {
 
 constinit ExitCallbackList atexit_callbacks;
-Mutex handler_list_mtx(false, false, false, false);
+Mutex handler_list_mtx(/*is_priority_inherit=*/false, /*is_recursive=*/false,
+                       /*is_robust=*/false, /*is_pshared=*/false);
 
 extern "C" {
 
diff --git a/libc/src/threads/mtx_init.cpp b/libc/src/threads/mtx_init.cpp
index eb0ba5010584e..c6b0278c52f77 100644
--- a/libc/src/threads/mtx_init.cpp
+++ b/libc/src/threads/mtx_init.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/threads/mtx_init.h"
+#include "src/__support/CPP/new.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/threads/mutex.h"
@@ -15,15 +16,16 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-static_assert(sizeof(Mutex) <= sizeof(mtx_t),
-              "The public mtx_t type cannot accommodate the internal mutex "
+static_assert(sizeof(Mutex) == sizeof(mtx_t) &&
+                  alignof(Mutex) == alignof(mtx_t),
+              "The public mtx_t type must exactly match the internal mutex "
               "type.");
 
 LLVM_LIBC_FUNCTION(int, mtx_init, (mtx_t * m, int type)) {
-  auto err = Mutex::init(reinterpret_cast<Mutex *>(m), type & mtx_timed,
-                         type & mtx_recursive, /* is_robust */ false,
-                         /* is_pshared */ false);
-  return err == MutexError::NONE ? thrd_success : thrd_error;
+  new (m) Mutex(/*is_priority_inherit=*/false,
+                /*is_recursive=*/static_cast<bool>(type & mtx_recursive),
+                /*is_robust=*/false, /*is_pshared=*/false);
+  return thrd_success;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 71cb039f7761e..eceebb42f18c4 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -15,6 +15,9 @@ add_integration_test(
     libc.src.pthread.pthread_mutex_lock
     libc.src.pthread.pthread_mutex_trylock
     libc.src.pthread.pthread_mutex_unlock
+    libc.src.pthread.pthread_mutexattr_destroy
+    libc.src.pthread.pthread_mutexattr_init
+    libc.src.pthread.pthread_mutexattr_settype
     libc.src.pthread.pthread_create
     libc.src.pthread.pthread_join
 )
diff --git a/libc/test/integration/src/pthread/pthread_mutex_test.cpp b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
index 488954c0ef255..b8d71c6f48d2a 100644
--- a/libc/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -15,6 +15,9 @@
 #include "src/pthread/pthread_mutex_lock.h"
 #include "src/pthread/pthread_mutex_trylock.h"
 #include "src/pthread/pthread_mutex_unlock.h"
+#include "src/pthread/pthread_mutexattr_destroy.h"
+#include "src/pthread/pthread_mutexattr_init.h"
+#include "src/pthread/pthread_mutexattr_settype.h"
 #include "test/IntegrationTest/test.h"
 
 #include <pthread.h>
@@ -143,6 +146,73 @@ void trylock_test() {
   LIBC_NAMESPACE::pthread_mutex_destroy(&trylock_mutex);
 }
 
+void *trylock_other_thread(void *arg) {
+  auto *mutex = reinterpret_cast<pthread_mutex_t *>(arg);
+  int result = LIBC_NAMESPACE::pthread_mutex_trylock(mutex);
+  if (result == 0)
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(mutex), 0);
+  return reinterpret_cast<void *>(uintptr_t(result));
+}
+
+void recursive_mutex_test() {
+  pthread_mutexattr_t attr;
+  pthread_mutex_t recursive_mutex;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_init(&attr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_settype(&attr,
+                                                      PTHREAD_MUTEX_RECURSIVE),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&recursive_mutex, &attr), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
+
+  ASSERT_TRUE(recursive_mutex.__recursive);
+  ASSERT_EQ(recursive_mutex.__owner, 0);
+  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&recursive_mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&recursive_mutex), 0);
+
+  pthread_t thread;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&thread, nullptr, trylock_other_thread,
+                                           &recursive_mutex),
+            0);
+  void *retval = nullptr;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread, &retval), 0);
+  ASSERT_EQ(uintptr_t(retval), uintptr_t(EBUSY));
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
+  ASSERT_EQ(recursive_mutex.__owner, 0);
+  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&recursive_mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_destroy(&recursive_mutex), 0);
+}
+
+void initializer_acts_the_same_as_null_attr() {
+  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+  pthread_mutex_t mutex_from_init;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&mutex_from_init, nullptr), 0);
+
+  ASSERT_EQ(mutex.__ftxw.__word, mutex_from_init.__ftxw.__word);
+  ASSERT_EQ(mutex.__priority_inherit, mutex_from_init.__priority_inherit);
+  ASSERT_EQ(mutex.__recursive, mutex_from_init.__recursive);
+  ASSERT_EQ(mutex.__robust, mutex_from_init.__robust);
+  ASSERT_EQ(mutex.__pshared, mutex_from_init.__pshared);
+  ASSERT_EQ(mutex.__owner, mutex_from_init.__owner);
+  ASSERT_EQ(mutex.__lock_count, mutex_from_init.__lock_count);
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&mutex), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&mutex), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_destroy(&mutex), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&mutex_from_init), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&mutex_from_init), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&mutex_from_init), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_destroy(&mutex_from_init), 0);
+}
+
 static constexpr int THREAD_COUNT = 10;
 static pthread_mutex_t multiple_waiter_lock;
 static pthread_mutex_t counter_lock;
@@ -199,14 +269,12 @@ void multiple_waiters() {
   LIBC_NAMESPACE::pthread_mutex_destroy(&counter_lock);
 }
 
-// Test the initializer
-[[maybe_unused]]
-static pthread_mutex_t test_initializer = PTHREAD_MUTEX_INITIALIZER;
-
 TEST_MAIN() {
   relay_counter();
   wait_and_step();
   trylock_test();
+  recursive_mutex_test();
+  initializer_acts_the_same_as_null_attr();
   multiple_waiters();
   return 0;
 }
diff --git a/libc/test/integration/src/threads/mtx_test.cpp b/libc/test/integration/src/threads/mtx_test.cpp
index 0b0a9faae24a6..18bdbc748d5fa 100644
--- a/libc/test/integration/src/threads/mtx_test.cpp
+++ b/libc/test/integration/src/threads/mtx_test.cpp
@@ -138,6 +138,30 @@ void wait_and_step() {
   LIBC_NAMESPACE::mtx_destroy(&step_lock);
 }
 
+void recursive_mutex_test() {
+  mtx_t recursive_mutex;
+  ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&recursive_mutex, mtx_recursive),
+            static_cast<int>(thrd_success));
+
+  ASSERT_TRUE(recursive_mutex.__recursive);
+  ASSERT_EQ(recursive_mutex.__owner, 0);
+  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+
+  ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&recursive_mutex),
+            static_cast<int>(thrd_success));
+  ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&recursive_mutex),
+            static_cast<int>(thrd_success));
+
+  ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&recursive_mutex),
+            static_cast<int>(thrd_success));
+  ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&recursive_mutex),
+            static_cast<int>(thrd_success));
+  ASSERT_EQ(recursive_mutex.__owner, 0);
+  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+
+  LIBC_NAMESPACE::mtx_destroy(&recursive_mutex);
+}
+
 static constexpr int THREAD_COUNT = 10;
 static mtx_t multiple_waiter_lock;
 static mtx_t counter_lock;
@@ -197,6 +221,7 @@ void multiple_waiters() {
 TEST_MAIN() {
   relay_counter();
   wait_and_step();
+  recursive_mutex_test();
   multiple_waiters();
   return 0;
 }

>From 25b2a4ad2ea7f200eeceef5aba82db1bfee569a1 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 24 Apr 2026 11:29:45 -0400
Subject: [PATCH 2/3] fix

---
 .../src/pthread/pthread_mutex_test.cpp        | 40 +++++++++++++------
 .../test/integration/src/threads/mtx_test.cpp | 22 +++++++---
 2 files changed, 45 insertions(+), 17 deletions(-)

diff --git a/libc/test/integration/src/pthread/pthread_mutex_test.cpp b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
index b8d71c6f48d2a..f45c5f1fb7d6e 100644
--- a/libc/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -18,6 +18,7 @@
 #include "src/pthread/pthread_mutexattr_destroy.h"
 #include "src/pthread/pthread_mutexattr_init.h"
 #include "src/pthread/pthread_mutexattr_settype.h"
+#include "src/string/memory_utils/inline_memcpy.h"
 #include "test/IntegrationTest/test.h"
 
 #include <pthread.h>
@@ -25,6 +26,15 @@
 constexpr int START = 0;
 constexpr int MAX = 10000;
 
+static pthread_mutex_t snapshot_mutex(const void *mutex_storage) {
+  pthread_mutex_t snapshot;
+  // The original storage may currently hold libc's internal mutex
+  // representation. Copy the bytes into pthread_mutex_t storage before
+  // inspection to avoid strict aliasing violations.
+  LIBC_NAMESPACE::inline_memcpy(&snapshot, mutex_storage, sizeof(snapshot));
+  return snapshot;
+}
+
 pthread_mutex_t mutex;
 static int shared_int = START;
 
@@ -164,9 +174,10 @@ void recursive_mutex_test() {
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&recursive_mutex, &attr), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
 
-  ASSERT_TRUE(recursive_mutex.__recursive);
-  ASSERT_EQ(recursive_mutex.__owner, 0);
-  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+  pthread_mutex_t snapshot = snapshot_mutex(&recursive_mutex);
+  ASSERT_TRUE(snapshot.__recursive);
+  ASSERT_EQ(snapshot.__owner, 0);
+  ASSERT_EQ(snapshot.__lock_count, size_t(0));
 
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&recursive_mutex), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&recursive_mutex), 0);
@@ -181,8 +192,9 @@ void recursive_mutex_test() {
 
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
-  ASSERT_EQ(recursive_mutex.__owner, 0);
-  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+  snapshot = snapshot_mutex(&recursive_mutex);
+  ASSERT_EQ(snapshot.__owner, 0);
+  ASSERT_EQ(snapshot.__lock_count, size_t(0));
 
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&recursive_mutex), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&recursive_mutex), 0);
@@ -194,13 +206,17 @@ void initializer_acts_the_same_as_null_attr() {
   pthread_mutex_t mutex_from_init;
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&mutex_from_init, nullptr), 0);
 
-  ASSERT_EQ(mutex.__ftxw.__word, mutex_from_init.__ftxw.__word);
-  ASSERT_EQ(mutex.__priority_inherit, mutex_from_init.__priority_inherit);
-  ASSERT_EQ(mutex.__recursive, mutex_from_init.__recursive);
-  ASSERT_EQ(mutex.__robust, mutex_from_init.__robust);
-  ASSERT_EQ(mutex.__pshared, mutex_from_init.__pshared);
-  ASSERT_EQ(mutex.__owner, mutex_from_init.__owner);
-  ASSERT_EQ(mutex.__lock_count, mutex_from_init.__lock_count);
+  pthread_mutex_t mutex_snapshot = snapshot_mutex(&mutex);
+  pthread_mutex_t mutex_from_init_snapshot = snapshot_mutex(&mutex_from_init);
+  ASSERT_EQ(mutex_snapshot.__ftxw.__word,
+            mutex_from_init_snapshot.__ftxw.__word);
+  ASSERT_EQ(mutex_snapshot.__priority_inherit,
+            mutex_from_init_snapshot.__priority_inherit);
+  ASSERT_EQ(mutex_snapshot.__recursive, mutex_from_init_snapshot.__recursive);
+  ASSERT_EQ(mutex_snapshot.__robust, mutex_from_init_snapshot.__robust);
+  ASSERT_EQ(mutex_snapshot.__pshared, mutex_from_init_snapshot.__pshared);
+  ASSERT_EQ(mutex_snapshot.__owner, mutex_from_init_snapshot.__owner);
+  ASSERT_EQ(mutex_snapshot.__lock_count, mutex_from_init_snapshot.__lock_count);
 
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&mutex), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&mutex), EBUSY);
diff --git a/libc/test/integration/src/threads/mtx_test.cpp b/libc/test/integration/src/threads/mtx_test.cpp
index 18bdbc748d5fa..5aa2d6d1b3f50 100644
--- a/libc/test/integration/src/threads/mtx_test.cpp
+++ b/libc/test/integration/src/threads/mtx_test.cpp
@@ -12,6 +12,7 @@
 #include "src/threads/mtx_unlock.h"
 #include "src/threads/thrd_create.h"
 #include "src/threads/thrd_join.h"
+#include "src/string/memory_utils/inline_memcpy.h"
 
 #include "test/IntegrationTest/test.h"
 
@@ -20,6 +21,15 @@
 constexpr int START = 0;
 constexpr int MAX = 10000;
 
+static mtx_t snapshot_mutex(const void *mutex_storage) {
+  mtx_t snapshot;
+  // The original storage may currently hold libc's internal mutex
+  // representation. Copy the bytes into mtx_t storage before inspection to
+  // avoid strict aliasing violations.
+  LIBC_NAMESPACE::inline_memcpy(&snapshot, mutex_storage, sizeof(snapshot));
+  return snapshot;
+}
+
 mtx_t mutex;
 static int shared_int = START;
 
@@ -143,9 +153,10 @@ void recursive_mutex_test() {
   ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&recursive_mutex, mtx_recursive),
             static_cast<int>(thrd_success));
 
-  ASSERT_TRUE(recursive_mutex.__recursive);
-  ASSERT_EQ(recursive_mutex.__owner, 0);
-  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+  mtx_t snapshot = snapshot_mutex(&recursive_mutex);
+  ASSERT_TRUE(snapshot.__recursive);
+  ASSERT_EQ(snapshot.__owner, 0);
+  ASSERT_EQ(snapshot.__lock_count, size_t(0));
 
   ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&recursive_mutex),
             static_cast<int>(thrd_success));
@@ -156,8 +167,9 @@ void recursive_mutex_test() {
             static_cast<int>(thrd_success));
   ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&recursive_mutex),
             static_cast<int>(thrd_success));
-  ASSERT_EQ(recursive_mutex.__owner, 0);
-  ASSERT_EQ(recursive_mutex.__lock_count, size_t(0));
+  snapshot = snapshot_mutex(&recursive_mutex);
+  ASSERT_EQ(snapshot.__owner, 0);
+  ASSERT_EQ(snapshot.__lock_count, size_t(0));
 
   LIBC_NAMESPACE::mtx_destroy(&recursive_mutex);
 }

>From 62cd690365ea9fdffb2ab9f32545fa72f270aff8 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 24 Apr 2026 11:32:05 -0400
Subject: [PATCH 3/3] fix

---
 libc/include/llvm-libc-macros/pthread-macros.h   | 16 ++++++++--------
 .../src/pthread/pthread_mutex_test.cpp           | 10 +++++-----
 libc/test/integration/src/threads/mtx_test.cpp   |  2 +-
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h
index 5fb8857ef37ab..1619ad123d2c1 100644
--- a/libc/include/llvm-libc-macros/pthread-macros.h
+++ b/libc/include/llvm-libc-macros/pthread-macros.h
@@ -32,18 +32,18 @@
 #ifdef __linux__
 #define PTHREAD_MUTEX_INITIALIZER                                              \
   {                                                                            \
-      /* .__ftxw = */ {0},           /* .__priority_inherit = */ 0,            \
-      /* .__recursive = */ 0,        /* .__robust = */ 0,                      \
-      /* .__pshared = */ 0,          /* .__owner = */ 0,                       \
-      /* .__lock_count = */ 0,                                              \
+      /* .__ftxw = */ {0},     /* .__priority_inherit = */ 0,                  \
+      /* .__recursive = */ 0,  /* .__robust = */ 0,                            \
+      /* .__pshared = */ 0,    /* .__owner = */ 0,                             \
+      /* .__lock_count = */ 0,                                                 \
   }
 #else
 #define PTHREAD_MUTEX_INITIALIZER                                              \
   {                                                                            \
-      /* .__ftxw = */ {0},           /* .__priority_inherit = */ 0,            \
-      /* .__recursive = */ 0,        /* .__robust = */ 0,                      \
-      /* .__pshared = */ 0,          /* .__owner = */ 0,                       \
-      /* .__lock_count = */ 0,                                              \
+      /* .__ftxw = */ {0},     /* .__priority_inherit = */ 0,                  \
+      /* .__recursive = */ 0,  /* .__robust = */ 0,                            \
+      /* .__pshared = */ 0,    /* .__owner = */ 0,                             \
+      /* .__lock_count = */ 0,                                                 \
   }
 #endif
 
diff --git a/libc/test/integration/src/pthread/pthread_mutex_test.cpp b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
index f45c5f1fb7d6e..7e323189431cd 100644
--- a/libc/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -168,9 +168,9 @@ void recursive_mutex_test() {
   pthread_mutexattr_t attr;
   pthread_mutex_t recursive_mutex;
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_init(&attr), 0);
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_settype(&attr,
-                                                      PTHREAD_MUTEX_RECURSIVE),
-            0);
+  ASSERT_EQ(
+      LIBC_NAMESPACE::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE),
+      0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&recursive_mutex, &attr), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
 
@@ -183,8 +183,8 @@ void recursive_mutex_test() {
   ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&recursive_mutex), 0);
 
   pthread_t thread;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&thread, nullptr, trylock_other_thread,
-                                           &recursive_mutex),
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_create(
+                &thread, nullptr, trylock_other_thread, &recursive_mutex),
             0);
   void *retval = nullptr;
   ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread, &retval), 0);
diff --git a/libc/test/integration/src/threads/mtx_test.cpp b/libc/test/integration/src/threads/mtx_test.cpp
index 5aa2d6d1b3f50..909c4f2c5c760 100644
--- a/libc/test/integration/src/threads/mtx_test.cpp
+++ b/libc/test/integration/src/threads/mtx_test.cpp
@@ -6,13 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/string/memory_utils/inline_memcpy.h"
 #include "src/threads/mtx_destroy.h"
 #include "src/threads/mtx_init.h"
 #include "src/threads/mtx_lock.h"
 #include "src/threads/mtx_unlock.h"
 #include "src/threads/thrd_create.h"
 #include "src/threads/thrd_join.h"
-#include "src/string/memory_utils/inline_memcpy.h"
 
 #include "test/IntegrationTest/test.h"
 



More information about the libc-commits mailing list