[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