[libc-commits] [libc] [libc][semaphore] Add post and wait operations for internal semaphore (PR #198959)

Pengxiang Huang via libc-commits libc-commits at lists.llvm.org
Wed Jun 3 23:10:29 PDT 2026


https://github.com/Pengxiang-Huang updated https://github.com/llvm/llvm-project/pull/198959

>From 03a43b663c6b35d5cd7150abe1374fb55abd0b6f Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Tue, 19 May 2026 23:17:42 -0400
Subject: [PATCH 1/5] [libc][semaphore] add post and trywait operations

---
 libc/src/semaphore/linux/CMakeLists.txt       |  3 +-
 libc/src/semaphore/linux/named_semaphore.cpp  |  5 ---
 libc/src/semaphore/linux/semaphore.h          | 43 ++++++++++++++++++-
 .../src/semaphore/linux/semaphore_test.cpp    | 25 +++++++++++
 4 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/libc/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
index a61075de9b3b9..1437342cd9da3 100644
--- a/libc/src/semaphore/linux/CMakeLists.txt
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -3,8 +3,10 @@ add_header_library(
   HDRS
     semaphore.h
   DEPENDS
+    libc.hdr.errno_macros
     libc.hdr.types.mode_t
     libc.src.__support.CPP.atomic
+    libc.src.__support.CPP.limits
     libc.src.__support.error_or
     libc.src.__support.threads.futex_utils
 )
@@ -20,7 +22,6 @@ add_object_library(
     libc.hdr.errno_macros
     libc.hdr.fcntl_macros
     libc.src.__support.CPP.array
-    libc.src.__support.CPP.limits
     libc.src.__support.CPP.new
     libc.src.__support.CPP.string_view
     libc.src.__support.ctype_utils
diff --git a/libc/src/semaphore/linux/named_semaphore.cpp b/libc/src/semaphore/linux/named_semaphore.cpp
index 2dbd87c0da5ca..795c8a731a38b 100644
--- a/libc/src/semaphore/linux/named_semaphore.cpp
+++ b/libc/src/semaphore/linux/named_semaphore.cpp
@@ -11,7 +11,6 @@
 #include "hdr/errno_macros.h"
 #include "hdr/fcntl_macros.h"
 #include "src/__support/CPP/array.h"
-#include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/new.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
@@ -34,10 +33,6 @@ namespace LIBC_NAMESPACE_DECL {
 
 namespace {
 
-// define SEM_VALUE_MAX as INT_MAX
-constexpr unsigned int SEM_VALUE_MAX =
-    static_cast<unsigned int>(cpp::numeric_limits<int>::max());
-
 // Named semaphores are backed by files in /dev/shm/.
 // a prefix "sem." is added to avoid name collision.
 constexpr cpp::string_view SEM_PREFIX = "/dev/shm/sem.";
diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index b1f7da42901e5..d1090a3999c63 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -9,14 +9,20 @@
 #ifndef LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
 #define LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
 
+#include "hdr/errno_macros.h"
 #include "hdr/types/mode_t.h"
 #include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
 #include "src/__support/threads/futex_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
+// Define SEM_VALUE_MAX as INT_MAX
+constexpr unsigned int SEM_VALUE_MAX =
+    static_cast<unsigned int>(cpp::numeric_limits<int>::max());
+
 class Semaphore {
   Futex value;
   unsigned int canary;
@@ -28,8 +34,8 @@ class Semaphore {
 
 public:
   // TODO:
-  // Add the posting and waiting operations: sem_post, sem_wait,
-  //    sem_trywait, sem_timedwait, sem_clockwait.
+  // Add the blocking waiting operations: sem_wait, sem_timedwait,
+  //    sem_clockwait.
 
   LIBC_INLINE constexpr Semaphore(unsigned int value)
       : value(value), canary(SEM_CANARY) {}
@@ -54,6 +60,39 @@ class Semaphore {
         const_cast<Futex &>(value).load(cpp::MemoryOrder::RELAXED));
   }
 
+  // Atomically increments the semaphore value and
+  // wakes one blocked waiter if any.
+  LIBC_INLINE int post() {
+    FutexWordType v = value.load(cpp::MemoryOrder::RELAXED);
+
+    do {
+      if (v >= SEM_VALUE_MAX)
+        return EOVERFLOW;
+      // RELEASE on success, since post should publish prior writes
+      // RELAXED on failture, since no synchronization event occurs
+    } while (!value.compare_exchange_weak(v, v + 1, cpp::MemoryOrder::RELEASE,
+                                          cpp::MemoryOrder::RELAXED));
+
+    // Named semaphores live in MAP_SHARED memory and may be shared across
+    // processes, so use a shared wake.
+    value.notify_one(/*is_shared=*/true);
+    return 0;
+  }
+
+  // Attempts to decrement the semaphore value without blocking.
+  LIBC_INLINE int trywait() {
+    FutexWordType v = value.load(cpp::MemoryOrder::RELAXED);
+
+    while (v > 0) {
+      // ACQUIRE on success to synchronize with the matching post().
+      if (value.compare_exchange_weak(v, v - 1, cpp::MemoryOrder::ACQUIRE,
+                                      cpp::MemoryOrder::RELAXED))
+        return 0;
+    }
+
+    return EAGAIN;
+  }
+
   // Named semaphore operations.
   // creates or opens a named semaphore backed by a file in /dev/shm/.
   // When O_CREAT is specified in oflag, mode and value are used for
diff --git a/libc/test/src/semaphore/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
index ccda4fd7d4fb4..c434b28b4d927 100644
--- a/libc/test/src/semaphore/linux/semaphore_test.cpp
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -26,6 +26,31 @@ TEST(LlvmLibcSemaphoreTest, Destroy) {
   ASSERT_FALSE(sem.is_valid());
 }
 
+TEST(LlvmLibcSemaphoreTest, TryWait) {
+  Semaphore sem(2);
+
+  // two successful non-blocking decrements.
+  ASSERT_EQ(sem.trywait(), 0);
+  ASSERT_EQ(sem.trywait(), 0);
+  ASSERT_EQ(sem.getvalue(), 0);
+
+  // value is now zero, trywait must fail with EAGAIN.
+  ASSERT_EQ(sem.trywait(), EAGAIN);
+  ASSERT_EQ(sem.getvalue(), 0);
+}
+
+TEST(LlvmLibcSemaphoreTest, Post) {
+  Semaphore sem(0);
+  ASSERT_EQ(sem.getvalue(), 0);
+
+  ASSERT_EQ(sem.post(), 0);
+  ASSERT_EQ(sem.getvalue(), 1);
+
+  // the posted value can be consumed by trywait.
+  ASSERT_EQ(sem.trywait(), 0);
+  ASSERT_EQ(sem.getvalue(), 0);
+}
+
 // Named semaphore tests.
 
 TEST(LlvmLibcSemaphoreTest, NamedOpenCloseUnlink) {

>From 94869380ce50040a28fcd3cb94736719344c34a1 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Tue, 19 May 2026 23:35:16 -0400
Subject: [PATCH 2/5] [libc][semaphore] add wait operation

---
 libc/src/semaphore/linux/semaphore.h             | 16 ++++++++++++++++
 libc/test/src/semaphore/linux/semaphore_test.cpp | 10 ++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index d1090a3999c63..310b0664e5b3c 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -93,6 +93,22 @@ class Semaphore {
     return EAGAIN;
   }
 
+  // Blocking wait, decrements the value when positive, or blocks until a
+  // post() makes it positive again.
+  LIBC_INLINE int wait() {
+    for (;;) {
+      if (trywait() == 0)
+        return 0;
+
+      // Blocks when value was zero.
+      // Futex wait re-checks the value atomically,
+      // so a racing post() before sleep is not lost.
+      // Spurious wakeups just send back to trywait().
+      (void)value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt,
+                       /*is_shared=*/true);
+    }
+  }
+
   // Named semaphore operations.
   // creates or opens a named semaphore backed by a file in /dev/shm/.
   // When O_CREAT is specified in oflag, mode and value are used for
diff --git a/libc/test/src/semaphore/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
index c434b28b4d927..001e1db5921b0 100644
--- a/libc/test/src/semaphore/linux/semaphore_test.cpp
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -51,6 +51,16 @@ TEST(LlvmLibcSemaphoreTest, Post) {
   ASSERT_EQ(sem.getvalue(), 0);
 }
 
+TEST(LlvmLibcSemaphoreTest, WaitNonBlocking) {
+  Semaphore sem(2);
+
+  // value is positive: wait() should decrement without blocking.
+  ASSERT_EQ(sem.wait(), 0);
+  ASSERT_EQ(sem.getvalue(), 1);
+  ASSERT_EQ(sem.wait(), 0);
+  ASSERT_EQ(sem.getvalue(), 0);
+}
+
 // Named semaphore tests.
 
 TEST(LlvmLibcSemaphoreTest, NamedOpenCloseUnlink) {

>From be29cc24a8c61b52332500398a1e785989974e6e Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 20 May 2026 00:01:10 -0400
Subject: [PATCH 3/5] [libc][semaphore] add timed wait and clock wait

---
 libc/src/semaphore/linux/CMakeLists.txt       |  4 ++
 libc/src/semaphore/linux/semaphore.h          | 51 ++++++++++++--
 libc/test/src/semaphore/linux/CMakeLists.txt  |  3 +
 .../src/semaphore/linux/semaphore_test.cpp    | 70 +++++++++++++++++++
 4 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/libc/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
index 1437342cd9da3..808dfab01bd39 100644
--- a/libc/src/semaphore/linux/CMakeLists.txt
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -4,11 +4,15 @@ add_header_library(
     semaphore.h
   DEPENDS
     libc.hdr.errno_macros
+    libc.hdr.time_macros
+    libc.hdr.types.clockid_t
     libc.hdr.types.mode_t
+    libc.hdr.types.struct_timespec
     libc.src.__support.CPP.atomic
     libc.src.__support.CPP.limits
     libc.src.__support.error_or
     libc.src.__support.threads.futex_utils
+    libc.src.__support.time.abs_timeout
 )
 
 add_object_library(
diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index 310b0664e5b3c..ae143cd4880d0 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -10,12 +10,16 @@
 #define LLVM_LIBC_SRC_SEMAPHORE_LINUX_SEMAPHORE_H
 
 #include "hdr/errno_macros.h"
+#include "hdr/time_macros.h"
+#include "hdr/types/clockid_t.h"
 #include "hdr/types/mode_t.h"
+#include "hdr/types/struct_timespec.h"
 #include "src/__support/CPP/atomic.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
 #include "src/__support/threads/futex_utils.h"
+#include "src/__support/time/abs_timeout.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -32,11 +36,24 @@ class Semaphore {
   // 0x4D='M', 0x31='1').
   static constexpr unsigned int SEM_CANARY = 0x53454D31U;
 
-public:
-  // TODO:
-  // Add the blocking waiting operations: sem_wait, sem_timedwait,
-  //    sem_clockwait.
+  // Helper function for timed blocking wait, wait for a required
+  // absolute timeout.
+  LIBC_INLINE int wait_until(internal::AbsTimeout timeout) {
+    for (;;) {
+      if (trywait() == 0)
+        return 0;
+
+      auto wait_or = value.wait(/*expected=*/0, timeout, /*is_shared=*/true);
+      if (!wait_or.has_value() && wait_or.error() == ETIMEDOUT) {
+        // Final attempt in case a post() raced with the timeout.
+        if (trywait() == 0)
+          return 0;
+        return ETIMEDOUT;
+      }
+    }
+  }
 
+public:
   LIBC_INLINE constexpr Semaphore(unsigned int value)
       : value(value), canary(SEM_CANARY) {}
 
@@ -109,6 +126,32 @@ class Semaphore {
     }
   }
 
+  // Blocking wait against an absolute CLOCK_REALTIME deadline.
+  LIBC_INLINE int timedwait(const timespec *abstime) {
+    return clockwait(CLOCK_REALTIME, abstime);
+  }
+
+  // Blocking wait against a deadline on the given clock:
+  // CLOCK_REALTIME or CLOCK_MONOTONIC
+  LIBC_INLINE int clockwait(clockid_t clock_id, const timespec *abstime) {
+    if (clock_id != CLOCK_MONOTONIC && clock_id != CLOCK_REALTIME)
+      return EINVAL;
+
+    bool is_realtime = (clock_id == CLOCK_REALTIME);
+    auto timeout =
+        internal::AbsTimeout::from_timespec(*abstime, /*realtime=*/is_realtime);
+    if (LIBC_LIKELY(timeout.has_value()))
+      return wait_until(timeout.value());
+
+    switch (timeout.error()) {
+    case internal::AbsTimeout::Error::Invalid:
+      return EINVAL;
+    case internal::AbsTimeout::Error::BeforeEpoch:
+      return ETIMEDOUT;
+    }
+    __builtin_unreachable();
+  }
+
   // Named semaphore operations.
   // creates or opens a named semaphore backed by a file in /dev/shm/.
   // When O_CREAT is specified in oflag, mode and value are used for
diff --git a/libc/test/src/semaphore/linux/CMakeLists.txt b/libc/test/src/semaphore/linux/CMakeLists.txt
index 2c4ae4dca3e39..7c5bf07630c86 100644
--- a/libc/test/src/semaphore/linux/CMakeLists.txt
+++ b/libc/test/src/semaphore/linux/CMakeLists.txt
@@ -9,6 +9,9 @@ add_libc_unittest(
   DEPENDS
     libc.hdr.errno_macros
     libc.hdr.fcntl_macros
+    libc.hdr.time_macros
+    libc.hdr.types.struct_timespec
+    libc.src.__support.time.clock_gettime
     libc.src.semaphore.linux.semaphore
     libc.src.semaphore.linux.named_semaphore
 )
diff --git a/libc/test/src/semaphore/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
index 001e1db5921b0..a09a55ca8951c 100644
--- a/libc/test/src/semaphore/linux/semaphore_test.cpp
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -8,6 +8,9 @@
 
 #include "hdr/errno_macros.h"
 #include "hdr/fcntl_macros.h"
+#include "hdr/time_macros.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/time/clock_gettime.h"
 #include "src/semaphore/linux/semaphore.h"
 #include "test/UnitTest/Test.h"
 
@@ -61,6 +64,73 @@ TEST(LlvmLibcSemaphoreTest, WaitNonBlocking) {
   ASSERT_EQ(sem.getvalue(), 0);
 }
 
+TEST(LlvmLibcSemaphoreTest, TimedWaitNonBlocking) {
+  Semaphore sem(2);
+  timespec ts{};
+  LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts);
+  ts.tv_sec += 60;
+
+  // value is positive: timedwait should decrement without consulting the
+  // clock.
+  ASSERT_EQ(sem.timedwait(&ts), 0);
+  ASSERT_EQ(sem.getvalue(), 1);
+}
+
+TEST(LlvmLibcSemaphoreTest, TimedWaitTimeout) {
+  Semaphore sem(0);
+  timespec ts{};
+  LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts);
+  // a few milliseconds in the future.
+  ts.tv_nsec += 10'000'000;
+  if (ts.tv_nsec >= 1'000'000'000) {
+    ts.tv_sec += 1;
+    ts.tv_nsec -= 1'000'000'000;
+  }
+
+  // value is zero and no post() arrives: must time out.
+  ASSERT_EQ(sem.timedwait(&ts), ETIMEDOUT);
+}
+
+TEST(LlvmLibcSemaphoreTest, TimedWaitBeforeEpoch) {
+  Semaphore sem(0);
+  // tv_sec < 0 is treated as an already expired deadline.
+  timespec ts{};
+  ts.tv_sec = -1;
+  ts.tv_nsec = 0;
+  ASSERT_EQ(sem.timedwait(&ts), ETIMEDOUT);
+}
+
+TEST(LlvmLibcSemaphoreTest, TimedWaitInvalidTimespec) {
+  Semaphore sem(0);
+  // tv_nsec out of [0, 1e9) is malformed.
+  timespec ts{};
+  ts.tv_sec = 1;
+  ts.tv_nsec = 1'000'000'001;
+  ASSERT_EQ(sem.timedwait(&ts), EINVAL);
+}
+
+TEST(LlvmLibcSemaphoreTest, ClockWaitMonotonicTimeout) {
+  Semaphore sem(0);
+  timespec ts{};
+  LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts);
+  ts.tv_nsec += 10'000'000;
+  if (ts.tv_nsec >= 1'000'000'000) {
+    ts.tv_sec += 1;
+    ts.tv_nsec -= 1'000'000'000;
+  }
+
+  ASSERT_EQ(sem.clockwait(CLOCK_MONOTONIC, &ts), ETIMEDOUT);
+}
+
+TEST(LlvmLibcSemaphoreTest, ClockWaitUnsupportedClock) {
+  Semaphore sem(0);
+  timespec ts{};
+  ts.tv_sec = 1;
+  ts.tv_nsec = 0;
+  // any clock other than CLOCK_MONOTONIC / CLOCK_REALTIME is rejected.
+  ASSERT_EQ(sem.clockwait(static_cast<clockid_t>(99), &ts), EINVAL);
+}
+
 // Named semaphore tests.
 
 TEST(LlvmLibcSemaphoreTest, NamedOpenCloseUnlink) {

>From 64d46348304c4acd9ad3637e8f62ee98b0d132f1 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Wed, 20 May 2026 22:42:40 -0400
Subject: [PATCH 4/5] [libc][semaphore] fiex some comments

---
 libc/src/semaphore/linux/semaphore.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index ae143cd4880d0..ea023e7866bfa 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -86,10 +86,12 @@ class Semaphore {
       if (v >= SEM_VALUE_MAX)
         return EOVERFLOW;
       // RELEASE on success, since post should publish prior writes
-      // RELAXED on failture, since no synchronization event occurs
+      // RELAXED on failure, since no synchronization event occurs
     } while (!value.compare_exchange_weak(v, v + 1, cpp::MemoryOrder::RELEASE,
                                           cpp::MemoryOrder::RELAXED));
 
+    // Wake one waiter if any,
+    // waiter selection is left to linux futex implementation.
     // Named semaphores live in MAP_SHARED memory and may be shared across
     // processes, so use a shared wake.
     value.notify_one(/*is_shared=*/true);

>From c584535a093741ac72e92efc860d36fee7a22810 Mon Sep 17 00:00:00 2001
From: Pengxiang Huang <huangpengxiang70 at gmail.com>
Date: Thu, 4 Jun 2026 02:10:12 -0400
Subject: [PATCH 5/5] fix reviewers comments

---
 libc/src/semaphore/linux/CMakeLists.txt       |  1 +
 libc/src/semaphore/linux/named_semaphore.cpp  |  5 ++++-
 libc/src/semaphore/linux/semaphore.h          | 22 +++++++++++--------
 .../src/semaphore/linux/semaphore_test.cpp    | 22 +++++++++----------
 4 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/libc/src/semaphore/linux/CMakeLists.txt b/libc/src/semaphore/linux/CMakeLists.txt
index 808dfab01bd39..bd57b9ee5fc5d 100644
--- a/libc/src/semaphore/linux/CMakeLists.txt
+++ b/libc/src/semaphore/linux/CMakeLists.txt
@@ -11,6 +11,7 @@ add_header_library(
     libc.src.__support.CPP.atomic
     libc.src.__support.CPP.limits
     libc.src.__support.error_or
+    libc.src.__support.libc_assert
     libc.src.__support.threads.futex_utils
     libc.src.__support.time.abs_timeout
 )
diff --git a/libc/src/semaphore/linux/named_semaphore.cpp b/libc/src/semaphore/linux/named_semaphore.cpp
index 795c8a731a38b..077e8f8e29d4b 100644
--- a/libc/src/semaphore/linux/named_semaphore.cpp
+++ b/libc/src/semaphore/linux/named_semaphore.cpp
@@ -147,7 +147,10 @@ ErrorOr<Semaphore *> Semaphore::open(const char *name, int oflag, mode_t mode,
   }
 
   Semaphore *sem = sem_or.value();
-  new (sem) Semaphore(value);
+
+  // Named semaphores are backed by MAP_SHARED memory and used across
+  // processes, so they use shared futex addressing mode.
+  new (sem) Semaphore(value, /*is_shared=*/true);
 
   // atomically publish the fully initialized semaphore.
   auto link_or = linux_syscalls::link(tmp_or->data(), path_or->data());
diff --git a/libc/src/semaphore/linux/semaphore.h b/libc/src/semaphore/linux/semaphore.h
index ea023e7866bfa..94d65cb3eb7b2 100644
--- a/libc/src/semaphore/linux/semaphore.h
+++ b/libc/src/semaphore/linux/semaphore.h
@@ -18,6 +18,7 @@
 #include "src/__support/CPP/limits.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
+#include "src/__support/libc_assert.h"
 #include "src/__support/threads/futex_utils.h"
 #include "src/__support/time/abs_timeout.h"
 
@@ -31,6 +32,9 @@ class Semaphore {
   Futex value;
   unsigned int canary;
 
+  // Whether the semaphore is shared between processes.
+  bool is_shared;
+
   // A private constant canary used to detect use of uninitialized or
   // destroyed semaphores. Chose "SEM1" in ASCII (0x53='S', 0x45='E',
   // 0x4D='M', 0x31='1').
@@ -43,7 +47,7 @@ class Semaphore {
       if (trywait() == 0)
         return 0;
 
-      auto wait_or = value.wait(/*expected=*/0, timeout, /*is_shared=*/true);
+      auto wait_or = value.wait(/*expected=*/0, timeout, is_shared);
       if (!wait_or.has_value() && wait_or.error() == ETIMEDOUT) {
         // Final attempt in case a post() raced with the timeout.
         if (trywait() == 0)
@@ -54,8 +58,8 @@ class Semaphore {
   }
 
 public:
-  LIBC_INLINE constexpr Semaphore(unsigned int value)
-      : value(value), canary(SEM_CANARY) {}
+  LIBC_INLINE constexpr Semaphore(unsigned int value, bool shared)
+      : value(value), canary(SEM_CANARY), is_shared(shared) {}
 
   // Sanity check to detect use of uninitialized or destroyed semaphores.
   LIBC_INLINE bool is_valid() const { return canary == SEM_CANARY; }
@@ -92,9 +96,7 @@ class Semaphore {
 
     // Wake one waiter if any,
     // waiter selection is left to linux futex implementation.
-    // Named semaphores live in MAP_SHARED memory and may be shared across
-    // processes, so use a shared wake.
-    value.notify_one(/*is_shared=*/true);
+    value.notify_one(is_shared);
     return 0;
   }
 
@@ -123,8 +125,7 @@ class Semaphore {
       // Futex wait re-checks the value atomically,
       // so a racing post() before sleep is not lost.
       // Spurious wakeups just send back to trywait().
-      (void)value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt,
-                       /*is_shared=*/true);
+      (void)value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt, is_shared);
     }
   }
 
@@ -145,7 +146,10 @@ class Semaphore {
     if (LIBC_LIKELY(timeout.has_value()))
       return wait_until(timeout.value());
 
-    switch (timeout.error()) {
+    internal::AbsTimeout::Error err = timeout.error();
+    LIBC_ASSERT(err == internal::AbsTimeout::Error::Invalid ||
+                err == internal::AbsTimeout::Error::BeforeEpoch);
+    switch (err) {
     case internal::AbsTimeout::Error::Invalid:
       return EINVAL;
     case internal::AbsTimeout::Error::BeforeEpoch:
diff --git a/libc/test/src/semaphore/linux/semaphore_test.cpp b/libc/test/src/semaphore/linux/semaphore_test.cpp
index a09a55ca8951c..286be2636a0df 100644
--- a/libc/test/src/semaphore/linux/semaphore_test.cpp
+++ b/libc/test/src/semaphore/linux/semaphore_test.cpp
@@ -17,20 +17,20 @@
 using LIBC_NAMESPACE::Semaphore;
 
 TEST(LlvmLibcSemaphoreTest, InitAndGetValue) {
-  Semaphore sem(3);
+  Semaphore sem(3, /*is_shared=*/false);
   ASSERT_TRUE(sem.is_valid());
   ASSERT_EQ(sem.getvalue(), 3);
 }
 
 TEST(LlvmLibcSemaphoreTest, Destroy) {
-  Semaphore sem(5);
+  Semaphore sem(5, /*is_shared=*/false);
   ASSERT_TRUE(sem.is_valid());
   sem.destroy();
   ASSERT_FALSE(sem.is_valid());
 }
 
 TEST(LlvmLibcSemaphoreTest, TryWait) {
-  Semaphore sem(2);
+  Semaphore sem(2, /*is_shared=*/false);
 
   // two successful non-blocking decrements.
   ASSERT_EQ(sem.trywait(), 0);
@@ -43,7 +43,7 @@ TEST(LlvmLibcSemaphoreTest, TryWait) {
 }
 
 TEST(LlvmLibcSemaphoreTest, Post) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   ASSERT_EQ(sem.getvalue(), 0);
 
   ASSERT_EQ(sem.post(), 0);
@@ -55,7 +55,7 @@ TEST(LlvmLibcSemaphoreTest, Post) {
 }
 
 TEST(LlvmLibcSemaphoreTest, WaitNonBlocking) {
-  Semaphore sem(2);
+  Semaphore sem(2, /*is_shared=*/false);
 
   // value is positive: wait() should decrement without blocking.
   ASSERT_EQ(sem.wait(), 0);
@@ -65,7 +65,7 @@ TEST(LlvmLibcSemaphoreTest, WaitNonBlocking) {
 }
 
 TEST(LlvmLibcSemaphoreTest, TimedWaitNonBlocking) {
-  Semaphore sem(2);
+  Semaphore sem(2, /*is_shared=*/false);
   timespec ts{};
   LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts);
   ts.tv_sec += 60;
@@ -77,7 +77,7 @@ TEST(LlvmLibcSemaphoreTest, TimedWaitNonBlocking) {
 }
 
 TEST(LlvmLibcSemaphoreTest, TimedWaitTimeout) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   timespec ts{};
   LIBC_NAMESPACE::internal::clock_gettime(CLOCK_REALTIME, &ts);
   // a few milliseconds in the future.
@@ -92,7 +92,7 @@ TEST(LlvmLibcSemaphoreTest, TimedWaitTimeout) {
 }
 
 TEST(LlvmLibcSemaphoreTest, TimedWaitBeforeEpoch) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   // tv_sec < 0 is treated as an already expired deadline.
   timespec ts{};
   ts.tv_sec = -1;
@@ -101,7 +101,7 @@ TEST(LlvmLibcSemaphoreTest, TimedWaitBeforeEpoch) {
 }
 
 TEST(LlvmLibcSemaphoreTest, TimedWaitInvalidTimespec) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   // tv_nsec out of [0, 1e9) is malformed.
   timespec ts{};
   ts.tv_sec = 1;
@@ -110,7 +110,7 @@ TEST(LlvmLibcSemaphoreTest, TimedWaitInvalidTimespec) {
 }
 
 TEST(LlvmLibcSemaphoreTest, ClockWaitMonotonicTimeout) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   timespec ts{};
   LIBC_NAMESPACE::internal::clock_gettime(CLOCK_MONOTONIC, &ts);
   ts.tv_nsec += 10'000'000;
@@ -123,7 +123,7 @@ TEST(LlvmLibcSemaphoreTest, ClockWaitMonotonicTimeout) {
 }
 
 TEST(LlvmLibcSemaphoreTest, ClockWaitUnsupportedClock) {
-  Semaphore sem(0);
+  Semaphore sem(0, /*is_shared=*/false);
   timespec ts{};
   ts.tv_sec = 1;
   ts.tv_nsec = 0;



More information about the libc-commits mailing list