use core::{
future::Future,
pin::Pin,
sync::atomic::Ordering::{Acquire, Relaxed, SeqCst},
task::{Context, Poll},
};
use crate::{
Node, NodeState,
linking::{AtomicEager, Linking},
loom::sync::atomic::fence,
node_wrapper,
sync::mutex::{DefaultMutex, Mutex},
wait_list::{
ClosedError, DEFAULT_WAKER_BATCH_SIZE, Notification, STATE_CLOSED, STATE_OPEN, WaitListRef,
synchronization::{SyncMode, Synchronization, Synchronized},
},
};
node_wrapper! {
pub struct Wait<
'a,
N: Notification = (),
S: Synchronization = Synchronized,
L: Linking = AtomicEager,
M: Mutex = DefaultMutex,
const WAKER_BATCH_SIZE: usize = DEFAULT_WAKER_BATCH_SIZE,
>(pub(super) Node<WaitListRef<'a, N, S, L, M, WAKER_BATCH_SIZE>>);
}
impl<N: Notification, S: Synchronization, L: Linking, M: Mutex, const WAKER_BATCH_SIZE: usize>
Wait<'_, N, S, L, M, WAKER_BATCH_SIZE>
{
#[cold]
pub fn unregister(self: Pin<&mut Self>) {
match self.node_mut().state() {
NodeState::Unlinked(mut node) => {
node.notification.take();
}
NodeState::Linked(node) => {
debug_assert!(node.notification.is_none());
node.unlink(|_, _| STATE_OPEN);
}
}
}
}
impl<N: Notification, S: Synchronization, L: Linking, M: Mutex, const WAKER_BATCH_SIZE: usize>
Future for Wait<'_, N, S, L, M, WAKER_BATCH_SIZE>
{
type Output = Result<N, ClosedError>;
#[cold]
#[allow(clippy::incompatible_msrv)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.node_mut().state() {
NodeState::Unlinked(mut node) => {
if let Some(notification) = node.notification.take() {
return Poll::Ready(Ok(notification.into_inner()));
}
let set_order = match S::MODE {
SyncMode::Sequential => SeqCst,
_ => Relaxed,
};
node.waker = Some(cx.waker().clone());
let pushed = node.try_push_back_with(set_order, Relaxed, |_, state| {
if state == Some(STATE_CLOSED) {
fence(Acquire);
return false;
}
true
});
if !pushed {
return Poll::Ready(Err(ClosedError));
}
if S::SYNC {
fence(SeqCst);
}
Poll::Pending
}
NodeState::Linked(mut node) => node.update_waker(cx, |n| &mut n.waker),
}
}
}
pub trait WakeCondition: Default {
type Output;
fn try_into_output(self) -> Option<Self::Output>;
}
impl WakeCondition for bool {
type Output = ();
fn try_into_output(self) -> Option<Self::Output> {
self.then_some(())
}
}
impl<T> WakeCondition for Option<T> {
type Output = T;
fn try_into_output(self) -> Option<Self::Output> {
self
}
}
pub struct WaitUntil<
'a,
F,
G,
N: Notification = (),
S: Synchronization = Synchronized,
L: Linking = AtomicEager,
M: Mutex = DefaultMutex,
const WAKER_BATCH_SIZE: usize = DEFAULT_WAKER_BATCH_SIZE,
> {
wait: Wait<'a, N, S, L, M, WAKER_BATCH_SIZE>,
wake_condition: F,
on_notification: G,
}
impl<
'a,
W: WakeCondition,
F: FnMut(bool) -> W,
G: FnMut(N) -> W,
N: Notification,
S: Synchronization,
L: Linking,
M: Mutex,
const WAKER_BATCH_SIZE: usize,
> WaitUntil<'a, F, G, N, S, L, M, WAKER_BATCH_SIZE>
{
pub(super) fn new(
wait: Wait<'a, N, S, L, M, WAKER_BATCH_SIZE>,
wake_condition: F,
on_notification: G,
) -> Self {
Self {
wait,
wake_condition,
on_notification,
}
}
#[allow(clippy::type_complexity)]
fn project(
self: Pin<&mut Self>,
) -> (
Pin<&mut Wait<'a, N, S, L, M, WAKER_BATCH_SIZE>>,
&mut F,
&mut G,
) {
let this = unsafe { self.get_unchecked_mut() };
(
unsafe { Pin::new_unchecked(&mut this.wait) },
&mut this.wake_condition,
&mut this.on_notification,
)
}
#[cold]
fn poll_cold(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<W::Output, ClosedError>> {
let (mut wait, wake_condition, on_notification) = self.as_mut().project();
let mut wait_res = wait.as_mut().poll(cx);
if let Poll::Ready(Ok(notification)) = wait_res {
if let Some(res) = on_notification(notification).try_into_output() {
return Poll::Ready(Ok(res));
}
wait_res = wait.as_mut().poll(cx);
}
debug_assert!(matches!(
wait_res,
Poll::Pending | Poll::Ready(Err(ClosedError))
));
match (wake_condition)(true).try_into_output() {
Some(res) => {
wait.unregister();
Poll::Ready(Ok(res))
}
None if wait_res.is_ready() => Poll::Ready(Err(ClosedError)),
None => Poll::Pending,
}
}
}
impl<
W: WakeCondition,
F: FnMut(bool) -> W,
G: FnMut(N) -> W,
N: Notification,
S: Synchronization,
L: Linking,
M: Mutex,
const WAKER_BATCH_SIZE: usize,
> Future for WaitUntil<'_, F, G, N, S, L, M, WAKER_BATCH_SIZE>
{
type Output = Result<W::Output, ClosedError>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let (wait, wake_condition, _) = self.as_mut().project();
match (wake_condition)(false).try_into_output() {
Some(res) => {
if wait.node().is_maybe_linked() {
wait.unregister();
}
Poll::Ready(Ok(res))
}
None => self.poll_cold(cx),
}
}
}