[libc-commits] [libc] [llvm] condvar rework (PR #192748)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Fri Apr 17 18:11:20 PDT 2026
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/192748
>From 3687f9e71dba6a52d3dce5beea2b03b78939a451 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Apr 2026 09:42:04 -0400
Subject: [PATCH 1/6] [libc] remove staled header libraries
---
libc/src/__support/CMakeLists.txt | 9 ---------
libc/src/__support/OSUtil/linux/CMakeLists.txt | 13 -------------
2 files changed, 22 deletions(-)
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index ce69d4e6f69ac..84de7766d071b 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -105,15 +105,6 @@ add_header_library(
libc.hdr.stdint_proxy
)
-add_header_library(
- bit
- HDRS
- bit.h
- DEPENDS
- libc.src.__support.macros.attributes
- libc.src.__support.CPP.type_traits
-)
-
add_header_library(
math_extras
HDRS
diff --git a/libc/src/__support/OSUtil/linux/CMakeLists.txt b/libc/src/__support/OSUtil/linux/CMakeLists.txt
index 8a686d4bce7dc..b02fa141fa456 100644
--- a/libc/src/__support/OSUtil/linux/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/CMakeLists.txt
@@ -38,19 +38,6 @@ add_header_library(
libc.src.__support.threads.callonce
)
-add_header_library(
- getrandom
- HDRS
- getrandom.h
- DEPENDS
- libc.src.__support.OSUtil.osutil
- libc.src.__support.common
- libc.src.__support.error_or
- libc.src.__support.macros.config
- libc.hdr.types.ssize_t
- libc.include.sys_syscall
-)
-
add_header_library(
vdso_sym
HDRS
>From cb7073500dd5ac4fcbaa5f525ea9ce2d8a38325b Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Apr 2026 17:53:55 -0400
Subject: [PATCH 2/6] wip
---
.codex | 0
libc/src/__support/threads/CMakeLists.txt | 13 +-
libc/src/__support/threads/CndVar.h | 249 ++++++++++++++++--
.../__support/threads/linux/CMakeLists.txt | 16 --
libc/src/__support/threads/linux/CndVar.cpp | 106 --------
libc/src/__support/threads/linux/barrier.cpp | 15 +-
libc/src/__support/threads/raw_mutex.h | 1 +
libc/src/__support/threads/unix_mutex.h | 6 +
libc/src/threads/linux/cnd_destroy.cpp | 2 +-
libc/src/threads/linux/cnd_init.cpp | 5 +-
libc/src/threads/linux/cnd_wait.cpp | 3 +-
11 files changed, 251 insertions(+), 165 deletions(-)
create mode 100644 .codex
delete mode 100644 libc/src/__support/threads/linux/CndVar.cpp
diff --git a/.codex b/.codex
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 290c27fa46d4f..0623a8743ad7a 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -147,12 +147,17 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.callonce)
)
endif()
-if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
- add_object_library(
+if(TARGET libc.src.__support.threads.futex_utils)
+ add_header_library(
CndVar
- ALIAS
+ HDRS
+ CndVar.h
DEPENDS
- .${LIBC_TARGET_OS}.CndVar
+ .futex_utils
+ .mutex
+ .raw_mutex
+ libc.hdr.stdint_proxy
+ libc.src.__support.CPP.mutex
)
endif()
diff --git a/libc/src/__support/threads/CndVar.h b/libc/src/__support/threads/CndVar.h
index a423b65bfb0b5..8d32f77b6df47 100644
--- a/libc/src/__support/threads/CndVar.h
+++ b/libc/src/__support/threads/CndVar.h
@@ -6,49 +6,248 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_CNDVAR_H
-#define LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_CNDVAR_H
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_CNDVAR_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_CNDVAR_H
#include "hdr/stdint_proxy.h" // uint32_t
+#include "src/__support/CPP/mutex.h"
#include "src/__support/macros/config.h"
-#include "src/__support/threads/futex_utils.h" // Futex
-#include "src/__support/threads/mutex.h" // Mutex
-#include "src/__support/threads/raw_mutex.h" // RawMutex
+#include "src/__support/threads/futex_utils.h" // Futex
+#include "src/__support/threads/mutex.h" // Mutex
+#include "src/__support/threads/raw_mutex.h" // RawMutex
+
+#ifndef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+#define LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY 1
+#endif
namespace LIBC_NAMESPACE_DECL {
class CndVar {
- enum CndWaiterStatus : uint32_t {
- WS_Waiting = 0xE,
- WS_Signalled = 0x5,
+ enum WaiterState : uint8_t {
+ // Initial state after entering the wait queue.
+ Waiting = 0,
+ // A signal has been received.
+ Signalled = 1,
+ // A cancellation has been requested.
+ Cancelled = 2,
+ };
+
+ struct WaiterHeader {
+ WaiterHeader *prev;
+ WaiterHeader *next;
+
+ LIBC_INLINE void ensure_queue_initialization() {
+ if (LIBC_UNLIKELY(prev == nullptr)) {
+ prev = next = this;
+ }
+ }
+
+ LIBC_INLINE void push_back(WaiterHeader *waiter) {
+ ensure_queue_initialization();
+ waiter->next = this;
+ waiter->prev = prev;
+ waiter->next->prev = waiter;
+ waiter->prev->next = waiter;
+ }
+
+ LIBC_INLINE static void remove(WaiterHeader *waiter) {
+ waiter->next->prev = waiter->prev;
+ waiter->prev->next = waiter->next;
+ waiter->prev = waiter->next = waiter;
+ }
+
+ LIBC_INLINE WaiterHeader *pop_front() {
+ ensure_queue_initialization();
+ if (next == this)
+ return nullptr;
+ WaiterHeader *first = next;
+ remove(first);
+ return first;
+ }
};
+ struct CndWaiter : WaiterHeader {
+ cpp::Atomic<Futex *> sender_futex;
+ RawMutex barrier;
+ cpp::Atomic<uint8_t> state;
+
+ LIBC_INLINE CndWaiter()
+ : WaiterHeader{}, sender_futex(nullptr), barrier{}, state{Waiting} {
+ // this lock should always success as no contention is possible
+ (void)barrier.try_lock();
+ }
- struct CndWaiter {
- Futex futex_word = WS_Waiting;
- CndWaiter *next = nullptr;
+ LIBC_INLINE void confirm_cancellation() {
+ Futex *sender = sender_futex.load();
+ if (sender && sender->fetch_sub(1) == 1)
+ sender->notify_one();
+ }
};
- CndWaiter *waitq_front;
- CndWaiter *waitq_back;
- RawMutex qmtx;
+ union {
+ struct {
+ RawMutex queue_lock;
+ WaiterHeader waiter_queue;
+ };
+ struct {
+ Futex shared_futex;
+ cpp::Atomic<size_t> shared_waiters;
+ };
+ };
public:
- LIBC_INLINE static int init(CndVar *cv) {
- cv->waitq_front = cv->waitq_back = nullptr;
- RawMutex::init(&cv->qmtx);
- return 0;
+ enum class CndVarResult {
+ Success,
+ MutexError,
+ Timeout,
+ };
+
+ using Timeout = internal::AbsTimeout;
+
+ LIBC_INLINE constexpr CndVar() : queue_lock{}, waiter_queue{} {}
+
+ LIBC_INLINE void reset() {
+ queue_lock.reset();
+ waiter_queue.prev = nullptr;
+ waiter_queue.next = nullptr;
+ }
+
+ // TODO: register callback for pthread cancellation
+ LIBC_INLINE CndVarResult wait(Mutex *mutex,
+ cpp::optional<Timeout> timeout = cpp::nullopt,
+ bool is_shared = false) {
+#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+ if (timeout)
+ ensure_monotonicity(*timeout);
+#endif
+
+ if (is_shared) {
+ shared_waiters.fetch_add(1);
+ FutexWordType old_val = shared_futex.load();
+ mutex->unlock();
+ ErrorOr<int> result =
+ shared_futex.wait(old_val, timeout, /*is_pshared=*/true);
+ shared_waiters.fetch_sub(1);
+ MutexError mutex_result = mutex->lock();
+ if (!result.has_value() && result.error() == ETIMEDOUT)
+ return CndVarResult::Timeout;
+ return mutex_result == MutexError::NONE ? CndVarResult::Success
+ : CndVarResult::MutexError;
+ }
+
+ CndWaiter waiter{};
+ // Register the waiter to the queue.
+ {
+ cpp::lock_guard lock(queue_lock);
+ waiter_queue.push_back(&waiter);
+ }
+
+ // Unlock the mutex and wait for the signal.
+ mutex->unlock();
+ bool locked = waiter.barrier.lock(timeout, /*is_shared=*/false);
+
+ // if we wake up and find that we are still waiting, this means
+ // timeout has been reached.
+ uint8_t old_state = Waiting;
+ if (waiter.state.compare_exchange_strong(old_state, Cancelled,
+ cpp::MemoryOrder::ACQ_REL)) {
+ // we haven't consumed the signal before timeout reaches.
+ {
+ cpp::lock_guard lock(queue_lock);
+ WaiterHeader::remove(&waiter);
+ }
+ waiter.confirm_cancellation();
+ } else if (!locked) {
+ // Whenever a signal is already consumed, we compete for the mutex
+ // in the FIFO order of the queue.
+ waiter.barrier.lock();
+ }
+
+ MutexError mutex_result = mutex->lock();
+ if (waiter.next != &waiter) {
+ auto *next_waiter = static_cast<CndWaiter *>(waiter.next);
+ WaiterHeader::remove(&waiter);
+ auto &next_barrier_futex = next_waiter->barrier.get_raw_futex();
+ auto &mutex_futex = mutex->get_raw_futex();
+ FutexWordType prev = next_barrier_futex.exchange(
+ RawMutex::UNLOCKED, cpp::MemoryOrder::RELEASE);
+ if (prev == RawMutex::IN_CONTENTION)
+ if (!mutex->can_be_requeued() ||
+ !next_barrier_futex
+ .requeue_to(mutex_futex, cpp::nullopt, 0, 1,
+ /*is_shared=*/false)
+ .has_value())
+ next_waiter->barrier.wake(/*is_shared=*/false);
+ }
+ if (mutex_result != MutexError::NONE)
+ return CndVarResult::MutexError;
+ return old_state == Waiting ? CndVarResult::Timeout : CndVarResult::Success;
}
- LIBC_INLINE static void destroy(CndVar *cv) {
- cv->waitq_front = cv->waitq_back = nullptr;
+private:
+ LIBC_INLINE void notify(bool broadcast, bool is_shared = false) {
+ if (is_shared) {
+ if (shared_waiters.load() == 0)
+ return;
+ shared_futex.fetch_add(1);
+ if (broadcast)
+ shared_futex.notify_all();
+ else
+ shared_futex.notify_one();
+ return;
+ }
+
+ Futex sender_futex{0};
+ auto wait_unregisteration_finish = [&]() {
+ constexpr size_t LIMIT = 100;
+ for (size_t i = 0; i < LIMIT; ++i) {
+ if (sender_futex.load(cpp::MemoryOrder::RELAXED) == 0)
+ break;
+ sleep_briefly();
+ }
+ while (auto remaining = sender_futex.load(cpp::MemoryOrder::RELAXED))
+ sender_futex.wait(remaining, cpp::nullopt, /*is_pshared=*/false);
+ };
+ CndWaiter *head = nullptr;
+ CndWaiter *cursor = nullptr;
+ size_t limit = broadcast ? cpp::numeric_limits<size_t>::max() : 1;
+ {
+ cpp::lock_guard lock(queue_lock);
+ for (cursor = static_cast<CndWaiter *>(waiter_queue.next);
+ cursor != &waiter_queue;
+ cursor = static_cast<CndWaiter *>(cursor->next)) {
+ if (limit == 0)
+ break;
+ uint8_t expected = Waiting;
+ if (!cursor->state.compare_exchange_strong(expected, Signalled)) {
+ sender_futex.fetch_add(1);
+ cursor->sender_futex.store(&sender_futex);
+ continue;
+ }
+ if (!head)
+ head = cursor;
+ limit--;
+ }
+ // remove everything before cursor
+ auto removed_head = waiter_queue.next;
+ auto removed_tail = cursor->prev;
+ waiter_queue.next = cursor;
+ cursor->prev = &waiter_queue;
+ removed_tail->next = removed_head;
+ removed_head->prev = removed_tail;
+ }
+ wait_unregisteration_finish();
+ if (head)
+ head->barrier.unlock();
}
- // Returns 0 on success, -1 on error.
- int wait(Mutex *m);
- void notify_one();
- void broadcast();
+public:
+ LIBC_INLINE void notify_one(bool is_shared = false) { notify(1, is_shared); }
+
+ LIBC_INLINE void broadcast(bool is_shared = false) {
+ notify(cpp::numeric_limits<size_t>::max(), is_shared);
+ }
};
} // namespace LIBC_NAMESPACE_DECL
-#endif // LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_CNDVAR_H
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_CNDVAR_H
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 5ada10ed04b88..bb19588884087 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -65,22 +65,6 @@ add_header_library(
libc.src.__support.macros.optimization
)
-add_object_library(
- CndVar
- SRCS
- CndVar.cpp
- HDRS
- ../CndVar.h
- DEPENDS
- libc.hdr.stdint_proxy
- libc.include.sys_syscall
- libc.src.__support.OSUtil.osutil
- libc.src.__support.threads.linux.futex_word_type
- libc.src.__support.threads.mutex
- libc.src.__support.threads.raw_mutex
- libc.src.__support.CPP.mutex
-)
-
add_object_library(
barrier
HDRS
diff --git a/libc/src/__support/threads/linux/CndVar.cpp b/libc/src/__support/threads/linux/CndVar.cpp
deleted file mode 100644
index 60424673e819c..0000000000000
--- a/libc/src/__support/threads/linux/CndVar.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//===-- Utility condition variable class ------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "src/__support/threads/CndVar.h"
-#include "src/__support/CPP/mutex.h"
-#include "src/__support/OSUtil/syscall.h" // syscall_impl
-#include "src/__support/macros/config.h"
-#include "src/__support/threads/linux/futex_word.h" // FutexWordType
-#include "src/__support/threads/mutex.h" // Mutex
-#include "src/__support/threads/raw_mutex.h" // RawMutex
-
-#include <sys/syscall.h> // For syscall numbers.
-
-namespace LIBC_NAMESPACE_DECL {
-
-int CndVar::wait(Mutex *m) {
- // The goal is to perform "unlock |m| and wait" in an
- // atomic operation. However, it is not possible to do it
- // in the true sense so we do it in spirit. Before unlocking
- // |m|, a new waiter object is added to the waiter queue with
- // the waiter queue locked. Iff a signalling thread signals
- // the waiter before the waiter actually starts waiting, the
- // wait operation will not begin at all and the waiter immediately
- // returns.
-
- CndWaiter waiter;
- {
- cpp::lock_guard ml(qmtx);
- CndWaiter *old_back = nullptr;
- if (waitq_front == nullptr) {
- waitq_front = waitq_back = &waiter;
- } else {
- old_back = waitq_back;
- waitq_back->next = &waiter;
- waitq_back = &waiter;
- }
-
- if (m->unlock() != MutexError::NONE) {
- // If we do not remove the queued up waiter before returning,
- // then another thread can potentially signal a non-existing
- // waiter. Note also that we do this with |qmtx| locked. This
- // ensures that another thread will not signal the withdrawing
- // waiter.
- waitq_back = old_back;
- if (waitq_back == nullptr)
- waitq_front = nullptr;
- else
- waitq_back->next = nullptr;
-
- return -1;
- }
- }
-
- waiter.futex_word.wait(WS_Waiting, cpp::nullopt, true);
-
- // At this point, if locking |m| fails, we can simply return as the
- // queued up waiter would have been removed from the queue.
- auto err = m->lock();
- return err == MutexError::NONE ? 0 : -1;
-}
-
-void CndVar::notify_one() {
- // We don't use an RAII locker in this method as we want to unlock
- // |qmtx| and signal the waiter using a single FUTEX_WAKE_OP signal.
- qmtx.lock();
- if (waitq_front == nullptr)
- qmtx.unlock();
-
- CndWaiter *first = waitq_front;
- waitq_front = waitq_front->next;
- if (waitq_front == nullptr)
- waitq_back = nullptr;
-
- qmtx.reset();
-
- // this is a special WAKE_OP, so we use syscall directly
- LIBC_NAMESPACE::syscall_impl<long>(
- FUTEX_SYSCALL_ID, &qmtx.get_raw_futex(), FUTEX_WAKE_OP, 1, 1,
- &first->futex_word.val,
- FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
-}
-
-void CndVar::broadcast() {
- cpp::lock_guard ml(qmtx);
- uint32_t dummy_futex_word;
- CndWaiter *waiter = waitq_front;
- waitq_front = waitq_back = nullptr;
- while (waiter != nullptr) {
- // FUTEX_WAKE_OP is used instead of just FUTEX_WAKE as it allows us to
- // atomically update the waiter status to WS_Signalled before waking
- // up the waiter. A dummy location is used for the other futex of
- // FUTEX_WAKE_OP.
- LIBC_NAMESPACE::syscall_impl<long>(
- FUTEX_SYSCALL_ID, &dummy_futex_word, FUTEX_WAKE_OP, 1, 1,
- &waiter->futex_word.val,
- FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
- waiter = waiter->next;
- }
-}
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/threads/linux/barrier.cpp b/libc/src/__support/threads/linux/barrier.cpp
index cf7207b53094b..175652a70fac2 100644
--- a/libc/src/__support/threads/linux/barrier.cpp
+++ b/libc/src/__support/threads/linux/barrier.cpp
@@ -8,6 +8,7 @@
#include "src/__support/threads/linux/barrier.h"
#include "hdr/errno_macros.h"
+#include "src/__support/CPP/new.h"
#include "src/__support/threads/CndVar.h"
#include "src/__support/threads/mutex.h"
@@ -24,14 +25,8 @@ int Barrier::init(Barrier *b,
b->waiting = 0;
b->blocking = true;
- int err;
- err = CndVar::init(&b->entering);
- if (err != 0)
- return err;
-
- err = CndVar::init(&b->exiting);
- if (err != 0)
- return err;
+ new (&b->entering) CndVar();
+ new (&b->exiting) CndVar();
auto mutex_err = Mutex::init(&b->m, false, false, false, false);
if (mutex_err != MutexError::NONE)
@@ -76,8 +71,8 @@ int Barrier::wait() {
}
int Barrier::destroy(Barrier *b) {
- CndVar::destroy(&b->entering);
- CndVar::destroy(&b->exiting);
+ b->entering.reset();
+ b->exiting.reset();
Mutex::destroy(&b->m);
return 0;
}
diff --git a/libc/src/__support/threads/raw_mutex.h b/libc/src/__support/threads/raw_mutex.h
index 616e4129e87dd..12dec56084e0e 100644
--- a/libc/src/__support/threads/raw_mutex.h
+++ b/libc/src/__support/threads/raw_mutex.h
@@ -45,6 +45,7 @@ class RawMutex {
LIBC_INLINE_VAR static constexpr FutexWordType UNLOCKED = 0b00;
LIBC_INLINE_VAR static constexpr FutexWordType LOCKED = 0b01;
LIBC_INLINE_VAR static constexpr FutexWordType IN_CONTENTION = 0b10;
+ friend class CndVar;
private:
LIBC_INLINE FutexWordType spin(unsigned spin_count) {
diff --git a/libc/src/__support/threads/unix_mutex.h b/libc/src/__support/threads/unix_mutex.h
index aaaa931efe310..ebe5de2c6e7a7 100644
--- a/libc/src/__support/threads/unix_mutex.h
+++ b/libc/src/__support/threads/unix_mutex.h
@@ -30,6 +30,12 @@ class Mutex final : private RawMutex {
pid_t owner;
unsigned long long lock_count;
+ friend class CndVar;
+
+ LIBC_INLINE bool can_be_requeued() const {
+ return !this->pshared && !this->robust;
+ }
+
public:
LIBC_INLINE constexpr Mutex(bool is_timed, bool is_recursive, bool is_robust,
bool is_pshared)
diff --git a/libc/src/threads/linux/cnd_destroy.cpp b/libc/src/threads/linux/cnd_destroy.cpp
index 963991bddfe45..e51728615da9c 100644
--- a/libc/src/threads/linux/cnd_destroy.cpp
+++ b/libc/src/threads/linux/cnd_destroy.cpp
@@ -19,7 +19,7 @@ static_assert(sizeof(CndVar) == sizeof(cnd_t));
LLVM_LIBC_FUNCTION(void, cnd_destroy, (cnd_t * cond)) {
CndVar *cndvar = reinterpret_cast<CndVar *>(cond);
- CndVar::destroy(cndvar);
+ cndvar->reset();
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/threads/linux/cnd_init.cpp b/libc/src/threads/linux/cnd_init.cpp
index 478011a5255e8..a27c0a92a67db 100644
--- a/libc/src/threads/linux/cnd_init.cpp
+++ b/libc/src/threads/linux/cnd_init.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "src/threads/cnd_init.h"
+#include "src/__support/CPP/new.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/CndVar.h"
@@ -18,8 +19,8 @@ namespace LIBC_NAMESPACE_DECL {
static_assert(sizeof(CndVar) == sizeof(cnd_t));
LLVM_LIBC_FUNCTION(int, cnd_init, (cnd_t * cond)) {
- CndVar *cndvar = reinterpret_cast<CndVar *>(cond);
- return CndVar::init(cndvar) ? thrd_error : thrd_success;
+ new (cond) CndVar();
+ return thrd_success;
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/threads/linux/cnd_wait.cpp b/libc/src/threads/linux/cnd_wait.cpp
index 3633cc85277b9..da7d549af3ca2 100644
--- a/libc/src/threads/linux/cnd_wait.cpp
+++ b/libc/src/threads/linux/cnd_wait.cpp
@@ -21,7 +21,8 @@ static_assert(sizeof(CndVar) == sizeof(cnd_t));
LLVM_LIBC_FUNCTION(int, cnd_wait, (cnd_t * cond, mtx_t *mtx)) {
CndVar *cndvar = reinterpret_cast<CndVar *>(cond);
Mutex *mutex = reinterpret_cast<Mutex *>(mtx);
- return cndvar->wait(mutex) ? thrd_error : thrd_success;
+ return cndvar->wait(mutex) == CndVar::CndVarResult::Success ? thrd_success
+ : thrd_error;
}
} // namespace LIBC_NAMESPACE_DECL
>From b304d9532c4d9f8f7a91a4a76c302bd61929411b Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 17 Apr 2026 20:41:23 -0400
Subject: [PATCH 3/6] comments
---
libc/src/__support/threads/CndVar.h | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/threads/CndVar.h b/libc/src/__support/threads/CndVar.h
index 8d32f77b6df47..e8dee53f80292 100644
--- a/libc/src/__support/threads/CndVar.h
+++ b/libc/src/__support/threads/CndVar.h
@@ -143,6 +143,8 @@ class CndVar {
// Unlock the mutex and wait for the signal.
mutex->unlock();
+ // Notice that lock is already initialized as lock. We abuse the LOCKED
+ // state to indicate that the waiter is pending.
bool locked = waiter.barrier.lock(timeout, /*is_shared=*/false);
// if we wake up and find that we are still waiting, this means
@@ -158,7 +160,9 @@ class CndVar {
waiter.confirm_cancellation();
} else if (!locked) {
// Whenever a signal is already consumed, we compete for the mutex
- // in the FIFO order of the queue.
+ // in the FIFO order of the queue. We only relock if we previously
+ // wake up due to timeout. Otherwise, it means that our turn has
+ // come, so we don't need to relock.
waiter.barrier.lock();
}
@@ -168,12 +172,15 @@ class CndVar {
WaiterHeader::remove(&waiter);
auto &next_barrier_futex = next_waiter->barrier.get_raw_futex();
auto &mutex_futex = mutex->get_raw_futex();
+ // the following is basically an inlined version of mutex::unlock
+ // but with requeue instead of wake if it is possible.
FutexWordType prev = next_barrier_futex.exchange(
RawMutex::UNLOCKED, cpp::MemoryOrder::RELEASE);
if (prev == RawMutex::IN_CONTENTION)
if (!mutex->can_be_requeued() ||
!next_barrier_futex
- .requeue_to(mutex_futex, cpp::nullopt, 0, 1,
+ .requeue_to(mutex_futex, cpp::nullopt, /*wake_limit=*/0,
+ /*requeue_limit=*/1,
/*is_shared=*/false)
.has_value())
next_waiter->barrier.wake(/*is_shared=*/false);
>From 76458c3c04665254730badb22707f223299d3810 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 17 Apr 2026 21:04:22 -0400
Subject: [PATCH 4/6] fix
---
libc/src/__support/threads/CndVar.h | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/libc/src/__support/threads/CndVar.h b/libc/src/__support/threads/CndVar.h
index e8dee53f80292..77958d8467da6 100644
--- a/libc/src/__support/threads/CndVar.h
+++ b/libc/src/__support/threads/CndVar.h
@@ -37,9 +37,8 @@ class CndVar {
WaiterHeader *next;
LIBC_INLINE void ensure_queue_initialization() {
- if (LIBC_UNLIKELY(prev == nullptr)) {
+ if (LIBC_UNLIKELY(prev == nullptr))
prev = next = this;
- }
}
LIBC_INLINE void push_back(WaiterHeader *waiter) {
@@ -143,7 +142,7 @@ class CndVar {
// Unlock the mutex and wait for the signal.
mutex->unlock();
- // Notice that lock is already initialized as lock. We abuse the LOCKED
+ // Notice that lock is already initialized as LOCKED. We abuse the LOCKED
// state to indicate that the waiter is pending.
bool locked = waiter.barrier.lock(timeout, /*is_shared=*/false);
@@ -176,14 +175,21 @@ class CndVar {
// but with requeue instead of wake if it is possible.
FutexWordType prev = next_barrier_futex.exchange(
RawMutex::UNLOCKED, cpp::MemoryOrder::RELEASE);
- if (prev == RawMutex::IN_CONTENTION)
- if (!mutex->can_be_requeued() ||
- !next_barrier_futex
- .requeue_to(mutex_futex, cpp::nullopt, /*wake_limit=*/0,
- /*requeue_limit=*/1,
- /*is_shared=*/false)
- .has_value())
+ if (prev == RawMutex::IN_CONTENTION) {
+ if (mutex->can_be_requeued()) {
+ ErrorOr<int> res = next_barrier_futex.requeue_to(
+ mutex_futex, cpp::nullopt, /*wake_limit=*/0,
+ /*requeue_limit=*/1,
+ /*is_shared=*/false);
+ if (!res.has_value()) // cannot requeue on this system
+ next_waiter->barrier.wake(/*is_shared=*/false);
+ else if (res.value() >
+ 0) // requeue succeeded, the lock needs to be waked up
+ mutex->get_raw_futex().store(RawMutex::IN_CONTENTION);
+ } else { // cannot requeue under special lock mode
next_waiter->barrier.wake(/*is_shared=*/false);
+ }
+ }
}
if (mutex_result != MutexError::NONE)
return CndVarResult::MutexError;
>From 7940369db49b22d358e67a21b94ac73e3cade0d3 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 17 Apr 2026 21:10:45 -0400
Subject: [PATCH 5/6] fix
---
libc/src/__support/threads/CndVar.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/threads/CndVar.h b/libc/src/__support/threads/CndVar.h
index 77958d8467da6..fc16119da0ae8 100644
--- a/libc/src/__support/threads/CndVar.h
+++ b/libc/src/__support/threads/CndVar.h
@@ -225,6 +225,9 @@ class CndVar {
size_t limit = broadcast ? cpp::numeric_limits<size_t>::max() : 1;
{
cpp::lock_guard lock(queue_lock);
+ waiter_queue.ensure_queue_initialization();
+ if (waiter_queue.next == &waiter_queue)
+ return;
for (cursor = static_cast<CndWaiter *>(waiter_queue.next);
cursor != &waiter_queue;
cursor = static_cast<CndWaiter *>(cursor->next)) {
@@ -254,7 +257,9 @@ class CndVar {
}
public:
- LIBC_INLINE void notify_one(bool is_shared = false) { notify(1, is_shared); }
+ LIBC_INLINE void notify_one(bool is_shared = false) {
+ notify(/*broadcast=*/false, is_shared);
+ }
LIBC_INLINE void broadcast(bool is_shared = false) {
notify(cpp::numeric_limits<size_t>::max(), is_shared);
>From 91ef92058e8e5850d384f5105f20b2c912e58422 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 17 Apr 2026 21:11:06 -0400
Subject: [PATCH 6/6] fix
---
.codex | 0
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 .codex
diff --git a/.codex b/.codex
deleted file mode 100644
index e69de29bb2d1d..0000000000000
More information about the libc-commits
mailing list