use core::{
fmt,
pin::Pin,
task::{Context, Poll},
};
use lock_api::RawMutex;
use pin_list::{Node, NodeData};
use crate::closeable::{IsCloseable, Uncloseable};
use crate::{SemaphoreQueue, SemaphoreState};
use super::PinQueue;
use crate::loom::Mutex;
pin_project_lite::pin_project! {
pub struct Acquire<'a, S, C, R>
where
S: ?Sized,
S: SemaphoreState,
C: IsCloseable,
R: RawMutex,
{
#[pin]
node: Node<PinQueue<S::Params, S::Permit, C>>,
order: FairOrder,
state: &'a Mutex<R, SemaphoreQueue<S, C>>,
params: Option<S::Params>,
}
impl<S, C, R> PinnedDrop for Acquire<'_, S, C, R>
where
S: ?Sized,
S: SemaphoreState,
C: IsCloseable,
R: RawMutex,
{
fn drop(this: Pin<&mut Self>) {
let this = this.project();
let Some(node) = this.node.initialized_mut() else {
return;
};
let mut state = this.state.lock();
match &mut state.queue {
Ok(queue) => {
match node.reset(queue).0 {
NodeData::Removed(Ok(permit)) => {
state.state.release(permit);
state.check();
}
NodeData::Linked(_) => state.check(),
NodeData::Removed(Err(_closed)) => {}
}
}
Err(_closed) => {
let (permit, ()) = unsafe { node.take_removed_unchecked() };
if let Ok(permit) = permit {
state.state.release(permit);
}
}
}
}
}
}
impl<S: SemaphoreState + ?Sized, C: IsCloseable, R: RawMutex> Future for Acquire<'_, S, C, R> {
type Output = Result<S::Permit, C::AcquireError<S::Params>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
let mut state = this.state.lock();
let Some(init) = this.node.as_mut().initialized_mut() else {
let params = this.params.take().unwrap();
let node = this.node.as_mut();
match state.try_acquire(params, Fairness::Fair(*this.order)) {
Ok(permit) => return Poll::Ready(Ok(permit)),
Err(TryAcquireError::Closed(params)) => return Poll::Ready(Err(params)),
Err(TryAcquireError::Poisoned(_params)) => {
panic!(
"the semaphore is poisoned: a previous SemaphoreState::acquire call panicked"
)
}
Err(TryAcquireError::NoPermits(params)) => {
let queue = match &mut state.queue {
Ok(queue) => queue,
Err(_closed) => unsafe { core::hint::unreachable_unchecked() },
};
let waker = cx.waker().clone();
match *this.order {
FairOrder::Lifo => queue.push_front(node, (Some(params), waker), ()),
FairOrder::Fifo => queue.push_back(node, (Some(params), waker), ()),
};
return Poll::Pending;
}
}
};
if let Ok(queue) = &mut state.queue
&& let Some((_, waker)) = init.protected_mut(queue)
{
waker.clone_from(cx.waker());
return Poll::Pending;
}
let (permit, ()) = unsafe { init.take_removed_unchecked() };
let permit = permit.map_err(|params| {
C::map_err(params, |params| {
params.expect(
"params should be set. likely the SemaphoreState::acquire method panicked",
)
})
});
Poll::Ready(permit)
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum FairOrder {
Lifo,
Fifo,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Fairness {
Fair(FairOrder),
Unfair,
}
impl<S: SemaphoreState + ?Sized, C: IsCloseable> SemaphoreQueue<S, C> {
#[inline]
pub fn acquire<R: RawMutex>(
this: &Mutex<R, Self>,
params: S::Params,
order: FairOrder,
) -> Acquire<'_, S, C, R> {
Acquire {
node: Node::new(),
order,
state: this,
params: Some(params),
}
}
#[inline]
pub fn try_acquire(
&mut self,
params: S::Params,
fairness: Fairness,
) -> Result<S::Permit, TryAcquireError<S::Params, C>> {
if self.is_poisoned() {
return Err(TryAcquireError::Poisoned(params));
}
let queue = match &mut self.queue {
Ok(queue) => queue,
Err(_closed) => {
return Err(TryAcquireError::Closed(C::new_err(params)));
}
};
let is_leader = match fairness {
Fairness::Fair(FairOrder::Fifo) => queue.is_empty(),
Fairness::Fair(FairOrder::Lifo) => true,
Fairness::Unfair => true,
};
if !is_leader {
return Err(TryAcquireError::NoPermits(params));
}
let guard = crate::PoisonOnUnwind(&mut self.poisoned);
let result = self.state.acquire(params);
core::mem::forget(guard);
match result {
Ok(permit) => Ok(permit),
Err(p) => Err(TryAcquireError::NoPermits(p)),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum TryAcquireError<P, C: IsCloseable> {
NoPermits(P),
Closed(C::AcquireError<P>),
Poisoned(P),
}
impl<P, C: IsCloseable> fmt::Display for TryAcquireError<P, C> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TryAcquireError::Closed(_) => write!(fmt, "semaphore closed"),
TryAcquireError::NoPermits(_) => write!(fmt, "no permits available"),
TryAcquireError::Poisoned(_) => write!(fmt, "semaphore poisoned"),
}
}
}
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub struct AcquireError<P> {
pub params: P,
}
impl AcquireError<Uncloseable> {
pub fn never(self) -> ! {
match self.params {}
}
}
impl<P> fmt::Display for AcquireError<P> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "semaphore closed")
}
}