[libc-commits] [libc] [libc][semaphore] Add post and wait operations for internal semaphore (PR #198959)
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Thu Jul 16 14:50:04 PDT 2026
================
@@ -54,6 +85,87 @@ 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 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.
+ value.notify_one(is_shared);
+ 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;
+ }
+
+ // Blocking wait, decrements the value when positive, or blocks until a
+ // post() makes it positive again.
+ LIBC_INLINE int wait() {
+ while (trywait() != 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().
+ auto wait_or =
+ value.wait(/*expected=*/0, /*timeout=*/cpp::nullopt, is_shared);
+
+ // A successful wake or EAGAIN both just send it back to trywait().
+ // Any other error is unexpected.
+ if (!wait_or.has_value() && wait_or.error() != EAGAIN)
+ return wait_or.error();
+ }
+ return 0;
+ }
+
+ // 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());
+
+ 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:
+ return ETIMEDOUT;
+ }
+ __builtin_unreachable();
----------------
michaelrj-google wrote:
given that there's an assert above to check if this is a valid state it might make more sense for this to be `__builtin_trap()`. That way on a non-debug build that reaches this supposedly unreachable state it will properly stop instead of doing something undefined.
https://github.com/llvm/llvm-project/pull/198959
More information about the libc-commits
mailing list