use std::fmt;
use std::future::Future;
use std::mem;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use crate::internal::Mutex;
use crate::internal::WaitList;
use crate::mutex;
use crate::mutex::MutexGuard;
use crate::mutex::OwnedMutexGuard;
#[cfg(test)]
mod tests;
pub struct Condvar {
waiters: Mutex<WaitList<WaitNode>>,
}
#[derive(Debug)]
struct WaitNode {
state: WaitState,
}
#[derive(Debug)]
enum WaitState {
Waiting(Waker),
NotifiedOne,
NotifiedAll,
}
fn notify_one_locked(waiters: &mut WaitList<WaitNode>) -> Option<Waker> {
let mut waker = None;
waiters.unlink_first_waiter(|node| {
let WaitState::Waiting(waiting) = mem::replace(&mut node.state, WaitState::NotifiedOne)
else {
unreachable!("only waiting tasks remain linked")
};
waker = Some(waiting);
true
});
waker
}
impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Condvar").finish_non_exhaustive()
}
}
impl Default for Condvar {
fn default() -> Self {
Self::new()
}
}
impl Condvar {
pub const fn new() -> Condvar {
Condvar {
waiters: Mutex::new(WaitList::new()),
}
}
pub fn notify_one(&self) {
let waker = {
let mut waiters = self.waiters.lock();
notify_one_locked(&mut waiters)
};
if let Some(waker) = waker {
waker.wake();
}
}
pub fn notify_all(&self) {
let wakers = {
let mut waiters = self.waiters.lock();
let mut wakers = Vec::new();
while waiters
.unlink_first_waiter(|node| {
let WaitState::Waiting(waker) =
mem::replace(&mut node.state, WaitState::NotifiedAll)
else {
unreachable!("only waiting tasks remain linked")
};
wakers.push(waker);
true
})
.is_some()
{}
wakers
};
for waker in wakers {
waker.wake();
}
}
pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
let mutex = mutex::guard_lock(&guard);
let notify_one_baton = Wait {
condvar: self,
guard: Some(guard),
index: None,
}
.await;
let guard = mutex.lock().await;
if let Some(baton) = notify_one_baton {
baton.complete();
}
guard
}
pub async fn wait_owned<T>(&self, guard: OwnedMutexGuard<T>) -> OwnedMutexGuard<T> {
let mutex = mutex::owned_guard_lock(&guard);
let notify_one_baton = Wait {
condvar: self,
guard: Some(guard),
index: None,
}
.await;
let guard = mutex.lock_owned().await;
if let Some(baton) = notify_one_baton {
baton.complete();
}
guard
}
pub async fn wait_while<'a, T, F>(
&self,
mut guard: MutexGuard<'a, T>,
mut condition: F,
) -> MutexGuard<'a, T>
where
F: FnMut(&mut T) -> bool,
{
while condition(&mut *guard) {
guard = self.wait(guard).await;
}
guard
}
pub async fn wait_while_owned<T, F>(
&self,
mut guard: OwnedMutexGuard<T>,
mut condition: F,
) -> OwnedMutexGuard<T>
where
F: FnMut(&mut T) -> bool,
{
while condition(&mut *guard) {
guard = self.wait_owned(guard).await;
}
guard
}
}
struct Wait<'a, G> {
condvar: &'a Condvar,
guard: Option<G>,
index: Option<usize>,
}
impl<'a, G> Future for Wait<'a, G>
where
G: Unpin,
{
type Output = Option<NotifyOneBaton<'a>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut waiters = this.condvar.waiters.lock();
if let Some(guard) = this.guard.take() {
waiters.register_waiter_to_tail(&mut this.index, || {
Some(WaitNode {
state: WaitState::Waiting(cx.waker().clone()),
})
});
drop(waiters);
drop(guard);
return Poll::Pending;
}
let index = this.index.expect("wait future polled after completion");
let notify_one_baton = match &mut waiters.waiter_mut(index).state {
WaitState::Waiting(waker) => {
if !waker.will_wake(cx.waker()) {
waker.clone_from(cx.waker());
}
return Poll::Pending;
}
WaitState::NotifiedOne => Some(NotifyOneBaton::new(this.condvar)),
WaitState::NotifiedAll => None,
};
waiters.remove_unlinked_waiter(index);
this.index = None;
Poll::Ready(notify_one_baton)
}
}
impl<G> Drop for Wait<'_, G> {
fn drop(&mut self) {
let Some(index) = self.index.take() else {
return;
};
let waker = {
let mut waiters = self.condvar.waiters.lock();
let mut pass_notification = false;
waiters.unlink_waiter(index, |node| match &node.state {
WaitState::Waiting(_) => true,
WaitState::NotifiedOne => {
pass_notification = true;
false
}
WaitState::NotifiedAll => false,
});
waiters.remove_unlinked_waiter(index);
if pass_notification {
notify_one_locked(&mut waiters)
} else {
None
}
};
if let Some(waker) = waker {
waker.wake();
}
}
}
struct NotifyOneBaton<'a> {
condvar: Option<&'a Condvar>,
}
impl<'a> NotifyOneBaton<'a> {
fn new(condvar: &'a Condvar) -> Self {
Self {
condvar: Some(condvar),
}
}
fn complete(mut self) {
self.condvar = None;
}
}
impl Drop for NotifyOneBaton<'_> {
fn drop(&mut self) {
if let Some(condvar) = self.condvar {
condvar.notify_one();
}
}
}