use std::{
sync::{Arc, atomic::Ordering},
task::Poll,
};
use crate::{Closed, Counts, State, lock::*, producer::Ref, waiter::*, weak::ConsumerWeak};
#[derive(Debug)]
pub struct Consumer<T> {
pub(crate) state: Lock<State<T>>,
pub(crate) counts: Arc<Counts>,
}
impl<T> Consumer<T> {
pub fn poll<F, R>(&self, waiter: &Waiter, mut f: F) -> Poll<Result<R, Ref<'_, T>>>
where
F: FnMut(&Ref<'_, T>) -> Poll<R>,
{
let state = self.state.lock();
let consumer_state = Ref { state };
if let Poll::Ready(res) = f(&consumer_state) {
return Poll::Ready(Ok(res));
}
if consumer_state.state.closed {
return Poll::Ready(Err(consumer_state));
}
let mut state = consumer_state.state;
waiter.register(&mut state.waiters_value);
Poll::Pending
}
pub fn poll_closed(&self, waiter: &Waiter) -> Poll<()> {
let mut state = self.state.lock();
if state.closed {
return Poll::Ready(());
}
waiter.register(&mut state.waiters_closed);
Poll::Pending
}
pub async fn wait<F, R>(&self, mut f: F) -> Result<R, Closed>
where
F: FnMut(&Ref<'_, T>) -> Poll<R> + Unpin,
{
crate::wait(move |waiter| self.poll(waiter, &mut f).map(|res| res.map_err(|_| Closed))).await
}
pub async fn closed(&self) {
crate::wait(move |waiter| self.poll_closed(waiter)).await
}
pub fn read(&self) -> Ref<'_, T> {
Ref {
state: self.state.lock(),
}
}
pub fn is_closed(&self) -> bool {
self.state.lock().closed
}
pub fn same_channel(&self, other: &Self) -> bool {
self.state.is_clone(&other.state)
}
pub fn weak(&self) -> ConsumerWeak<T> {
ConsumerWeak {
state: self.state.clone(),
counts: self.counts.clone(),
}
}
}
impl<T> Drop for Consumer<T> {
fn drop(&mut self) {
let prev = self.counts.consumers.fetch_sub(1, Ordering::AcqRel);
if prev > 1 {
return;
}
let mut waiters = {
let mut state = self.state.lock();
state.waiters_consumer.take()
};
waiters.wake();
}
}
impl<T> Clone for Consumer<T> {
fn clone(&self) -> Self {
self.counts.consumers.fetch_add(1, Ordering::Relaxed);
Self {
state: self.state.clone(),
counts: self.counts.clone(),
}
}
}
#[cfg(test)]
mod test {
use crate::{Closed, Producer};
use std::task::Poll;
#[tokio::test]
async fn wait_reports_closure_without_holding_the_lock() {
let producer = Producer::new(0u32);
let consumer = producer.consume();
let never = |v: &crate::Ref<'_, u32>| if **v == 99 { Poll::Ready(()) } else { Poll::Pending };
producer.close().ok().expect("open");
let err = consumer.wait(never).await.expect_err("closed");
assert_eq!(err, Closed);
tokio::task::yield_now().await;
assert_eq!(err, Closed);
assert_eq!(*consumer.read(), 0);
assert!(consumer.is_closed());
}
}