[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