use super::*;
use core::{
cell::Cell,
future::Future,
pin::Pin,
sync::atomic::{AtomicUsize, Ordering},
task::{Context, Poll},
};
use std::{rc::Rc, sync::Arc};
use futures::{
future::FusedFuture,
task::{waker, ArcWake},
};
#[test]
fn bounded_try_send_recv_is_fifo() {
let (tx, rx) = bounded::<u32>(4);
tx.try_send(1).unwrap();
tx.try_send(2).unwrap();
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Ok(2));
assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn bounded_try_send_reports_full() {
let (tx, _rx) = bounded::<u32>(1);
tx.try_send(1).unwrap();
assert!(matches!(tx.try_send(2), Err(TrySendError::Full(2))));
}
#[test]
fn try_send_after_receiver_drop_is_closed() {
let (tx, rx) = bounded::<u32>(1);
drop(rx);
assert!(matches!(tx.try_send(9), Err(TrySendError::Closed(9))));
}
#[test]
fn try_recv_drains_queue_before_reporting_disconnected() {
let (tx, rx) = bounded::<u32>(4);
tx.try_send(1).unwrap();
drop(tx);
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn bounded_reports_len_capacity_and_fullness() {
let (tx, rx) = bounded::<u32>(2);
assert_eq!(tx.capacity(), Some(2));
assert!(tx.is_empty());
assert!(!tx.is_closed());
tx.try_send(1).unwrap();
assert_eq!(tx.len(), 1);
assert!(!tx.is_full());
tx.try_send(2).unwrap();
assert!(tx.is_full());
assert_eq!(rx.try_recv(), Ok(1));
assert!(!tx.is_full());
}
#[test]
fn cloning_a_sender_tracks_live_producers() {
let (tx, rx) = bounded::<u32>(1);
let tx2 = tx.clone();
drop(tx); tx2.try_send(7).unwrap();
drop(tx2); assert_eq!(rx.try_recv(), Ok(7));
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn is_closed_after_last_receiver_drop() {
let (tx, rx) = bounded::<u32>(1);
assert!(!tx.is_closed());
drop(rx);
assert!(tx.is_closed());
}
#[test]
fn cloning_a_receiver_keeps_the_channel_open_until_the_last_drops() {
let (tx, rx) = bounded::<u32>(2);
let rx2 = rx.clone();
drop(rx); assert!(!tx.is_closed());
tx.try_send(7).unwrap();
assert_eq!(rx2.try_recv(), Ok(7));
drop(rx2); assert!(tx.is_closed());
assert!(matches!(tx.try_send(8), Err(TrySendError::Closed(8))));
}
#[test]
fn sender_sees_closed_only_after_the_last_receiver_drops() {
let (tx, rx) = unbounded::<u32>();
let rx2 = rx.clone();
drop(rx);
tx.try_send(1).unwrap();
assert!(!tx.is_closed());
drop(rx2); assert!(tx.is_closed());
assert!(matches!(tx.try_send(2), Err(TrySendError::Closed(2))));
}
#[test]
fn two_receivers_split_the_queued_items() {
let (tx, rx) = unbounded::<u32>();
let rx2 = rx.clone();
tx.try_send(1).unwrap();
tx.try_send(2).unwrap();
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx2.try_recv(), Ok(2));
assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn unbounded_try_send_recv_is_fifo_across_blocks() {
let (tx, rx) = unbounded::<u32>();
for i in 0..100 {
tx.try_send(i).unwrap(); }
for i in 0..100 {
assert_eq!(rx.try_recv(), Ok(i));
}
assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn unbounded_has_no_capacity_and_is_never_full() {
let (tx, _rx) = unbounded::<u32>();
assert_eq!(tx.capacity(), None);
assert!(!tx.is_full());
}
#[test]
fn unbounded_try_send_after_receiver_drop_is_closed() {
let (tx, rx) = unbounded::<u32>();
drop(rx);
assert!(matches!(tx.try_send(1), Err(TrySendError::Closed(1))));
}
#[test]
fn unbounded_drains_queue_before_reporting_disconnected() {
let (tx, rx) = unbounded::<u32>();
tx.try_send(1).unwrap();
drop(tx);
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
}
struct CountingWaker(AtomicUsize);
impl ArcWake for CountingWaker {
fn wake_by_ref(arc: &Arc<Self>) {
arc.0.fetch_add(1, Ordering::SeqCst);
}
}
fn counting_waker() -> (core::task::Waker, Arc<CountingWaker>) {
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
(waker(cw.clone()), cw)
}
fn poll_once<F: Future + Unpin>(fut: &mut F, w: &core::task::Waker) -> Poll<F::Output> {
Pin::new(fut).poll(&mut Context::from_waker(w))
}
#[test]
fn recv_parks_then_wakes_on_send() {
let (tx, rx) = bounded::<u32>(1);
let (w, cw) = counting_waker();
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
tx.try_send(5).unwrap(); assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(Some(5)));
}
#[test]
fn recv_ready_when_item_available() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(9).unwrap();
let (w, _cw) = counting_waker();
let mut fut = rx.recv();
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(Some(9)));
}
#[test]
fn recv_returns_none_when_disconnected() {
let (tx, rx) = bounded::<u32>(1);
drop(tx);
let (w, _cw) = counting_waker();
let mut fut = rx.recv();
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(None));
}
#[test]
fn recv_future_reports_terminated_after_ready() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(1).unwrap();
let (w, _cw) = counting_waker();
let mut fut = rx.recv();
assert!(!fut.is_terminated());
let _ = poll_once(&mut fut, &w);
assert!(fut.is_terminated());
}
#[test]
fn two_parked_receivers_one_send_wakes_both_one_gets_the_item() {
let (tx, rx) = bounded::<u32>(1);
let rx2 = rx.clone();
let (w1, cw1) = counting_waker();
let (w2, cw2) = counting_waker();
let mut f1 = rx.recv();
let mut f2 = rx2.recv();
assert!(poll_once(&mut f1, &w1).is_pending()); assert!(poll_once(&mut f2, &w2).is_pending());
assert_eq!(cw1.0.load(Ordering::SeqCst), 0);
assert_eq!(cw2.0.load(Ordering::SeqCst), 0);
tx.try_send(42).unwrap(); assert_eq!(cw1.0.load(Ordering::SeqCst), 1);
assert_eq!(cw2.0.load(Ordering::SeqCst), 1);
assert_eq!(poll_once(&mut f1, &w1), Poll::Ready(Some(42)));
assert!(poll_once(&mut f2, &w2).is_pending());
tx.try_send(43).unwrap();
assert_eq!(cw2.0.load(Ordering::SeqCst), 2);
assert_eq!(poll_once(&mut f2, &w2), Poll::Ready(Some(43)));
}
#[test]
fn dropping_a_parked_recv_unregisters_its_waker() {
let (tx, rx) = bounded::<u32>(1);
let (w, cw) = counting_waker();
{
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); }
tx.try_send(1).unwrap(); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
}
#[test]
fn re_polling_a_parked_recv_replaces_its_waker() {
let (tx, rx) = unbounded::<u32>();
let (w, cw) = counting_waker();
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); assert!(poll_once(&mut fut, &w).is_pending()); tx.try_send(7).unwrap();
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(Some(7)));
}
#[test]
fn send_ready_when_room() {
let (tx, rx) = bounded::<u32>(2);
let (w, _cw) = counting_waker();
let mut fut = tx.send(7);
assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(()))));
assert_eq!(rx.try_recv(), Ok(7));
}
#[test]
fn send_parks_when_full_then_wakes_on_recv() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(1).unwrap(); let (w, cw) = counting_waker();
let mut fut = tx.send(2);
assert!(poll_once(&mut fut, &w).is_pending()); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
assert_eq!(rx.try_recv(), Ok(1)); assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(()))));
assert_eq!(rx.try_recv(), Ok(2));
}
#[test]
fn send_returns_err_when_receiver_gone() {
let (tx, rx) = bounded::<u32>(1);
drop(rx);
let (w, _cw) = counting_waker();
let mut fut = tx.send(3);
match poll_once(&mut fut, &w) {
Poll::Ready(Err(e)) => assert_eq!(e.into_inner(), 3),
_ => panic!("expected a SendError"),
}
}
#[test]
fn parked_send_wakes_with_closed_only_after_last_receiver_drops() {
let (tx, rx) = bounded::<u32>(1);
let rx2 = rx.clone();
tx.try_send(0).unwrap(); let (w, cw) = counting_waker();
let mut fut = tx.send(1);
assert!(poll_once(&mut fut, &w).is_pending()); drop(rx); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
assert!(poll_once(&mut fut, &w).is_pending()); drop(rx2); assert_eq!(cw.0.load(Ordering::SeqCst), 1);
match poll_once(&mut fut, &w) {
Poll::Ready(Err(e)) => assert_eq!(e.into_inner(), 1),
other => panic!("expected a SendError, got {other:?}"),
}
}
#[test]
fn unbounded_send_is_immediate() {
let (tx, rx) = unbounded::<u32>();
let (w, _cw) = counting_waker();
let mut fut = tx.send(8);
assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(()))));
assert_eq!(rx.try_recv(), Ok(8));
}
#[test]
fn send_future_reports_terminated_after_ready() {
let (tx, rx) = bounded::<u32>(2);
let (w, _cw) = counting_waker();
let mut fut = tx.send(1);
assert!(!fut.is_terminated());
let _ = poll_once(&mut fut, &w);
assert!(fut.is_terminated());
let _ = rx.try_recv();
}
#[test]
fn dropping_a_parked_send_unregisters_its_waker() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(0).unwrap(); let (w, cw) = counting_waker();
{
let mut fut = tx.send(1);
assert!(poll_once(&mut fut, &w).is_pending()); }
rx.try_recv().unwrap(); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
}
#[test]
fn multiple_parked_sends_all_wake_on_recv() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(0).unwrap(); let tx2 = tx.clone();
let (w1, cw1) = counting_waker();
let (w2, cw2) = counting_waker();
let mut f1 = tx.send(1);
let mut f2 = tx2.send(2);
assert!(poll_once(&mut f1, &w1).is_pending());
assert!(poll_once(&mut f2, &w2).is_pending());
rx.try_recv().unwrap(); assert_eq!(cw1.0.load(Ordering::SeqCst), 1);
assert_eq!(cw2.0.load(Ordering::SeqCst), 1);
let _ = poll_once(&mut f1, &w1);
let _ = poll_once(&mut f2, &w2);
}
#[test]
fn last_receiver_drop_wakes_sender_before_panicking_payload_drop() {
use std::panic::{catch_unwind, AssertUnwindSafe};
let drops = Rc::new(Cell::new(0));
#[derive(Debug)]
struct MaybePanic(u32, Rc<Cell<usize>>);
impl Drop for MaybePanic {
fn drop(&mut self) {
self.1.set(self.1.get() + 1);
if self.0 == 99 {
panic!("payload drop panics");
}
}
}
let (tx, rx) = bounded::<MaybePanic>(1);
tx.try_send(MaybePanic(99, drops.clone())).unwrap(); let (w, cw) = counting_waker();
let mut fut = tx.send(MaybePanic(2, drops.clone())); assert!(poll_once(&mut fut, &w).is_pending());
let _ = catch_unwind(AssertUnwindSafe(|| drop(rx)));
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
let _ = poll_once(&mut fut, &w); }
#[test]
fn completed_send_releases_its_waker() {
let (tx, rx) = bounded::<u32>(1);
tx.try_send(0).unwrap(); let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let w = waker(cw.clone());
let mut fut = tx.send(1);
assert!(poll_once(&mut fut, &w).is_pending()); rx.try_recv().unwrap(); assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(())))); assert_eq!(Arc::strong_count(&cw), 2);
}
#[test]
fn completed_recv_releases_its_waker() {
let (tx, rx) = bounded::<u32>(1);
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let w = waker(cw.clone());
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); assert_eq!(Arc::strong_count(&cw), 3); tx.try_send(1).unwrap(); assert_eq!(poll_once(&mut fut, &w), Poll::Ready(Some(1))); assert_eq!(Arc::strong_count(&cw), 2);
}
#[test]
fn send_waker_ops_run_vtable_outside_borrow() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_send_waker(&futures::task::noop_waker());
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_send_waker(&futures::task::noop_waker());
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let id = chan.add_send_waker(&reentrant); chan.remove_send_waker(id); }
#[test]
fn recv_waker_ops_run_vtable_outside_borrow() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_recv_waker(&futures::task::noop_waker());
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_recv_waker(&futures::task::noop_waker());
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let id = chan.add_recv_waker(&reentrant); chan.remove_recv_waker(id); }
#[test]
fn dropping_a_pending_recv_clears_its_waker() {
let (tx, rx) = bounded::<u32>(1);
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let w = waker(cw.clone());
{
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); assert_eq!(Arc::strong_count(&cw), 3); }
assert_eq!(Arc::strong_count(&cw), 2); let _ = tx;
}
#[test]
fn send_rechecks_readiness_after_registration() {
use super::chan::Chan;
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.pop(); RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let tx = Sender::new(chan.clone());
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let mut fut = tx.send(1);
let res = Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant));
assert!(matches!(res, Poll::Ready(Ok(())))); }
#[test]
fn recv_rechecks_readiness_after_registration() {
use super::chan::Chan;
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.try_push(99); RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::bounded(2);
let _tx = Sender::new(chan.clone());
let rx = Receiver::new(chan.clone());
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let mut fut = rx.recv();
let res = Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant));
assert!(matches!(res, Poll::Ready(Some(99)))); }
#[test]
fn drain_runs_payload_drop_outside_flavor_borrow() {
struct Reentrant(Option<Sender<Reentrant>>);
impl Drop for Reentrant {
fn drop(&mut self) {
if let Some(tx) = &self.0 {
let _ = tx.is_full(); }
}
}
let (tx, rx) = bounded::<Reentrant>(4);
tx.try_send(Reentrant(Some(tx.clone()))).unwrap();
tx.try_send(Reentrant(None)).unwrap();
drop(rx);
}
#[test]
fn send_rechecks_closure_after_registration() {
use super::chan::Chan;
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.decr_receivers();
chan.drain();
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let tx = Sender::new(chan.clone());
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let mut fut = tx.send(1);
match Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant)) {
Poll::Ready(Err(e)) => assert_eq!(e.into_inner(), 1),
other => panic!("expected Err for a closed channel, got {other:?}"),
}
}
#[test]
fn recv_recheck_observes_disconnect_during_registration() {
use core::{
ptr,
task::{Context, RawWaker, RawWakerVTable, Waker},
};
std::thread_local! {
static LAST_TX: core::cell::RefCell<Option<Sender<u32>>> =
const { core::cell::RefCell::new(None) };
}
unsafe fn vt_clone(p: *const ()) -> RawWaker {
LAST_TX.with(|t| drop(t.borrow_mut().take()));
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let (tx, rx) = unbounded::<u32>();
LAST_TX.with(|t| *t.borrow_mut() = Some(tx));
let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &VT)) };
let mut fut = rx.recv();
assert_eq!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&waker)),
Poll::Ready(None)
);
}
#[test]
fn last_receiver_drop_drains_even_if_a_sender_waker_panics() {
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let drops = Rc::new(Cell::new(0));
#[allow(dead_code)]
struct Cyclic(Option<Sender<Cyclic>>, Rc<Cell<usize>>);
impl Drop for Cyclic {
fn drop(&mut self) {
self.1.set(self.1.get() + 1);
}
}
let (tx, rx) = bounded::<Cyclic>(1);
tx.try_send(Cyclic(Some(tx.clone()), drops.clone()))
.unwrap();
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut fut = tx.send(Cyclic(None, drops.clone()));
assert!(Pin::new(&mut fut)
.poll(&mut Context::from_waker(&panicking))
.is_pending());
let _ = catch_unwind(AssertUnwindSafe(|| drop(rx)));
assert_eq!(drops.get(), 1); drop(fut); }
#[test]
fn drain_continues_past_a_panicking_payload() {
use std::panic::{catch_unwind, AssertUnwindSafe};
let drops = Rc::new(Cell::new(0));
#[allow(dead_code)]
struct Item {
panic_on_drop: bool,
sender: Option<Sender<Item>>,
drops: Rc<Cell<usize>>,
}
impl Drop for Item {
fn drop(&mut self) {
self.drops.set(self.drops.get() + 1);
if self.panic_on_drop {
panic!("payload drop panics");
}
}
}
let (tx, rx) = bounded::<Item>(4);
tx.try_send(Item {
panic_on_drop: true,
sender: None,
drops: drops.clone(),
})
.unwrap();
tx.try_send(Item {
panic_on_drop: false,
sender: Some(tx.clone()),
drops: drops.clone(),
})
.unwrap();
let _ = catch_unwind(AssertUnwindSafe(|| drop(rx)));
assert_eq!(drops.get(), 2); }
#[test]
fn send_replays_committed_ok_after_a_wake_panic() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("recv waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, rx) = bounded::<u32>(2);
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
tx.chan().add_recv_waker(&panicking);
let (w, _cw) = counting_waker();
let mut fut = tx.send(7);
assert!(catch_unwind(AssertUnwindSafe(|| poll_once(&mut fut, &w))).is_err());
assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(()))));
assert_eq!(rx.try_recv(), Ok(7));
}
#[test]
fn recv_does_not_lose_a_queued_item_when_waking_a_sender_panics() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, rx) = bounded::<u32>(1);
tx.try_send(1).unwrap(); let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut sfut = tx.send(2);
assert!(Pin::new(&mut sfut)
.poll(&mut Context::from_waker(&panicking))
.is_pending()); assert!(catch_unwind(AssertUnwindSafe(|| rx.try_recv())).is_err());
assert_eq!(rx.try_recv(), Ok(1));
drop(sfut);
}
#[test]
fn a_redelivered_item_precedes_the_queue_in_fifo_order() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, rx) = bounded::<u32>(2);
tx.try_send(1).unwrap();
tx.try_send(2).unwrap(); let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut sfut = tx.send(3);
assert!(Pin::new(&mut sfut)
.poll(&mut Context::from_waker(&panicking))
.is_pending()); assert!(catch_unwind(AssertUnwindSafe(|| rx.try_recv())).is_err());
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Ok(2));
drop(sfut);
}
#[test]
fn a_redelivered_item_keeps_the_stream_live_and_non_empty() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use futures_core::stream::FusedStream;
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, rx) = bounded::<u32>(1);
tx.try_send(1).unwrap();
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut sfut = tx.send(2);
assert!(Pin::new(&mut sfut)
.poll(&mut Context::from_waker(&panicking))
.is_pending());
assert!(catch_unwind(AssertUnwindSafe(|| rx.try_recv())).is_err());
drop(sfut);
drop(tx);
assert!(!rx.is_empty());
assert_eq!(rx.len(), 1);
assert!(!rx.is_terminated());
assert_eq!(rx.try_recv(), Ok(1));
assert!(rx.is_empty());
assert!(rx.is_terminated());
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn a_redelivered_item_can_transiently_exceed_capacity() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, rx) = bounded::<u32>(1);
tx.try_send(1).unwrap();
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut sfut = tx.send(2);
assert!(Pin::new(&mut sfut)
.poll(&mut Context::from_waker(&panicking))
.is_pending());
assert!(catch_unwind(AssertUnwindSafe(|| rx.try_recv())).is_err());
drop(sfut);
tx.try_send(3).unwrap();
assert_eq!(tx.capacity(), Some(1));
assert_eq!(rx.len(), 2);
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Ok(3));
assert!(rx.is_empty());
}
#[test]
fn wake_all_isolates_multiple_panicking_wakers() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
let p1 = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let p2 = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
chan.add_send_waker(&p1);
let (cw, cwc) = counting_waker();
chan.add_send_waker(&cw);
chan.add_send_waker(&p2);
assert!(catch_unwind(AssertUnwindSafe(|| chan.wake_senders())).is_err());
assert_eq!(cwc.0.load(Ordering::SeqCst), 1);
}
#[test]
fn wake_senders_releases_the_borrow_before_a_reentrant_wake() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_send_waker(&futures::task::noop_waker());
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
chan.add_send_waker(&reentrant); chan.wake_senders(); }
#[test]
fn wake_receivers_releases_the_borrow_before_a_reentrant_wake() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_recv_waker(&futures::task::noop_waker());
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
chan.add_recv_waker(&reentrant);
chan.wake_receivers();
}
#[test]
fn last_receiver_drop_removes_stream_waker_even_if_a_sender_wake_panics() {
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
use futures_core::stream::Stream;
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &PANIC_VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static PANIC_VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let (tx, mut rx) = bounded::<u32>(1);
let (sw, swc) = counting_waker();
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&sw))
.is_pending());
assert_eq!(Arc::strong_count(&swc), 3); tx.try_send(1).unwrap();
let panic_w = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &PANIC_VT)) };
let mut sfut = tx.send(2);
assert!(Pin::new(&mut sfut)
.poll(&mut Context::from_waker(&panic_w))
.is_pending());
assert!(catch_unwind(AssertUnwindSafe(|| drop(rx))).is_err());
assert_eq!(Arc::strong_count(&swc), 2); drop(sfut);
}
#[test]
fn last_receiver_drop_removes_stream_waker_even_if_a_payload_drop_panics() {
use core::task::Context;
use futures_core::stream::Stream;
use std::panic::{catch_unwind, AssertUnwindSafe};
struct PanicOnDrop;
impl Drop for PanicOnDrop {
fn drop(&mut self) {
panic!("payload drop panics");
}
}
let (tx, mut rx) = bounded::<PanicOnDrop>(1);
let (sw, swc) = counting_waker();
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&sw))
.is_pending());
assert_eq!(Arc::strong_count(&swc), 3); tx.try_send(PanicOnDrop).unwrap();
assert!(catch_unwind(AssertUnwindSafe(|| drop(rx))).is_err());
assert_eq!(Arc::strong_count(&swc), 2); drop(tx);
}
#[test]
fn committed_send_removes_its_send_waker_even_if_a_receiver_wake_panics() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.pop();
RawWaker::new(p, &SEND_VT)
}
unsafe fn vt_noop(_: *const ()) {}
static SEND_VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
unsafe fn vt_rclone(p: *const ()) -> RawWaker {
RawWaker::new(p, &RECV_VT)
}
unsafe fn vt_rwake(_: *const ()) {
panic!("receiver waker panics on wake");
}
static RECV_VT: RawWakerVTable = RawWakerVTable::new(vt_rclone, vt_rwake, vt_rwake, vt_noop);
let chan = Chan::<u32>::bounded(1);
let tx = Sender::new(chan.clone());
chan.try_push(0).unwrap(); let panic_rw = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &RECV_VT)) };
chan.add_recv_waker(&panic_rw);
let reentrant =
unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &SEND_VT)) };
let mut fut = tx.send(7);
assert!(catch_unwind(AssertUnwindSafe(|| {
let _ = Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant));
}))
.is_err());
assert_eq!(chan.send_wakers_len(), 0); }
#[test]
fn recheck_disconnect_removes_the_just_registered_recv_waker() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let slot = unsafe { &*(p as *const Cell<Option<Sender<u32>>>) };
let _ = slot.take(); RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::unbounded();
let tx = Sender::new(chan.clone());
let rx = Receiver::new(chan.clone());
let tx_slot = Cell::new(Some(tx));
let waker = unsafe { Waker::from_raw(RawWaker::new(&tx_slot as *const _ as *const (), &VT)) };
let waker_id = Cell::new(None);
assert!(matches!(
rx.poll_recv(&mut Context::from_waker(&waker), &waker_id),
Poll::Ready(None)
));
assert_eq!(chan.recv_wakers_len(), 0); }
#[test]
fn final_item_recheck_keeps_stream_non_terminal_until_waker_cleared() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use futures_core::stream::{FusedStream, Stream};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let slot = unsafe { &*(p as *const Cell<Option<Sender<u32>>>) };
if let Some(tx) = slot.take() {
let _ = tx.try_send(99); }
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::unbounded();
let tx = Sender::new(chan.clone());
let mut rx = Receiver::new(chan.clone());
let tx_slot = Cell::new(Some(tx));
let waker = unsafe { Waker::from_raw(RawWaker::new(&tx_slot as *const _ as *const (), &VT)) };
assert_eq!(
Pin::new(&mut rx).poll_next(&mut Context::from_waker(&waker)),
Poll::Ready(Some(99))
);
assert!(!rx.is_terminated());
assert_eq!(
Pin::new(&mut rx).poll_next(&mut Context::from_waker(&waker)),
Poll::Ready(None)
);
assert!(rx.is_terminated());
assert_eq!(chan.recv_wakers_len(), 0);
}
#[test]
fn try_take_rechecks_queue_when_a_reentrant_consumer_drains_redelivery() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_wake(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.try_take(); }
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(2);
chan.try_push(1).unwrap();
chan.try_push(2).unwrap(); let waker = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
chan.add_send_waker(&waker);
assert_eq!(chan.try_take(), Some(2));
}
#[test]
fn final_item_recheck_keeps_recv_non_terminal_until_waker_cleared() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use futures_core::future::FusedFuture;
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let slot = unsafe { &*(p as *const Cell<Option<Sender<u32>>>) };
if let Some(tx) = slot.take() {
let _ = tx.try_send(99); }
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::unbounded();
let tx = Sender::new(chan.clone());
let rx = Receiver::new(chan.clone());
let tx_slot = Cell::new(Some(tx));
let waker = unsafe { Waker::from_raw(RawWaker::new(&tx_slot as *const _ as *const (), &VT)) };
let mut fut = rx.recv();
assert_eq!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&waker)),
Poll::Ready(Some(99))
);
assert!(!fut.is_terminated());
let (cw, count) = counting_waker();
assert_eq!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&cw)),
Poll::Pending
);
assert!(fut.is_terminated());
assert!(count.0.load(Ordering::SeqCst) >= 1);
assert_eq!(chan.recv_wakers_len(), 0);
}
#[test]
fn committed_send_is_not_terminated_until_the_outcome_is_replayed() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use futures_core::future::FusedFuture;
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.pop();
RawWaker::new(p, &SEND_VT)
}
unsafe fn vt_noop(_: *const ()) {}
static SEND_VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
unsafe fn vt_rclone(p: *const ()) -> RawWaker {
RawWaker::new(p, &RECV_VT)
}
unsafe fn vt_rwake(_: *const ()) {
panic!("receiver waker panics on wake");
}
static RECV_VT: RawWakerVTable = RawWakerVTable::new(vt_rclone, vt_rwake, vt_rwake, vt_noop);
let chan = Chan::<u32>::bounded(1);
let tx = Sender::new(chan.clone());
chan.try_push(0).unwrap(); let panic_rw = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &RECV_VT)) };
chan.add_recv_waker(&panic_rw);
let reentrant =
unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &SEND_VT)) };
let mut fut = tx.send(7);
assert!(catch_unwind(AssertUnwindSafe(|| {
let _ = Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant));
}))
.is_err());
assert!(!fut.is_terminated());
assert!(matches!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant)),
Poll::Ready(Ok(()))
));
assert!(fut.is_terminated());
}
#[test]
fn last_receiver_drop_decrements_before_a_panicking_stream_waker_drop() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use futures_core::stream::Stream;
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(_: *const ()) {
panic!("stream waker drop panics");
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let (tx, mut rx) = unbounded::<u32>();
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&panicking))
.is_pending());
assert!(catch_unwind(AssertUnwindSafe(|| drop(rx))).is_err());
assert!(tx.is_closed());
assert!(matches!(tx.try_send(9), Err(TrySendError::Closed(9))));
core::mem::forget(panicking);
}
#[test]
fn committed_send_wakes_a_parked_receiver_before_a_panicking_send_waker_drop() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.pop(); RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(_: *const ()) {
panic!("send waker drop panics");
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(1);
let tx = Sender::new(chan.clone());
chan.try_push(0).unwrap(); let (rw, rcw) = counting_waker();
chan.add_recv_waker(&rw);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let mut fut = tx.send(7);
assert!(catch_unwind(AssertUnwindSafe(|| {
let _ = Pin::new(&mut fut).poll(&mut Context::from_waker(&reentrant));
}))
.is_err());
assert_eq!(rcw.0.load(Ordering::SeqCst), 1);
core::mem::forget(reentrant);
}
#[test]
fn send_keeps_message_across_a_panicking_waker_clone() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(_: *const ()) -> RawWaker {
panic!("waker clone panics");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let (tx, rx) = bounded::<u32>(1);
tx.try_send(0).unwrap(); let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut fut = tx.send(7);
assert!(catch_unwind(AssertUnwindSafe(|| {
let _ = Pin::new(&mut fut).poll(&mut Context::from_waker(&panicking));
}))
.is_err());
let (w, _cw) = counting_waker();
assert!(poll_once(&mut fut, &w).is_pending());
assert_eq!(rx.try_recv(), Ok(0));
assert!(matches!(poll_once(&mut fut, &w), Poll::Ready(Ok(()))));
assert_eq!(rx.try_recv(), Ok(7));
}
#[test]
fn recv_keeps_polling_across_a_panicking_waker_clone() {
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(_: *const ()) -> RawWaker {
panic!("waker clone panics");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let (tx, rx) = bounded::<u32>(1);
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let mut fut = rx.recv();
assert!(catch_unwind(AssertUnwindSafe(|| {
let _ = Pin::new(&mut fut).poll(&mut Context::from_waker(&panicking));
}))
.is_err());
let (w, _cw) = counting_waker();
assert!(poll_once(&mut fut, &w).is_pending());
tx.try_send(5).unwrap();
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(Some(5)));
}
#[test]
fn wake_senders_wakes_the_rest_when_one_waker_panics() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("sender waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let normal = waker(cw.clone());
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
chan.add_send_waker(&normal);
chan.add_send_waker(&panicking); let _ = catch_unwind(AssertUnwindSafe(|| chan.wake_senders()));
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
}
#[test]
fn wake_receivers_wakes_the_rest_when_one_waker_panics() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("recv waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::unbounded();
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let normal = waker(cw.clone());
let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
chan.add_recv_waker(&normal);
chan.add_recv_waker(&panicking); let _ = catch_unwind(AssertUnwindSafe(|| chan.wake_receivers()));
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
}
#[test]
fn recv_delivers_rechecked_item_despite_a_panicking_recv_waker_drop() {
use super::chan::Chan;
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.try_push(99); RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(_: *const ()) {
panic!("recv waker drop panics");
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(2);
let _tx = Sender::new(chan.clone());
let rx = Receiver::new(chan.clone());
let waker = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let mut fut = rx.recv();
assert!(matches!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&waker)),
Poll::Ready(Some(99))
));
chan.wake_receivers();
core::mem::forget(fut);
waker.wake();
}
#[test]
fn recv_drop_clears_a_stale_recheck_recv_waker() {
use super::chan::Chan;
use core::{
ptr::addr_of,
task::{Context, RawWaker, RawWakerVTable, Waker},
};
struct WakerCtx {
chan: *const Chan<u32>,
drops: Cell<usize>,
}
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let ctx = unsafe { &*(p as *const WakerCtx) };
let _ = unsafe { (*ctx.chan).try_push(99) }; RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(p: *const ()) {
let ctx = unsafe { &*(p as *const WakerCtx) };
ctx.drops.set(ctx.drops.get() + 1);
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(2);
let tx = Sender::new(chan.clone()); let ctx = WakerCtx {
chan: Rc::as_ptr(&chan),
drops: Cell::new(0),
};
let waker = unsafe { Waker::from_raw(RawWaker::new(addr_of!(ctx) as *const (), &VT)) };
{
let rx = Receiver::new(chan.clone());
let mut fut = rx.recv();
assert!(matches!(
Pin::new(&mut fut).poll(&mut Context::from_waker(&waker)),
Poll::Ready(Some(99))
));
drop(fut); drop(rx);
}
assert_eq!(ctx.drops.get(), 1); drop(tx);
waker.wake(); }
#[test]
fn try_send_error_methods() {
let full = TrySendError::Full(7u32);
assert!(full.is_full());
assert!(!full.is_closed());
assert_eq!(format!("{full}"), "sending on a full channel");
assert_eq!(format!("{full:?}"), "Full(..)");
assert_eq!(full.into_inner(), 7);
let closed = TrySendError::Closed(9u32);
assert!(closed.is_closed());
assert!(!closed.is_full());
assert_eq!(format!("{closed}"), "sending on a closed channel");
assert_eq!(format!("{closed:?}"), "Closed(..)");
assert_eq!(closed.into_inner(), 9);
}
#[test]
fn try_recv_error_methods() {
let empty = TryRecvError::Empty;
assert!(empty.is_empty());
assert!(!empty.is_disconnected());
assert_eq!(empty.as_str(), "receiving on an empty channel");
assert_eq!(format!("{empty}"), empty.as_str());
let disconnected = TryRecvError::Disconnected;
assert!(disconnected.is_disconnected());
assert!(!disconnected.is_empty());
assert_eq!(
disconnected.as_str(),
"receiving on an empty and disconnected channel"
);
assert_eq!(format!("{disconnected}"), disconnected.as_str());
}
#[test]
fn send_error_methods() {
let (tx, rx) = bounded::<u32>(1);
drop(rx);
let (w, _cw) = counting_waker();
let mut fut = tx.send(5);
match poll_once(&mut fut, &w) {
Poll::Ready(Err(e)) => {
assert_eq!(format!("{e}"), "sending on a closed channel");
assert_eq!(format!("{e:?}"), "SendError(..)");
assert_eq!(e.into_inner(), 5);
}
other => panic!("expected a SendError, got {other:?}"),
}
}
#[test]
fn accessors_cover_both_flavors() {
let (tx, rx) = unbounded::<u32>();
assert!(tx.is_empty() && rx.is_empty());
assert_eq!(tx.len(), 0);
assert_eq!(rx.len(), 0);
tx.try_send(1).unwrap();
assert_eq!(tx.len(), 1);
assert_eq!(rx.len(), 1);
assert!(!tx.is_empty() && !rx.is_empty());
assert_eq!(rx.try_recv(), Ok(1));
let (tx, rx) = bounded::<u32>(2);
assert!(rx.is_empty());
assert_eq!(rx.len(), 0);
tx.try_send(1).unwrap();
assert_eq!(rx.len(), 1);
assert!(!rx.is_empty());
assert_eq!(rx.try_recv(), Ok(1));
}
#[test]
fn last_receiver_drop_drains_queued_items() {
let drops = Rc::new(Cell::new(0));
#[derive(Debug)]
struct D(Rc<Cell<usize>>);
impl Drop for D {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
let (tx, rx) = unbounded::<D>();
let rx2 = rx.clone();
for _ in 0..3 {
tx.try_send(D(drops.clone())).unwrap();
}
assert_eq!(drops.get(), 0);
drop(rx); assert_eq!(drops.get(), 0);
drop(rx2); assert_eq!(drops.get(), 3);
}
#[test]
#[should_panic(expected = "non-zero capacity")]
fn bounded_zero_capacity_panics() {
let _ = bounded::<u32>(0);
}
#[test]
fn stream_collects_queued_items_until_disconnect() {
use futures::{executor::block_on, StreamExt};
let (tx, rx) = unbounded::<u32>();
tx.try_send(1).unwrap();
tx.try_send(2).unwrap();
tx.try_send(3).unwrap();
drop(tx); assert_eq!(block_on(rx.collect::<Vec<_>>()), vec![1, 2, 3]);
}
#[test]
fn stream_poll_next_parks_then_wakes_on_send() {
use futures_core::Stream;
let (tx, mut rx) = unbounded::<u32>();
let (w, cw) = counting_waker();
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&w))
.is_pending());
assert_eq!(cw.0.load(Ordering::SeqCst), 0);
tx.try_send(9).unwrap();
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert_eq!(
Pin::new(&mut rx).poll_next(&mut Context::from_waker(&w)),
Poll::Ready(Some(9))
);
}
#[test]
fn stream_re_poll_replaces_its_registration() {
use futures_core::Stream;
let (tx, mut rx) = unbounded::<u32>();
let (w, cw) = counting_waker();
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&w))
.is_pending());
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&w))
.is_pending());
tx.try_send(1).unwrap();
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert_eq!(
Pin::new(&mut rx).poll_next(&mut Context::from_waker(&w)),
Poll::Ready(Some(1))
);
}
#[test]
fn dropping_a_streamed_receiver_clears_its_registration() {
use futures_core::Stream;
let (tx, rx) = unbounded::<u32>();
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
let w = waker(cw.clone());
{
let mut rx = rx;
assert!(Pin::new(&mut rx)
.poll_next(&mut Context::from_waker(&w))
.is_pending()); assert_eq!(Arc::strong_count(&cw), 3); }
assert_eq!(Arc::strong_count(&cw), 2); let _ = tx;
}
#[test]
fn fused_stream_terminates_once_drained_and_disconnected() {
use futures_core::stream::FusedStream;
let (tx, rx) = unbounded::<u32>();
tx.try_send(1).unwrap();
drop(tx);
assert!(!rx.is_terminated()); assert_eq!(rx.try_recv(), Ok(1));
assert!(rx.is_terminated()); }
#[test]
fn try_iter_drains_ready_items_without_blocking() {
let (tx, rx) = unbounded::<u32>();
tx.try_send(1).unwrap();
tx.try_send(2).unwrap();
let drained: Vec<u32> = rx.try_iter().collect();
assert_eq!(drained, vec![1, 2]);
assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
tx.try_send(3).unwrap();
assert_eq!(rx.try_recv(), Ok(3));
}
#[test]
fn receiver_is_closed_once_every_sender_drops() {
let (tx, rx) = bounded::<u32>(1);
assert!(!rx.is_closed());
drop(tx);
assert!(rx.is_closed());
}
#[test]
fn close_from_sender_disconnects_both_ends() {
let (tx, rx) = bounded::<u32>(2);
assert!(!tx.is_closed());
assert!(!rx.is_closed());
assert!(tx.close()); assert!(tx.is_closed());
assert!(rx.is_closed());
assert!(matches!(tx.try_send(1), Err(TrySendError::Closed(1))));
}
#[test]
fn close_from_receiver_disconnects_both_ends() {
let (tx, rx) = bounded::<u32>(2);
assert!(rx.close());
assert!(rx.is_closed());
assert!(tx.is_closed());
assert!(matches!(tx.try_send(1), Err(TrySendError::Closed(1))));
}
#[test]
fn close_keeps_queued_items_drainable_then_disconnects() {
let (tx, rx) = bounded::<u32>(4);
tx.try_send(1).unwrap();
tx.try_send(2).unwrap();
tx.close();
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Ok(2));
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn close_is_idempotent() {
let (tx, _rx) = bounded::<u32>(1);
assert!(tx.close()); assert!(!tx.close()); }
#[test]
fn close_disconnects_every_receiver() {
let (tx, rx) = bounded::<u32>(1);
let rx2 = rx.clone();
assert!(tx.close());
assert!(rx.is_closed());
assert!(rx2.is_closed());
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
assert_eq!(rx2.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn close_wakes_a_parked_recv_to_none() {
let (tx, rx) = bounded::<u32>(1);
let (w, cw) = counting_waker();
let mut fut = rx.recv();
assert!(poll_once(&mut fut, &w).is_pending()); assert_eq!(cw.0.load(Ordering::SeqCst), 0);
tx.close(); assert_eq!(cw.0.load(Ordering::SeqCst), 1);
assert_eq!(poll_once(&mut fut, &w), Poll::Ready(None));
}
#[test]
fn close_after_receiver_drop_returns_false() {
let (tx, rx) = bounded::<u32>(1);
drop(rx);
assert!(tx.is_closed());
assert!(!tx.close()); }
#[test]
fn close_after_senders_drop_returns_false() {
let (tx, rx) = bounded::<u32>(1);
drop(tx);
assert!(rx.is_closed());
assert!(!rx.close()); }
#[test]
fn close_wakes_parked_senders_even_if_a_recv_waker_panics() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("recv waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let panicking_recv = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
chan.add_recv_waker(&panicking_recv);
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
chan.add_send_waker(&waker(cw.clone()));
let _ = catch_unwind(AssertUnwindSafe(|| chan.close()));
assert_eq!(cw.0.load(Ordering::SeqCst), 1); }
#[test]
fn close_isolates_panics_from_both_a_recv_and_a_send_waker() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
chan.add_recv_waker(&panicking); let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
chan.add_send_waker(&waker(cw.clone())); chan.add_send_waker(&panicking); let _ = catch_unwind(AssertUnwindSafe(|| chan.close()));
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
}
#[test]
fn re_entrant_drop_cannot_repopulate_recv_wakers_during_disconnect() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
unsafe fn vt_drop(p: *const ()) {
let chan = unsafe { &*(p as *const Chan<u32>) };
chan.add_recv_waker(&futures::task::noop_waker());
}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_drop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
chan.add_recv_waker(&reentrant);
let tx = Sender::new(chan.clone());
drop(tx); assert_eq!(chan.recv_wakers_len(), 0); }
#[test]
fn item_and_close_during_recv_registration_leaves_no_orphan() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
let chan = unsafe { &*(p as *const Chan<u32>) };
let _ = chan.try_push(7); chan.close();
RawWaker::new(p, &VT)
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_noop, vt_noop, vt_noop);
let chan = Chan::<u32>::bounded(1);
let reentrant = unsafe { Waker::from_raw(RawWaker::new(Rc::as_ptr(&chan) as *const (), &VT)) };
let rx = Receiver::new(chan.clone());
let mut fut = rx.recv();
assert_eq!(poll_once(&mut fut, &reentrant), Poll::Ready(Some(7)));
assert_eq!(chan.recv_wakers_len(), 0);
}
#[test]
fn close_isolates_two_panicking_send_wakers() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
panic!("send waker panics on wake");
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let panicking = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
chan.add_send_waker(&panicking);
chan.add_send_waker(&waker(cw.clone())); chan.add_send_waker(&panicking);
let _ = catch_unwind(AssertUnwindSafe(|| chan.close()));
assert_eq!(cw.0.load(Ordering::SeqCst), 1); }
#[test]
fn close_does_not_abort_on_a_toxic_panic_payload() {
use super::chan::Chan;
use core::task::{RawWaker, RawWakerVTable, Waker};
use std::panic::{catch_unwind, AssertUnwindSafe};
struct PanicOnDrop;
impl Drop for PanicOnDrop {
fn drop(&mut self) {
panic!("toxic payload dropped");
}
}
unsafe fn vt_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VT)
}
unsafe fn vt_wake(_: *const ()) {
std::panic::panic_any(PanicOnDrop);
}
unsafe fn vt_noop(_: *const ()) {}
static VT: RawWakerVTable = RawWakerVTable::new(vt_clone, vt_wake, vt_wake, vt_noop);
let chan = Chan::<u32>::bounded(1);
chan.try_push(0).unwrap(); let toxic = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VT)) };
let cw = Arc::new(CountingWaker(AtomicUsize::new(0)));
chan.add_recv_waker(&toxic); chan.add_send_waker(&waker(cw.clone())); chan.add_send_waker(&toxic); chan.add_send_waker(&toxic); if let Err(payload) = catch_unwind(AssertUnwindSafe(|| chan.close())) {
crate::drop_panic_payload(payload);
}
assert_eq!(cw.0.load(Ordering::SeqCst), 1);
}