Skip to main content

Module shared_semaphore

Module shared_semaphore 

Source
Expand description

SharedSemaphore - cross-process counting semaphore.

Three MMF files compose the primitive:

  • <base>.count.bin - SharedAtomicU32: available permits.
  • <base>.wakeup.bin - SharedAtomicU64: monotonic generation bumped on every release to wake waiters.
  • <base>.waiters.bin - SharedAtomicU32: count of currently- waiting acquirers. Releasers consult it to skip the wakeup bump when there are no waiters (saves an atomic store on the uncontended path).

§Protocol

acquire:

  1. Load count. If > 0, try CAS to decrement; on success, return.
  2. On 0 (or CAS lost), increment waiters, snapshot wakeup, re-check count, then yield/sleep until either count > 0 OR wakeup advances. Loop back to 1.

release:

  1. count.fetch_add(1, AcqRel).
  2. If waiters.load(Acquire) > 0, wakeup.fetch_add(1, Release) to wake at least one waiter.

try_acquire: a single CAS pass; never spins, never sleeps.

§Why no real wait queue ring?

Linux futex semantics are “wake N waiters”; an acquirer just needs to know “something changed.” A generation counter gives that exactly. Adding a ring of waiter PIDs only helps if you need strict FIFO fairness, which most cross-process resource limiters do NOT. The generation-counter design is simpler, has zero allocation, and matches the semantics of every modern OS semaphore primitive (which all coalesce identical wakeups internally).

§Permit RAII

acquire / try_acquire return a Permit guard tied to the semaphore. Dropping the permit releases the count. For cross- thread / cross-process ownership (e.g., handing a permit to a background task), use the standalone release API: mem::forget(permit) then sem.release() from the new owner.

Structs§

Permit
RAII permit guard. Dropping releases one permit back to the semaphore. Use mem::forget(permit) + sem.release() to transfer ownership.
SharedSemaphore

Enums§

SemaphoreError