[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:30:00 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/2] [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/2] 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);
}
More information about the libc-commits
mailing list