#![expect(
clippy::unwrap_used,
reason = "ratchet MOIRAI-UNWRAP-1: pre-existing debt"
)]
use std::future::Future;
use std::pin::Pin;
use std::sync::Mutex;
use std::task::{Context, Poll};
use crate::sync::wait_queue::{WaitQueue, WaiterPoll};
pub struct Semaphore {
state: Mutex<SemaphoreState>,
}
struct SemaphoreState {
available: usize,
waiters: WaitQueue<()>,
}
impl Semaphore {
pub fn new(permits: usize) -> Self {
Self {
state: Mutex::new(SemaphoreState {
available: permits,
waiters: WaitQueue::new(),
}),
}
}
pub fn acquire(&self) -> SemaphoreAcquire<'_> {
SemaphoreAcquire {
semaphore: self,
id: None,
}
}
pub fn try_acquire(&self) -> Option<SemaphorePermit<'_>> {
let mut state = self.state.lock().unwrap();
if state.available > 0 {
state.available -= 1;
Some(SemaphorePermit { semaphore: self })
} else {
None
}
}
pub fn available_permits(&self) -> usize {
self.state.lock().unwrap().available
}
fn release(&self) {
let waker = {
let mut state = self.state.lock().unwrap();
let waker = state.waiters.grant_oldest(());
if waker.is_none() {
state.available += 1;
}
waker
};
if let Some(waker) = waker {
waker.wake();
}
}
}
pub struct SemaphoreAcquire<'a> {
semaphore: &'a Semaphore,
id: Option<u64>,
}
impl<'a> Future for SemaphoreAcquire<'a> {
type Output = SemaphorePermit<'a>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut state = self.semaphore.state.lock().unwrap();
if let Some(id) = self.id {
match state.waiters.poll_waiter(id, cx.waker()) {
WaiterPoll::Granted(()) => {
self.id = None;
return Poll::Ready(SemaphorePermit {
semaphore: self.semaphore,
});
}
WaiterPoll::Pending => return Poll::Pending,
WaiterPoll::NotRegistered => {}
}
}
if state.available > 0 {
state.available -= 1;
if let Some(id) = self.id.take() {
let _removed_grant = state.waiters.deregister(id);
}
return Poll::Ready(SemaphorePermit {
semaphore: self.semaphore,
});
}
if self.id.is_none() {
self.id = Some(state.waiters.register(cx.waker().clone()));
}
Poll::Pending
}
}
impl<'a> Drop for SemaphoreAcquire<'a> {
fn drop(&mut self) {
if let Some(id) = self.id
&& let Ok(mut state) = self.semaphore.state.lock()
{
if state.waiters.deregister(id).is_some() {
drop(state);
self.semaphore.release();
}
}
}
}
pub struct SemaphorePermit<'a> {
semaphore: &'a Semaphore,
}
impl<'a> Drop for SemaphorePermit<'a> {
fn drop(&mut self) {
self.semaphore.release();
}
}
#[cfg(test)]
mod tests;