[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