use std::{
fmt, mem,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
use super::WaitStart;
use crate::Event;
pub struct Semaphore {
permits: AtomicUsize,
starved: AtomicUsize,
released: Event,
}
impl Semaphore {
pub const MAX_PERMITS: usize = usize::MAX >> 1;
pub const fn new(permits: usize) -> Self {
assert!(
permits <= Self::MAX_PERMITS,
"more permits than a semaphore can have free"
);
Self {
permits: AtomicUsize::new(permits),
starved: AtomicUsize::new(0),
released: Event::new(),
}
}
pub async fn acquire(&self) -> SemaphoreGuard<'_> {
self.take(|| SemaphoreGuard(self)).await
}
#[inline]
pub fn try_acquire(&self) -> Option<SemaphoreGuard<'_>> {
self.try_take_as(false).then(|| SemaphoreGuard(self))
}
pub async fn acquire_arc(self: &Arc<Self>) -> SemaphoreGuardArc {
self.take(|| SemaphoreGuardArc(Some(self.clone()))).await
}
#[inline]
pub fn try_acquire_arc(self: &Arc<Self>) -> Option<SemaphoreGuardArc> {
self.try_take_as(false)
.then(|| SemaphoreGuardArc(Some(self.clone())))
}
pub fn add_permits(&self, n: usize) {
if n == 0 {
return;
}
let mut free = self.permits.load(Ordering::SeqCst);
loop {
let added = free
.checked_add(n)
.filter(|&added| added <= Self::MAX_PERMITS)
.expect("more permits than a semaphore can have free");
match self.permits.compare_exchange_weak(
free,
added,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => break,
Err(now) => free = now,
}
}
self.released.notify_additional_unfenced(n);
}
async fn take<F, G>(&self, guard: F) -> G
where
F: FnOnce() -> G,
{
let mut since = None;
let mut starved = None;
let listener = loop {
if self.try_take_as(since.is_some()) {
break None;
}
if starved.is_none() && since.is_some_and(WaitStart::waited_long) {
starved = Some(Starved::new(self));
}
let listener = self.released.listen_unfenced();
if self.try_take_as(since.is_some()) {
break Some(listener);
}
since.get_or_insert_with(WaitStart::now);
listener.await;
};
let guard = guard();
drop(listener);
drop(starved);
guard
}
#[inline]
fn try_take_as(&self, waited: bool) -> bool {
if !waited && self.starved.load(Ordering::SeqCst) != 0 {
return false;
}
let mut free = self.permits.load(Ordering::SeqCst);
loop {
let Some(left) = free.checked_sub(1) else {
return false;
};
match self.permits.compare_exchange_weak(
free,
left,
Ordering::Acquire,
Ordering::SeqCst,
) {
Ok(_) => return true,
Err(now) => free = now,
}
}
}
fn release(&self) {
self.permits.fetch_add(1, Ordering::SeqCst);
self.released.notify_additional_unfenced(1);
}
}
impl fmt::Debug for Semaphore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Semaphore")
.field("permits", &self.permits.load(Ordering::Relaxed))
.finish()
}
}
#[must_use = "if unused the Semaphore will immediately give the permit back"]
pub struct SemaphoreGuard<'a>(&'a Semaphore);
impl SemaphoreGuard<'_> {
pub fn forget(self) {
mem::forget(self);
}
}
impl fmt::Debug for SemaphoreGuard<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("SemaphoreGuard").field(self.0).finish()
}
}
impl Drop for SemaphoreGuard<'_> {
fn drop(&mut self) {
self.0.release();
}
}
#[must_use = "if unused the Semaphore will immediately give the permit back"]
pub struct SemaphoreGuardArc(
Option<Arc<Semaphore>>,
);
impl SemaphoreGuardArc {
pub fn forget(mut self) {
self.0 = None;
}
}
impl fmt::Debug for SemaphoreGuardArc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_tuple("SemaphoreGuardArc");
if let Some(semaphore) = &self.0 {
s.field(&**semaphore);
}
s.finish()
}
}
impl Drop for SemaphoreGuardArc {
fn drop(&mut self) {
let Some(semaphore) = &self.0 else {
return;
};
semaphore.release();
}
}
struct Starved<'a>(&'a Semaphore);
impl<'a> Starved<'a> {
fn new(semaphore: &'a Semaphore) -> Self {
semaphore.starved.fetch_add(1, Ordering::SeqCst);
Self(semaphore)
}
}
impl Drop for Starved<'_> {
fn drop(&mut self) {
self.0.starved.fetch_sub(1, Ordering::SeqCst);
let free = self.0.permits.load(Ordering::SeqCst);
if free == 0 {
return;
}
self.0.released.notify_additional_unfenced(free);
}
}