use std::future::Future;
use std::future::IntoFuture;
use std::mem;
use std::pin::Pin;
use std::sync::atomic::Ordering;
use std::sync::mpsc;
use std::task::Context;
use std::task::Poll;
use std::time::Duration;
use self::support::DropProbe;
use self::support::WakerProbe;
use self::support::spawn_named;
use crate::oneshot;
#[test]
fn poll_returns_while_sender_owns_waker() {
let (sender, receiver) = oneshot::channel();
let channel_ptr = sender.channel_ptr();
mem::forget(sender);
let mut receiver = receiver.into_future();
let (stored_waker, stored_probe) = WakerProbe::new();
let mut stored_context = Context::from_waker(&stored_waker);
assert_eq!(
Pin::new(&mut receiver).poll(&mut stored_context),
Poll::Pending
);
let channel = unsafe { channel_ptr.as_ref() };
unsafe { channel.write_message(1234u128) };
assert_eq!(
channel.state.fetch_add(1, Ordering::Release),
super::RECEIVING
);
let (started_tx, started_rx) = mpsc::sync_channel(0);
let (result_tx, result_rx) = mpsc::sync_channel(1);
let poll_thread = spawn_named("receiver", move || {
let (current_waker, current_probe) = WakerProbe::new();
let mut current_context = Context::from_waker(¤t_waker);
started_tx.send(()).unwrap();
let result = Pin::new(&mut receiver).poll(&mut current_context);
result_tx.send((receiver, current_probe, result)).unwrap();
});
started_rx.recv().unwrap();
let result = result_rx.recv_timeout(Duration::from_secs(5));
let returned_before_publish = result.is_ok();
let (sender_waker, receiver_owns_allocation) =
unsafe { channel.finish_sender_awakening(super::MESSAGE) };
assert!(receiver_owns_allocation);
sender_waker.wake();
assert_eq!(stored_probe.wake_count(), 1);
let (mut receiver, current_probe, result) = match result {
Ok(result) => result,
Err(mpsc::RecvTimeoutError::Timeout) => result_rx
.recv_timeout(Duration::from_secs(5))
.expect("receiver remained blocked after the sender published MESSAGE"),
Err(mpsc::RecvTimeoutError::Disconnected) => panic!("receiver thread exited unexpectedly"),
};
poll_thread.join().unwrap();
assert!(
returned_before_publish,
"poll blocked while the sender owned the waker"
);
assert_eq!(result, Poll::Pending);
assert_eq!(current_probe.wake_count(), 1);
let (current_waker, _) = WakerProbe::new();
let mut current_context = Context::from_waker(¤t_waker);
assert_eq!(
Pin::new(&mut receiver).poll(&mut current_context),
Poll::Ready(Ok(1234))
);
}
#[test]
fn drop_transfers_cleanup_while_sender_owns_waker() {
let (sender, receiver) = oneshot::channel();
let channel_ptr = sender.channel_ptr();
mem::forget(sender);
let mut receiver = receiver.into_future();
let (message, message_drop_count) = DropProbe::new(1234u128);
let (stored_waker, stored_probe) = WakerProbe::new();
let mut context = Context::from_waker(&stored_waker);
assert!(matches!(
Pin::new(&mut receiver).poll(&mut context),
Poll::Pending
));
let channel = unsafe { channel_ptr.as_ref() };
unsafe { channel.write_message(message) };
assert_eq!(
channel.state.fetch_add(1, Ordering::Release),
super::RECEIVING
);
let (started_tx, started_rx) = mpsc::sync_channel(0);
let (done_tx, done_rx) = mpsc::sync_channel(1);
let drop_thread = spawn_named("receiver", move || {
started_tx.send(()).unwrap();
drop(receiver);
done_tx.send(()).unwrap();
});
started_rx.recv().unwrap();
let result = done_rx.recv_timeout(Duration::from_secs(5));
let returned_before_publish = result.is_ok();
let (sender_waker, receiver_owns_allocation) =
unsafe { channel.finish_sender_awakening(super::MESSAGE) };
if receiver_owns_allocation {
sender_waker.wake();
} else {
unsafe { super::drop_message_and_deallocate_channel(channel_ptr) };
drop(sender_waker);
}
match result {
Ok(()) => {}
Err(mpsc::RecvTimeoutError::Timeout) => done_rx
.recv_timeout(Duration::from_secs(5))
.expect("receiver remained blocked after the sender published MESSAGE"),
Err(mpsc::RecvTimeoutError::Disconnected) => panic!("receiver thread exited unexpectedly"),
}
drop_thread.join().unwrap();
assert!(
returned_before_publish,
"drop blocked while the sender owned the waker"
);
assert!(!receiver_owns_allocation);
assert_eq!(message_drop_count.load(Ordering::Relaxed), 1);
assert_eq!(WakerProbe::live_waker_count(&stored_probe), 1);
}
mod support {
use std::sync::Arc;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::task::Wake;
use std::task::Waker;
pub(super) struct DropProbe<T> {
drop_count: Arc<AtomicUsize>,
_value: T,
}
impl<T> DropProbe<T> {
pub(super) fn new(value: T) -> (Self, Arc<AtomicUsize>) {
let drop_count = Arc::new(AtomicUsize::new(0));
(
Self {
drop_count: drop_count.clone(),
_value: value,
},
drop_count,
)
}
}
impl<T> Drop for DropProbe<T> {
fn drop(&mut self) {
self.drop_count.fetch_add(1, Ordering::Relaxed);
}
}
#[derive(Default)]
pub(super) struct WakerProbe {
wake_count: AtomicU32,
}
impl WakerProbe {
pub(super) fn new() -> (Waker, Arc<Self>) {
let probe = Arc::new(Self::default());
(Waker::from(probe.clone()), probe)
}
pub(super) fn live_waker_count(this: &Arc<Self>) -> usize {
Arc::strong_count(this) - 1
}
pub(super) fn wake_count(&self) -> u32 {
self.wake_count.load(Ordering::Relaxed)
}
}
impl Wake for WakerProbe {
fn wake(self: Arc<Self>) {
self.wake_count.fetch_add(1, Ordering::Relaxed);
}
fn wake_by_ref(self: &Arc<Self>) {
self.wake_count.fetch_add(1, Ordering::Relaxed);
}
}
pub(super) fn spawn_named<F, T>(name: &str, f: F) -> std::thread::JoinHandle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
std::thread::Builder::new()
.name(name.to_owned())
.spawn(f)
.unwrap()
}
}