[libc-commits] [libc] [libc] implement PI mutex (PR #199393)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Tue May 26 21:06:16 PDT 2026
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/199393
>From 19113e56e60b19b80347d541bb93f9380f1f82b7 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sat, 23 May 2026 22:03:33 -0400
Subject: [PATCH 01/10] [libc] implement PI mutex
This patch implements PI mutex that is used by realtime systems/softwares. The unix_mutex
now dispatches based on the priority_inherit flag on linux.
The `PIMutex` algorithm itself is basically of the same shape as Bionic's implementation.
Notice that PI futex requires tid being tracked inside the word, so we need to handle it
specially.
See also:
https://android.googlesource.com/platform/bionic/+/master/libc/bionic/pthread_mutex.cpp
Assisted-by: Codex with gpt-5.5 high fast
---
.../include/llvm-libc-macros/pthread-macros.h | 22 ++-
libc/include/llvm-libc-types/__mutex_type.h | 6 +-
libc/src/__support/threads/CMakeLists.txt | 6 +
.../__support/threads/linux/CMakeLists.txt | 16 ++
libc/src/__support/threads/linux/pi_mutex.h | 175 ++++++++++++++++++
libc/src/__support/threads/unix_mutex.h | 55 +++++-
libc/src/pthread/pthread_mutex_init.cpp | 5 +-
libc/src/pthread/pthread_mutexattr.h | 9 +-
.../src/pthread/pthread_mutex_test.cpp | 61 ++++--
9 files changed, 321 insertions(+), 34 deletions(-)
create mode 100644 libc/src/__support/threads/linux/pi_mutex.h
diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h
index d6518189f1ccb..e23d2873e0891 100644
--- a/libc/include/llvm-libc-macros/pthread-macros.h
+++ b/libc/include/llvm-libc-macros/pthread-macros.h
@@ -32,18 +32,24 @@
#ifdef __linux__
#define PTHREAD_MUTEX_INITIALIZER \
{ \
- /* .__ftxw = */ {0}, /* .__priority_inherit = */ 0, \
- /* .__recursive = */ 0, /* .__robust = */ 0, \
- /* .__pshared = */ 0, /* .__error_checking = */ 0, \
- /* .__owner = */ 0, /* .__lock_count = */ 0, \
+ /* .__ftxw = */ {0}, \
+ /* .__priority_inherit = */ 0, \
+ /* .__recursive = */ 0, \
+ /* .__robust = */ 0, \
+ /* .__pshared = */ 0, \
+ /* .__error_checking = */ 0, \
+ {/* .__owner = */ 0, /* .__lock_count = */ 0}, \
}
#else
#define PTHREAD_MUTEX_INITIALIZER \
{ \
- /* .__ftxw = */ {0}, /* .__priority_inherit = */ 0, \
- /* .__recursive = */ 0, /* .__robust = */ 0, \
- /* .__pshared = */ 0, /* .__error_checking = */ 0, \
- /* .__owner = */ 0, /* .__lock_count = */ 0, \
+ /* .__ftxw = */ {0}, \
+ /* .__priority_inherit = */ 0, \
+ /* .__recursive = */ 0, \
+ /* .__robust = */ 0, \
+ /* .__pshared = */ 0, \
+ /* .__error_checking = */ 0, \
+ {/* .__owner = */ 0, /* .__lock_count = */ 0}, \
}
#endif
diff --git a/libc/include/llvm-libc-types/__mutex_type.h b/libc/include/llvm-libc-types/__mutex_type.h
index 40f37c5235f2e..adc379318e675 100644
--- a/libc/include/llvm-libc-types/__mutex_type.h
+++ b/libc/include/llvm-libc-types/__mutex_type.h
@@ -26,8 +26,10 @@ typedef struct {
unsigned int __pshared : 1;
unsigned int __error_checking : 1;
- pid_t __owner;
- size_t __lock_count;
+ struct {
+ 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 0846a78bbf904..560235b0bad19 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -69,12 +69,18 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
libc.src.__support.threads.identifier
)
+ set(pi_mutex_dep)
+ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.pi_mutex)
+ set(pi_mutex_dep .${LIBC_TARGET_OS}.pi_mutex)
+ endif()
+
add_header_library(
unix_mutex
HDRS
unix_mutex.h
DEPENDS
.raw_mutex
+ ${pi_mutex_dep}
libc.src.__support.CPP.atomic
)
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 8ce19634c41b1..2e1e701f9ec84 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -24,6 +24,22 @@ add_header_library(
libc.src.__support.time.abs_timeout
)
+add_header_library(
+ pi_mutex
+ HDRS
+ pi_mutex.h
+ DEPENDS
+ .futex_utils
+ .futex_word_type
+ libc.hdr.errno_macros
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.CPP.limits
+ libc.src.__support.CPP.optional
+ libc.src.__support.time.monotonicity
+ libc.src.__support.macros.attributes
+ libc.src.__support.macros.config
+)
+
add_object_library(
thread
SRCS
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
new file mode 100644
index 0000000000000..fe898faa26bd3
--- /dev/null
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux priority inheritance mutex support.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_PI_MUTEX_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_PI_MUTEX_H
+
+#include "hdr/errno_macros.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/threads/identifier.h"
+#include "src/__support/threads/linux/futex_utils.h"
+#include "src/__support/threads/mutex_common.h"
+
+#include <linux/futex.h>
+
+#ifdef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+#include "src/__support/time/monotonicity.h"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+class PIMutex {
+protected:
+ Futex owner;
+ // Number of recursive locks minus one.
+ size_t recursive_count;
+
+public:
+ enum class Type { Normal, ErrorChecking, Recursive };
+ LIBC_INLINE static void init(PIMutex *mutex) {
+ mutex->owner.store(0);
+ mutex->recursive_count = 0;
+ }
+ LIBC_INLINE constexpr PIMutex() : owner(0), recursive_count(0) {}
+ LIBC_INLINE MutexError try_lock(Type type) {
+ FutexWordType old_owner = 0;
+ auto current = static_cast<FutexWordType>(internal::gettid());
+ if (owner.compare_exchange_strong(old_owner, current,
+ cpp::MemoryOrder::ACQUIRE,
+ cpp::MemoryOrder::RELAXED))
+ return MutexError::NONE;
+
+ if (old_owner == current) {
+ switch (type) {
+ case Type::Normal:
+ break;
+ case Type::ErrorChecking:
+ return MutexError::DEADLOCK;
+ case Type::Recursive:
+ if (LIBC_UNLIKELY(recursive_count ==
+ cpp::numeric_limits<size_t>::max()))
+ return MutexError::OVERFLOW;
+ recursive_count++;
+ return MutexError::NONE;
+ }
+ }
+
+ return MutexError::BUSY;
+ }
+ LIBC_INLINE MutexError
+ lock(Type type, cpp::optional<Futex::Timeout> timeout = cpp::nullopt,
+ bool is_shared = false) {
+ MutexError result = try_lock(type);
+ if (result != MutexError::BUSY)
+ return result;
+
+#ifdef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+ if (timeout)
+ ensure_monotonicity(*timeout);
+#endif
+
+ int op = is_shared ? FUTEX_LOCK_PI : FUTEX_LOCK_PI_PRIVATE;
+ for (;;) {
+ ErrorOr<int> ret = linux_syscalls::syscall_checked<int>(
+ /*syscall_number=*/FUTEX_SYSCALL_ID,
+ /*futex_addr=*/&owner,
+ /*op=*/op,
+ /*ignored=*/0,
+ /*timeout=*/timeout ? &timeout->get_timespec() : nullptr,
+ /*ignored=*/nullptr,
+ /*ignored=*/0);
+
+ if (ret.has_value())
+ return MutexError::NONE;
+
+ switch (ret.error()) {
+ case EINTR:
+ continue;
+ case ETIMEDOUT:
+ return MutexError::TIMEOUT;
+ case EDEADLK:
+ return MutexError::DEADLOCK;
+ default:
+ return MutexError::BAD_LOCK_STATE;
+ }
+ }
+ }
+ LIBC_INLINE MutexError unlock(Type type, bool is_shared) {
+ FutexWordType current = static_cast<FutexWordType>(internal::gettid());
+ FutexWordType old_owner = current;
+
+ if (LIBC_LIKELY(type == Type::Normal)) {
+ if (LIBC_LIKELY(owner.compare_exchange_strong(old_owner, 0,
+ cpp::MemoryOrder::RELEASE,
+ cpp::MemoryOrder::RELAXED)))
+ return MutexError::NONE;
+ } else {
+ old_owner = owner.load(cpp::MemoryOrder::RELAXED);
+ }
+
+ if (current != (old_owner & FUTEX_TID_MASK))
+ return MutexError::UNLOCK_WITHOUT_LOCK;
+
+ if (type == Type::Recursive && recursive_count != 0) {
+ recursive_count--;
+ return MutexError::NONE;
+ }
+
+ if (old_owner == current && LIBC_LIKELY(owner.compare_exchange_strong(
+ old_owner, 0, cpp::MemoryOrder::RELEASE,
+ cpp::MemoryOrder::RELAXED)))
+ return MutexError::NONE;
+
+ int op = is_shared ? FUTEX_UNLOCK_PI : FUTEX_UNLOCK_PI_PRIVATE;
+ ErrorOr<int> ret = linux_syscalls::syscall_checked<int>(
+ /*syscall_number=*/FUTEX_SYSCALL_ID,
+ /*futex_addr=*/&owner,
+ /*op=*/op,
+ /*ignored=*/0,
+ /*ignored=*/nullptr,
+ /*ignored=*/nullptr,
+ /*ignored=*/0);
+
+ if (ret.has_value())
+ return MutexError::NONE;
+
+ switch (ret.error()) {
+ case EPERM:
+ return MutexError::UNLOCK_WITHOUT_LOCK;
+ default:
+ return MutexError::BAD_LOCK_STATE;
+ }
+ }
+ LIBC_INLINE static MutexError destroy(PIMutex *lock) {
+ FutexWordType old_owner = 0;
+ if (lock->owner.compare_exchange_strong(old_owner, 0xffffffff,
+ cpp::MemoryOrder::RELAXED,
+ cpp::MemoryOrder::RELAXED))
+ return MutexError::NONE;
+ return MutexError::BUSY;
+ }
+ LIBC_INLINE void reset() {
+ owner.store(0);
+ recursive_count = 0;
+ }
+};
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_PI_MUTEX_H
diff --git a/libc/src/__support/threads/unix_mutex.h b/libc/src/__support/threads/unix_mutex.h
index 272a1dfe6dbba..9afc3d337db2d 100644
--- a/libc/src/__support/threads/unix_mutex.h
+++ b/libc/src/__support/threads/unix_mutex.h
@@ -20,6 +20,10 @@
#include "src/__support/threads/mutex_common.h"
#include "src/__support/threads/raw_mutex.h"
+#ifdef LIBC_TARGET_OS_IS_LINUX
+#include "src/__support/threads/linux/pi_mutex.h"
+#endif
+
namespace LIBC_NAMESPACE_DECL {
// TODO: support shared/recursive/robust mutexes.
@@ -34,12 +38,31 @@ class Mutex final : private RawMutex {
LIBC_PREFERED_TYPE(bool) unsigned int error_checking : 1;
// TLS address may not work across forked processes. Use thread id instead.
- cpp::Atomic<pid_t> owner;
- size_t lock_count;
+ union {
+ struct {
+ cpp::Atomic<pid_t> owner;
+ size_t lock_count;
+ };
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ // Special case, when "priority_inherit" is set, we ignore the base mutex
+ // and use this field.
+ PIMutex pi_mutex;
+#endif
+ };
// CndVar needs to access Mutex as RawMutex
friend class CndVar;
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ LIBC_INLINE PIMutex::Type pi_mutex_type() const {
+ if (is_recursive())
+ return PIMutex::Type::Recursive;
+ if (is_error_checking())
+ return PIMutex::Type::ErrorChecking;
+ return PIMutex::Type::Normal;
+ }
+#endif
+
template <class LockRoutine>
LIBC_INLINE MutexError lock_impl(LockRoutine do_lock) {
if (is_recursive() && owner == internal::gettid()) {
@@ -70,9 +93,18 @@ class Mutex final : private RawMutex {
bool is_error_checking = false)
: RawMutex(), priority_inherit(is_priority_inherit),
recursive(is_recursive), robust(is_robust), pshared(is_pshared),
- error_checking(is_error_checking), owner(0), lock_count(0) {}
+ error_checking(is_error_checking), owner(0), lock_count(0) {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (is_priority_inherit)
+ new (&pi_mutex) PIMutex{};
+#endif
+ }
LIBC_INLINE static MutexError destroy(Mutex *lock) {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (lock->priority_inherit)
+ return PIMutex::destroy(&lock->pi_mutex);
+#endif
LIBC_ASSERT(lock->owner == 0 && lock->lock_count == 0 &&
"Mutex destroyed while being locked.");
RawMutex::destroy(lock);
@@ -80,6 +112,11 @@ class Mutex final : private RawMutex {
}
LIBC_INLINE MutexError lock() {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (priority_inherit)
+ return pi_mutex.lock(pi_mutex_type(), /*timeout=*/cpp::nullopt,
+ this->pshared);
+#endif
return lock_impl([this] {
// Since timeout is not specified, we do not need to check the return
// value.
@@ -90,6 +127,10 @@ class Mutex final : private RawMutex {
}
LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (priority_inherit)
+ return pi_mutex.lock(pi_mutex_type(), abs_time, this->pshared);
+#endif
return lock_impl([this, abs_time] {
// TODO: check deadlock? POSIX made it optional.
if (this->RawMutex::lock(abs_time, this->pshared))
@@ -99,6 +140,10 @@ class Mutex final : private RawMutex {
}
LIBC_INLINE MutexError unlock() {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (priority_inherit)
+ return pi_mutex.unlock(pi_mutex_type(), this->pshared);
+#endif
if (is_recursive()) {
// lock_count == 0 can happen if previous unlock is
// suspended before signal frame
@@ -121,6 +166,10 @@ class Mutex final : private RawMutex {
}
LIBC_INLINE MutexError try_lock() {
+#ifdef LIBC_TARGET_OS_IS_LINUX
+ if (priority_inherit)
+ return pi_mutex.try_lock(pi_mutex_type());
+#endif
return lock_impl([this] {
if (this->RawMutex::try_lock())
return MutexError::NONE;
diff --git a/libc/src/pthread/pthread_mutex_init.cpp b/libc/src/pthread/pthread_mutex_init.cpp
index 73aad9d13792a..c44b585e89b83 100644
--- a/libc/src/pthread/pthread_mutex_init.cpp
+++ b/libc/src/pthread/pthread_mutex_init.cpp
@@ -45,9 +45,10 @@ LLVM_LIBC_FUNCTION(int, pthread_mutex_init,
is_robust = true;
bool is_pshared = get_mutexattr_pshared(mutexattr) == PTHREAD_PROCESS_SHARED;
+ bool is_priority_inherit = get_mutexattr_priority_inherit(mutexattr);
- new (m) Mutex(/*is_priority_inherit=*/false, is_recursive, is_robust,
- is_pshared, is_error_checking);
+ new (m) Mutex(is_priority_inherit, is_recursive, is_robust, is_pshared,
+ is_error_checking);
return 0;
}
diff --git a/libc/src/pthread/pthread_mutexattr.h b/libc/src/pthread/pthread_mutexattr.h
index be719b9d14997..2becdc34abc6f 100644
--- a/libc/src/pthread/pthread_mutexattr.h
+++ b/libc/src/pthread/pthread_mutexattr.h
@@ -26,7 +26,10 @@ enum class PThreadMutexAttrPos : unsigned int {
PSHARED_SHIFT = 3,
PSHARED_MASK = 0x1 << PSHARED_SHIFT,
- // TODO: Add a mask for protocol and prioceiling when it is supported.
+ PRIORITY_INHERIT_SHIFT = 4,
+ PRIORITY_INHERIT_MASK = 0x1 << PRIORITY_INHERIT_SHIFT,
+
+ // TODO: Add a mask for prioceiling when it is supported.
};
constexpr pthread_mutexattr_t DEFAULT_MUTEXATTR =
@@ -49,6 +52,10 @@ LIBC_INLINE int get_mutexattr_pshared(pthread_mutexattr_t attr) {
unsigned(PThreadMutexAttrPos::PSHARED_SHIFT);
}
+LIBC_INLINE bool get_mutexattr_priority_inherit(pthread_mutexattr_t attr) {
+ return (attr & unsigned(PThreadMutexAttrPos::PRIORITY_INHERIT_MASK)) != 0;
+}
+
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_MUTEXATTR_H
diff --git a/libc/test/integration/src/pthread/pthread_mutex_test.cpp b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
index 1a27fec139f98..f286864118789 100644
--- a/libc/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -15,6 +15,7 @@
#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.h"
#include "src/pthread/pthread_mutexattr_destroy.h"
#include "src/pthread/pthread_mutexattr_init.h"
#include "src/pthread/pthread_mutexattr_settype.h"
@@ -35,6 +36,23 @@ static pthread_mutex_t snapshot_mutex(const void *mutex_storage) {
return snapshot;
}
+static void set_priority_inherit(pthread_mutexattr_t *attr,
+ bool priority_inherit) {
+ if (priority_inherit)
+ *attr |=
+ unsigned(LIBC_NAMESPACE::PThreadMutexAttrPos::PRIORITY_INHERIT_MASK);
+}
+
+static void init_mutex(pthread_mutex_t *mutex, bool priority_inherit) {
+ pthread_mutexattr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_init(&attr), 0);
+ set_priority_inherit(&attr, priority_inherit);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(mutex, &attr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
+ pthread_mutex_t snapshot = snapshot_mutex(mutex);
+ ASSERT_EQ(bool(snapshot.__priority_inherit), priority_inherit);
+}
+
pthread_mutex_t mutex;
static int shared_int = START;
@@ -53,8 +71,9 @@ void *counter([[maybe_unused]] void *arg) {
return nullptr;
}
-void relay_counter() {
- ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&mutex, nullptr), 0);
+void relay_counter(bool priority_inherit) {
+ shared_int = START;
+ init_mutex(&mutex, priority_inherit);
// The idea of this test is that two competing threads will update
// a counter only if the other thread has updated it.
@@ -98,9 +117,9 @@ void *stepper([[maybe_unused]] void *arg) {
return nullptr;
}
-void wait_and_step() {
- ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&start_lock, nullptr), 0);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&step_lock, nullptr), 0);
+void wait_and_step(bool priority_inherit) {
+ init_mutex(&start_lock, priority_inherit);
+ init_mutex(&step_lock, priority_inherit);
// In this test, we start a new thread but block it before it can make a
// step. Once we ensure that the thread is blocked, we unblock it.
@@ -143,9 +162,9 @@ void wait_and_step() {
LIBC_NAMESPACE::pthread_mutex_destroy(&step_lock);
}
-void trylock_test() {
+void trylock_test(bool priority_inherit) {
pthread_mutex_t trylock_mutex;
- ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&trylock_mutex, nullptr), 0);
+ init_mutex(&trylock_mutex, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&trylock_mutex), 0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_trylock(&trylock_mutex), EBUSY);
@@ -164,13 +183,14 @@ void *trylock_other_thread(void *arg) {
return reinterpret_cast<void *>(uintptr_t(result));
}
-void recursive_mutex_test() {
+void recursive_mutex_test(bool priority_inherit) {
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);
+ set_priority_inherit(&attr, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&recursive_mutex, &attr), 0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
@@ -238,13 +258,14 @@ void initializer_acts_the_same_as_null_attr() {
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_destroy(&mutex_from_init), 0);
}
-void error_checking_mutex_test() {
+void error_checking_mutex_test(bool priority_inherit) {
pthread_mutexattr_t attr;
pthread_mutex_t error_checking_mutex;
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_init(&attr), 0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_settype(&attr,
PTHREAD_MUTEX_ERRORCHECK),
0);
+ set_priority_inherit(&attr, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&error_checking_mutex, &attr),
0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
@@ -288,9 +309,10 @@ void *waiter_func(void *) {
return nullptr;
}
-void multiple_waiters() {
- LIBC_NAMESPACE::pthread_mutex_init(&multiple_waiter_lock, nullptr);
- LIBC_NAMESPACE::pthread_mutex_init(&counter_lock, nullptr);
+void multiple_waiters(bool priority_inherit) {
+ wait_count = 0;
+ init_mutex(&multiple_waiter_lock, priority_inherit);
+ init_mutex(&counter_lock, priority_inherit);
LIBC_NAMESPACE::pthread_mutex_lock(&multiple_waiter_lock);
pthread_t waiters[THREAD_COUNT];
@@ -323,12 +345,15 @@ void multiple_waiters() {
}
TEST_MAIN() {
- relay_counter();
- wait_and_step();
- trylock_test();
- recursive_mutex_test();
+ for (int i = 0; i < 2; ++i) {
+ bool priority_inherit = i & 1;
+ relay_counter(priority_inherit);
+ wait_and_step(priority_inherit);
+ trylock_test(priority_inherit);
+ recursive_mutex_test(priority_inherit);
+ error_checking_mutex_test(priority_inherit);
+ multiple_waiters(priority_inherit);
+ }
initializer_acts_the_same_as_null_attr();
- error_checking_mutex_test();
- multiple_waiters();
return 0;
}
>From 98ad35cd46306a9346fade203468d37d4ec07f76 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sat, 23 May 2026 22:16:52 -0400
Subject: [PATCH 02/10] fix a correctness issue found by copilot
---
libc/src/__support/threads/linux/pi_mutex.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index fe898faa26bd3..bcabbd01f95ac 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -55,7 +55,7 @@ class PIMutex {
cpp::MemoryOrder::RELAXED))
return MutexError::NONE;
- if (old_owner == current) {
+ if (current == (old_owner & FUTEX_TID_MASK)) {
switch (type) {
case Type::Normal:
break;
>From 1cf7d3ba47358408523215e8e3aa792ff026e340 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 11:48:47 -0400
Subject: [PATCH 03/10] switch to intrusive approach
---
.../include/llvm-libc-macros/pthread-macros.h | 22 +++------
libc/include/llvm-libc-types/__mutex_type.h | 6 +--
libc/src/__support/threads/linux/pi_mutex.h | 49 ++++++++++---------
libc/src/__support/threads/unix_mutex.h | 42 ++++++----------
4 files changed, 50 insertions(+), 69 deletions(-)
diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h
index e23d2873e0891..d6518189f1ccb 100644
--- a/libc/include/llvm-libc-macros/pthread-macros.h
+++ b/libc/include/llvm-libc-macros/pthread-macros.h
@@ -32,24 +32,18 @@
#ifdef __linux__
#define PTHREAD_MUTEX_INITIALIZER \
{ \
- /* .__ftxw = */ {0}, \
- /* .__priority_inherit = */ 0, \
- /* .__recursive = */ 0, \
- /* .__robust = */ 0, \
- /* .__pshared = */ 0, \
- /* .__error_checking = */ 0, \
- {/* .__owner = */ 0, /* .__lock_count = */ 0}, \
+ /* .__ftxw = */ {0}, /* .__priority_inherit = */ 0, \
+ /* .__recursive = */ 0, /* .__robust = */ 0, \
+ /* .__pshared = */ 0, /* .__error_checking = */ 0, \
+ /* .__owner = */ 0, /* .__lock_count = */ 0, \
}
#else
#define PTHREAD_MUTEX_INITIALIZER \
{ \
- /* .__ftxw = */ {0}, \
- /* .__priority_inherit = */ 0, \
- /* .__recursive = */ 0, \
- /* .__robust = */ 0, \
- /* .__pshared = */ 0, \
- /* .__error_checking = */ 0, \
- {/* .__owner = */ 0, /* .__lock_count = */ 0}, \
+ /* .__ftxw = */ {0}, /* .__priority_inherit = */ 0, \
+ /* .__recursive = */ 0, /* .__robust = */ 0, \
+ /* .__pshared = */ 0, /* .__error_checking = */ 0, \
+ /* .__owner = */ 0, /* .__lock_count = */ 0, \
}
#endif
diff --git a/libc/include/llvm-libc-types/__mutex_type.h b/libc/include/llvm-libc-types/__mutex_type.h
index adc379318e675..40f37c5235f2e 100644
--- a/libc/include/llvm-libc-types/__mutex_type.h
+++ b/libc/include/llvm-libc-types/__mutex_type.h
@@ -26,10 +26,8 @@ typedef struct {
unsigned int __pshared : 1;
unsigned int __error_checking : 1;
- struct {
- pid_t __owner;
- size_t __lock_count;
- };
+ pid_t __owner;
+ size_t __lock_count;
} __mutex_type;
#endif // LLVM_LIBC_TYPES___MUTEX_TYPE_H
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index bcabbd01f95ac..861c42ccb1219 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -24,6 +24,7 @@
#include "src/__support/macros/optimization.h"
#include "src/__support/threads/identifier.h"
#include "src/__support/threads/linux/futex_utils.h"
+#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/mutex_common.h"
#include <linux/futex.h>
@@ -34,20 +35,22 @@
namespace LIBC_NAMESPACE_DECL {
-class PIMutex {
+class PIMutexRef {
+public:
+ enum class Type { Normal, ErrorChecking, Recursive };
+
protected:
- Futex owner;
- // Number of recursive locks minus one.
- size_t recursive_count;
+ Futex &owner;
+ Type type;
+ // Number of recursive locks minus one. This pointer must be provided
+ // if type == Recursive
+ size_t *recursive_count;
public:
- enum class Type { Normal, ErrorChecking, Recursive };
- LIBC_INLINE static void init(PIMutex *mutex) {
- mutex->owner.store(0);
- mutex->recursive_count = 0;
- }
- LIBC_INLINE constexpr PIMutex() : owner(0), recursive_count(0) {}
- LIBC_INLINE MutexError try_lock(Type type) {
+ LIBC_INLINE constexpr PIMutexRef(Futex &owner, Type type,
+ size_t *recursive_count = nullptr)
+ : owner(owner), type(type), recursive_count(recursive_count) {}
+ LIBC_INLINE MutexError try_lock() {
FutexWordType old_owner = 0;
auto current = static_cast<FutexWordType>(internal::gettid());
if (owner.compare_exchange_strong(old_owner, current,
@@ -62,10 +65,10 @@ class PIMutex {
case Type::ErrorChecking:
return MutexError::DEADLOCK;
case Type::Recursive:
- if (LIBC_UNLIKELY(recursive_count ==
+ if (LIBC_UNLIKELY(*recursive_count ==
cpp::numeric_limits<size_t>::max()))
return MutexError::OVERFLOW;
- recursive_count++;
+ *recursive_count += 1;
return MutexError::NONE;
}
}
@@ -73,9 +76,9 @@ class PIMutex {
return MutexError::BUSY;
}
LIBC_INLINE MutexError
- lock(Type type, cpp::optional<Futex::Timeout> timeout = cpp::nullopt,
+ lock(cpp::optional<Futex::Timeout> timeout = cpp::nullopt,
bool is_shared = false) {
- MutexError result = try_lock(type);
+ MutexError result = try_lock();
if (result != MutexError::BUSY)
return result;
@@ -110,7 +113,7 @@ class PIMutex {
}
}
}
- LIBC_INLINE MutexError unlock(Type type, bool is_shared) {
+ LIBC_INLINE MutexError unlock(bool is_shared) {
FutexWordType current = static_cast<FutexWordType>(internal::gettid());
FutexWordType old_owner = current;
@@ -126,8 +129,8 @@ class PIMutex {
if (current != (old_owner & FUTEX_TID_MASK))
return MutexError::UNLOCK_WITHOUT_LOCK;
- if (type == Type::Recursive && recursive_count != 0) {
- recursive_count--;
+ if (type == Type::Recursive && *recursive_count != 0) {
+ *recursive_count -= 1;
return MutexError::NONE;
}
@@ -156,17 +159,17 @@ class PIMutex {
return MutexError::BAD_LOCK_STATE;
}
}
- LIBC_INLINE static MutexError destroy(PIMutex *lock) {
+ LIBC_INLINE MutexError destroy() {
FutexWordType old_owner = 0;
- if (lock->owner.compare_exchange_strong(old_owner, 0xffffffff,
- cpp::MemoryOrder::RELAXED,
- cpp::MemoryOrder::RELAXED))
+ if (owner.compare_exchange_strong(
+ old_owner, cpp::numeric_limits<FutexWordType>::max(),
+ cpp::MemoryOrder::RELAXED, cpp::MemoryOrder::RELAXED))
return MutexError::NONE;
return MutexError::BUSY;
}
LIBC_INLINE void reset() {
owner.store(0);
- recursive_count = 0;
+ *recursive_count = 0;
}
};
diff --git a/libc/src/__support/threads/unix_mutex.h b/libc/src/__support/threads/unix_mutex.h
index 9afc3d337db2d..9d49eaf7278b7 100644
--- a/libc/src/__support/threads/unix_mutex.h
+++ b/libc/src/__support/threads/unix_mutex.h
@@ -38,28 +38,20 @@ class Mutex final : private RawMutex {
LIBC_PREFERED_TYPE(bool) unsigned int error_checking : 1;
// TLS address may not work across forked processes. Use thread id instead.
- union {
- struct {
- cpp::Atomic<pid_t> owner;
- size_t lock_count;
- };
-#ifdef LIBC_TARGET_OS_IS_LINUX
- // Special case, when "priority_inherit" is set, we ignore the base mutex
- // and use this field.
- PIMutex pi_mutex;
-#endif
- };
+ cpp::Atomic<pid_t> owner;
+ size_t lock_count;
// CndVar needs to access Mutex as RawMutex
friend class CndVar;
#ifdef LIBC_TARGET_OS_IS_LINUX
- LIBC_INLINE PIMutex::Type pi_mutex_type() const {
+ LIBC_INLINE PIMutexRef pi_mutex_ref() {
+ PIMutexRef::Type type = PIMutexRef::Type::Normal;
if (is_recursive())
- return PIMutex::Type::Recursive;
- if (is_error_checking())
- return PIMutex::Type::ErrorChecking;
- return PIMutex::Type::Normal;
+ type = PIMutexRef::Type::Recursive;
+ else if (is_error_checking())
+ type = PIMutexRef::Type::ErrorChecking;
+ return {get_raw_futex(), type, &lock_count};
}
#endif
@@ -93,17 +85,12 @@ class Mutex final : private RawMutex {
bool is_error_checking = false)
: RawMutex(), priority_inherit(is_priority_inherit),
recursive(is_recursive), robust(is_robust), pshared(is_pshared),
- error_checking(is_error_checking), owner(0), lock_count(0) {
-#ifdef LIBC_TARGET_OS_IS_LINUX
- if (is_priority_inherit)
- new (&pi_mutex) PIMutex{};
-#endif
- }
+ error_checking(is_error_checking), owner(0), lock_count(0) {}
LIBC_INLINE static MutexError destroy(Mutex *lock) {
#ifdef LIBC_TARGET_OS_IS_LINUX
if (lock->priority_inherit)
- return PIMutex::destroy(&lock->pi_mutex);
+ return lock->pi_mutex_ref().destroy();
#endif
LIBC_ASSERT(lock->owner == 0 && lock->lock_count == 0 &&
"Mutex destroyed while being locked.");
@@ -114,8 +101,7 @@ class Mutex final : private RawMutex {
LIBC_INLINE MutexError lock() {
#ifdef LIBC_TARGET_OS_IS_LINUX
if (priority_inherit)
- return pi_mutex.lock(pi_mutex_type(), /*timeout=*/cpp::nullopt,
- this->pshared);
+ return pi_mutex_ref().lock(/*timeout=*/cpp::nullopt, this->pshared);
#endif
return lock_impl([this] {
// Since timeout is not specified, we do not need to check the return
@@ -129,7 +115,7 @@ class Mutex final : private RawMutex {
LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) {
#ifdef LIBC_TARGET_OS_IS_LINUX
if (priority_inherit)
- return pi_mutex.lock(pi_mutex_type(), abs_time, this->pshared);
+ return pi_mutex_ref().lock(abs_time, this->pshared);
#endif
return lock_impl([this, abs_time] {
// TODO: check deadlock? POSIX made it optional.
@@ -142,7 +128,7 @@ class Mutex final : private RawMutex {
LIBC_INLINE MutexError unlock() {
#ifdef LIBC_TARGET_OS_IS_LINUX
if (priority_inherit)
- return pi_mutex.unlock(pi_mutex_type(), this->pshared);
+ return pi_mutex_ref().unlock(this->pshared);
#endif
if (is_recursive()) {
// lock_count == 0 can happen if previous unlock is
@@ -168,7 +154,7 @@ class Mutex final : private RawMutex {
LIBC_INLINE MutexError try_lock() {
#ifdef LIBC_TARGET_OS_IS_LINUX
if (priority_inherit)
- return pi_mutex.try_lock(pi_mutex_type());
+ return pi_mutex_ref().try_lock();
#endif
return lock_impl([this] {
if (this->RawMutex::try_lock())
>From 37d622863c0054ab88358556949539a490ac1bf2 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 12:18:27 -0400
Subject: [PATCH 04/10] fix realtime flag and deadlock
---
libc/src/__support/threads/linux/pi_mutex.h | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index 861c42ccb1219..7bd7b0efc5c0e 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -87,7 +87,9 @@ class PIMutexRef {
ensure_monotonicity(*timeout);
#endif
- int op = is_shared ? FUTEX_LOCK_PI : FUTEX_LOCK_PI_PRIVATE;
+ int op = is_shared ? FUTEX_LOCK_PI2 : FUTEX_LOCK_PI2_PRIVATE;
+ if (timeout && timeout->is_realtime())
+ op |= FUTEX_CLOCK_REALTIME;
for (;;) {
ErrorOr<int> ret = linux_syscalls::syscall_checked<int>(
/*syscall_number=*/FUTEX_SYSCALL_ID,
@@ -107,6 +109,8 @@ class PIMutexRef {
case ETIMEDOUT:
return MutexError::TIMEOUT;
case EDEADLK:
+ if (type == Type::Normal)
+ continue;
return MutexError::DEADLOCK;
default:
return MutexError::BAD_LOCK_STATE;
@@ -169,7 +173,8 @@ class PIMutexRef {
}
LIBC_INLINE void reset() {
owner.store(0);
- *recursive_count = 0;
+ if (recursive_count)
+ *recursive_count = 0;
}
};
>From 032526b399511110866dc810b14651eda94a9c80 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 24 May 2026 09:27:14 -0700
Subject: [PATCH 05/10] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot at users.noreply.github.com>
---
libc/src/__support/threads/linux/CMakeLists.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 2e1e701f9ec84..59fced587d9c9 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -34,10 +34,13 @@ add_header_library(
libc.hdr.errno_macros
libc.src.__support.OSUtil.osutil
libc.src.__support.CPP.limits
+ libc.src.__support.CPP.new
libc.src.__support.CPP.optional
libc.src.__support.time.monotonicity
libc.src.__support.macros.attributes
libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+ libc.src.__support.threads.identifier
)
add_object_library(
>From 07d48aed27e5502096feb6a0f43192199c4053e3 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 12:31:14 -0400
Subject: [PATCH 06/10] fix deadlock
---
libc/src/__support/threads/linux/pi_mutex.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index 7bd7b0efc5c0e..624af8dc0e6d4 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -109,8 +109,10 @@ class PIMutexRef {
case ETIMEDOUT:
return MutexError::TIMEOUT;
case EDEADLK:
- if (type == Type::Normal)
- continue;
+ // indefinitely park the thread if type == Type::Normal.
+ // use Futex::wait to avoid burning the CPU time
+ while (type == Type::Normal)
+ owner.wait(owner, /*timeout=*/cpp::nullopt, is_shared);
return MutexError::DEADLOCK;
default:
return MutexError::BAD_LOCK_STATE;
>From f82ab298c6b227d007c8d6b71064d2c941f2ef72 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 12:45:30 -0400
Subject: [PATCH 07/10] more
---
libc/src/__support/threads/linux/CMakeLists.txt | 1 -
libc/src/__support/threads/linux/pi_mutex.h | 1 +
.../integration/src/pthread/pthread_mutex_test.cpp | 10 +++++-----
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 59fced587d9c9..204ee9e1945ec 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -40,7 +40,6 @@ add_header_library(
libc.src.__support.macros.attributes
libc.src.__support.macros.config
libc.src.__support.macros.optimization
- libc.src.__support.threads.identifier
)
add_object_library(
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index 624af8dc0e6d4..ec87c55cfe410 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -15,6 +15,7 @@
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_PI_MUTEX_H
#include "hdr/errno_macros.h"
+#include "hdr/types/size_t.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/optional.h"
diff --git a/libc/test/integration/src/pthread/pthread_mutex_test.cpp b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
index f286864118789..e1b92d11fa9cf 100644
--- a/libc/test/integration/src/pthread/pthread_mutex_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -36,8 +36,8 @@ static pthread_mutex_t snapshot_mutex(const void *mutex_storage) {
return snapshot;
}
-static void set_priority_inherit(pthread_mutexattr_t *attr,
- bool priority_inherit) {
+static void enable_priority_inherit_if_needed(pthread_mutexattr_t *attr,
+ bool priority_inherit) {
if (priority_inherit)
*attr |=
unsigned(LIBC_NAMESPACE::PThreadMutexAttrPos::PRIORITY_INHERIT_MASK);
@@ -46,7 +46,7 @@ static void set_priority_inherit(pthread_mutexattr_t *attr,
static void init_mutex(pthread_mutex_t *mutex, bool priority_inherit) {
pthread_mutexattr_t attr;
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_init(&attr), 0);
- set_priority_inherit(&attr, priority_inherit);
+ enable_priority_inherit_if_needed(&attr, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(mutex, &attr), 0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
pthread_mutex_t snapshot = snapshot_mutex(mutex);
@@ -190,7 +190,7 @@ void recursive_mutex_test(bool priority_inherit) {
ASSERT_EQ(
LIBC_NAMESPACE::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE),
0);
- set_priority_inherit(&attr, priority_inherit);
+ enable_priority_inherit_if_needed(&attr, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&recursive_mutex, &attr), 0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
@@ -265,7 +265,7 @@ void error_checking_mutex_test(bool priority_inherit) {
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_settype(&attr,
PTHREAD_MUTEX_ERRORCHECK),
0);
- set_priority_inherit(&attr, priority_inherit);
+ enable_priority_inherit_if_needed(&attr, priority_inherit);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&error_checking_mutex, &attr),
0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_mutexattr_destroy(&attr), 0);
>From 55b748d9babd940c1a22239f81aea4886f1d36f0 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 12:46:38 -0400
Subject: [PATCH 08/10] more
---
libc/src/__support/threads/linux/CMakeLists.txt | 2 +-
libc/src/__support/threads/linux/pi_mutex.h | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 204ee9e1945ec..87e7ce122acde 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -32,9 +32,9 @@ add_header_library(
.futex_utils
.futex_word_type
libc.hdr.errno_macros
+ libc.hdr.types.size_t
libc.src.__support.OSUtil.osutil
libc.src.__support.CPP.limits
- libc.src.__support.CPP.new
libc.src.__support.CPP.optional
libc.src.__support.time.monotonicity
libc.src.__support.macros.attributes
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index ec87c55cfe410..237d7f9d09df4 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -17,7 +17,6 @@
#include "hdr/errno_macros.h"
#include "hdr/types/size_t.h"
#include "src/__support/CPP/limits.h"
-#include "src/__support/CPP/new.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/attributes.h"
>From b0d56e6dd80b3af66eaded0fe1bed0453fae5562 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Sun, 24 May 2026 13:10:00 -0400
Subject: [PATCH 09/10] more
---
libc/docs/dev/undefined_behavior.rst | 7 +++++++
libc/src/__support/threads/linux/CMakeLists.txt | 1 +
libc/src/__support/threads/linux/pi_mutex.h | 10 +++++++---
libc/src/pthread/CMakeLists.txt | 1 +
libc/src/pthread/pthread_mutex_lock.cpp | 4 ++++
5 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/libc/docs/dev/undefined_behavior.rst b/libc/docs/dev/undefined_behavior.rst
index b2f8805421292..2feb826ee799b 100644
--- a/libc/docs/dev/undefined_behavior.rst
+++ b/libc/docs/dev/undefined_behavior.rst
@@ -199,3 +199,10 @@ Cyclic joining with more than two threads is not detected.
Concurrent and repeated joinings on the same thread are faulty behaviors, because
target thread's TLS may already be torn down. ``EINVAL`` may be returned if
multiple joinings occur on the same thread but it is not guaranteed to observe.
+
+Invalid Mutex Lock
+------------------------------------------------------
+The kernel may return special errors from futex-like syscalls in edge cases.
+POSIX.1 specifies that ``EINTR`` must be handled inside libc, but it does not
+clearly define how other special errors, such as ``EPERM`` or ``ENOMEM``, should
+be handled. For such cases, we return ``EINVAL``.
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 87e7ce122acde..cafb84932835c 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -40,6 +40,7 @@ add_header_library(
libc.src.__support.macros.attributes
libc.src.__support.macros.config
libc.src.__support.macros.optimization
+ libc.src.__support.macros.null_check
)
add_object_library(
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index 237d7f9d09df4..a162ada51ff83 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -21,6 +21,7 @@
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/threads/identifier.h"
#include "src/__support/threads/linux/futex_utils.h"
@@ -47,9 +48,12 @@ class PIMutexRef {
size_t *recursive_count;
public:
- LIBC_INLINE constexpr PIMutexRef(Futex &owner, Type type,
- size_t *recursive_count = nullptr)
- : owner(owner), type(type), recursive_count(recursive_count) {}
+ LIBC_INLINE PIMutexRef(Futex &owner, Type type,
+ size_t *recursive_count = nullptr)
+ : owner(owner), type(type), recursive_count(recursive_count) {
+ if (type == Type::Recursive)
+ LIBC_CRASH_ON_NULLPTR(recursive_count);
+ }
LIBC_INLINE MutexError try_lock() {
FutexWordType old_owner = 0;
auto current = static_cast<FutexWordType>(internal::gettid());
diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 7901d5ce67a5e..644ac32b4b32f 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -467,6 +467,7 @@ add_entrypoint_object(
libc.hdr.errno_macros
libc.include.pthread
libc.src.__support.threads.mutex
+ libc.src.__support.macros.null_check
)
add_entrypoint_object(
diff --git a/libc/src/pthread/pthread_mutex_lock.cpp b/libc/src/pthread/pthread_mutex_lock.cpp
index 857228d8d4240..ee75fbd1aac69 100644
--- a/libc/src/pthread/pthread_mutex_lock.cpp
+++ b/libc/src/pthread/pthread_mutex_lock.cpp
@@ -11,6 +11,7 @@
#include "hdr/errno_macros.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
#include "src/__support/threads/mutex.h"
#include <pthread.h>
@@ -18,9 +19,12 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, pthread_mutex_lock, (pthread_mutex_t * mutex)) {
+ LIBC_CRASH_ON_NULLPTR(mutex);
MutexError err = reinterpret_cast<Mutex *>(mutex)->lock();
if (err == MutexError::DEADLOCK)
return EDEADLK;
+ else if (err != MutexError::NONE)
+ return EINVAL;
// TODO: When the Mutex class supports all the possible error conditions
// return the appropriate error value here.
return 0;
>From 162aa46c52f3efa2f0a39262531bdaccfd3d1706 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Wed, 27 May 2026 00:05:54 -0400
Subject: [PATCH 10/10] address CRs
---
.../linux/syscall_wrappers/CMakeLists.txt | 14 +++++++
.../OSUtil/linux/syscall_wrappers/futex.h | 41 +++++++++++++++++++
.../__support/threads/linux/CMakeLists.txt | 1 +
libc/src/__support/threads/linux/pi_mutex.h | 8 ++--
4 files changed, 59 insertions(+), 5 deletions(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/futex.h
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 02dfbfdcb49fb..3e0535a776175 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -1,3 +1,17 @@
+add_header_library(
+ futex
+ HDRS
+ futex.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.stdint_proxy
+ libc.hdr.types.struct_timespec
+ libc.include.sys_syscall
+)
+
add_header_library(
getrandom
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/futex.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/futex.h
new file mode 100644
index 0000000000000..07c826b693132
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/futex.h
@@ -0,0 +1,41 @@
+//===-- Syscall wrapper for futex -------------------------------*- 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_OSUTIL_LINUX_SYSCALL_WRAPPERS_FUTEX_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_FUTEX_H
+
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+#if SYS_futex
+LIBC_INLINE_VAR constexpr long FUTEX_SYSCALL_NUMBER = SYS_futex;
+#elif defined(SYS_futex_time64)
+LIBC_INLINE_VAR constexpr long FUTEX_SYSCALL_NUMBER = SYS_futex_time64;
+#else
+#error "futex and futex_time64 syscalls not available."
+#endif
+
+LIBC_INLINE ErrorOr<int> futex(void *futex_addr, int op, uint32_t val,
+ const timespec *timeout, void *futex_addr2,
+ uint32_t val3) {
+ return syscall_checked<int>(FUTEX_SYSCALL_NUMBER, futex_addr, op, val,
+ timeout, futex_addr2, val3);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_FUTEX_H
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index cafb84932835c..6f9f6a9d40644 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -34,6 +34,7 @@ add_header_library(
libc.hdr.errno_macros
libc.hdr.types.size_t
libc.src.__support.OSUtil.osutil
+ libc.src.__support.OSUtil.linux.syscall_wrappers.futex
libc.src.__support.CPP.limits
libc.src.__support.CPP.optional
libc.src.__support.time.monotonicity
diff --git a/libc/src/__support/threads/linux/pi_mutex.h b/libc/src/__support/threads/linux/pi_mutex.h
index a162ada51ff83..7243cd2ae8b8e 100644
--- a/libc/src/__support/threads/linux/pi_mutex.h
+++ b/libc/src/__support/threads/linux/pi_mutex.h
@@ -18,7 +18,7 @@
#include "hdr/types/size_t.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
-#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/futex.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
@@ -95,8 +95,7 @@ class PIMutexRef {
if (timeout && timeout->is_realtime())
op |= FUTEX_CLOCK_REALTIME;
for (;;) {
- ErrorOr<int> ret = linux_syscalls::syscall_checked<int>(
- /*syscall_number=*/FUTEX_SYSCALL_ID,
+ ErrorOr<int> ret = linux_syscalls::futex(
/*futex_addr=*/&owner,
/*op=*/op,
/*ignored=*/0,
@@ -150,8 +149,7 @@ class PIMutexRef {
return MutexError::NONE;
int op = is_shared ? FUTEX_UNLOCK_PI : FUTEX_UNLOCK_PI_PRIVATE;
- ErrorOr<int> ret = linux_syscalls::syscall_checked<int>(
- /*syscall_number=*/FUTEX_SYSCALL_ID,
+ ErrorOr<int> ret = linux_syscalls::futex(
/*futex_addr=*/&owner,
/*op=*/op,
/*ignored=*/0,
More information about the libc-commits
mailing list