2019-04-23 23:17:57 -04:00
|
|
|
//! Futures task based helpers
|
|
|
|
|
//!
|
|
|
|
|
//! # Example
|
|
|
|
|
//!
|
|
|
|
|
//! This example will use the `MockTask` to set the current task on
|
|
|
|
|
//! poll.
|
|
|
|
|
//!
|
|
|
|
|
//! ```
|
2019-05-14 10:27:36 -07:00
|
|
|
//! # use tokio_test::assert_ready_eq;
|
2019-04-23 23:17:57 -04:00
|
|
|
//! # use tokio_test::task::MockTask;
|
|
|
|
|
//! # use futures::{sync::mpsc, Stream, Sink, Future, Async};
|
|
|
|
|
//! let mut task = MockTask::new();
|
|
|
|
|
//! let (tx, mut rx) = mpsc::channel(5);
|
|
|
|
|
//!
|
|
|
|
|
//! tx.send(()).wait();
|
|
|
|
|
//!
|
|
|
|
|
//! assert_ready_eq!(task.enter(|| rx.poll()), Some(()));
|
|
|
|
|
//! ```
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
use tokio_executor::enter;
|
|
|
|
|
|
|
|
|
|
use pin_convert::AsPinMut;
|
|
|
|
|
use std::future::Future;
|
|
|
|
|
use std::mem;
|
2019-04-23 23:17:57 -04:00
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
2019-06-24 12:34:30 -07:00
|
|
|
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
2019-04-23 23:17:57 -04:00
|
|
|
|
|
|
|
|
/// Mock task
|
|
|
|
|
///
|
2019-06-24 12:34:30 -07:00
|
|
|
/// A mock task is able to intercept and track wake notifications.
|
2019-04-23 23:17:57 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct MockTask {
|
2019-06-24 12:34:30 -07:00
|
|
|
waker: Arc<ThreadWaker>,
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2019-06-24 12:34:30 -07:00
|
|
|
struct ThreadWaker {
|
|
|
|
|
state: Mutex<usize>,
|
2019-04-23 23:17:57 -04:00
|
|
|
condvar: Condvar,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const IDLE: usize = 0;
|
2019-06-24 12:34:30 -07:00
|
|
|
const WAKE: usize = 1;
|
2019-04-23 23:17:57 -04:00
|
|
|
const SLEEP: usize = 2;
|
|
|
|
|
|
|
|
|
|
impl MockTask {
|
|
|
|
|
/// Create a new mock task
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
MockTask {
|
2019-06-24 12:34:30 -07:00
|
|
|
waker: Arc::new(ThreadWaker::new()),
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
/// Poll a future
|
|
|
|
|
pub fn poll<T, F>(&mut self, mut fut: T) -> Poll<F::Output>
|
|
|
|
|
where
|
|
|
|
|
T: AsPinMut<F>,
|
|
|
|
|
F: Future,
|
|
|
|
|
{
|
|
|
|
|
self.enter(|cx| fut.as_pin_mut().poll(cx))
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 23:17:57 -04:00
|
|
|
/// Run a closure from the context of the task.
|
|
|
|
|
///
|
2019-06-24 12:34:30 -07:00
|
|
|
/// Any wake notifications resulting from the execution of the closure are
|
2019-04-23 23:17:57 -04:00
|
|
|
/// tracked.
|
|
|
|
|
pub fn enter<F, R>(&mut self, f: F) -> R
|
|
|
|
|
where
|
2019-06-24 12:34:30 -07:00
|
|
|
F: FnOnce(&mut Context<'_>) -> R,
|
2019-04-23 23:17:57 -04:00
|
|
|
{
|
2019-06-24 12:34:30 -07:00
|
|
|
let _enter = enter().unwrap();
|
2019-04-23 23:17:57 -04:00
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
self.waker.clear();
|
|
|
|
|
let waker = self.waker();
|
|
|
|
|
let mut cx = Context::from_waker(&waker);
|
2019-04-23 23:17:57 -04:00
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
f(&mut cx)
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
/// Returns `true` if the inner future has received a wake notification
|
2019-04-23 23:17:57 -04:00
|
|
|
/// since the last call to `enter`.
|
2019-06-24 12:34:30 -07:00
|
|
|
pub fn is_woken(&self) -> bool {
|
|
|
|
|
self.waker.is_woken()
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
/// Returns the number of references to the task waker
|
2019-04-23 23:17:57 -04:00
|
|
|
///
|
|
|
|
|
/// The task itself holds a reference. The return value will never be zero.
|
2019-06-24 12:34:30 -07:00
|
|
|
pub fn waker_ref_count(&self) -> usize {
|
|
|
|
|
Arc::strong_count(&self.waker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn waker(&self) -> Waker {
|
|
|
|
|
unsafe {
|
|
|
|
|
let raw = to_raw(self.waker.clone());
|
|
|
|
|
Waker::from_raw(raw)
|
|
|
|
|
}
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
impl ThreadWaker {
|
2019-04-23 23:17:57 -04:00
|
|
|
fn new() -> Self {
|
2019-06-24 12:34:30 -07:00
|
|
|
ThreadWaker {
|
|
|
|
|
state: Mutex::new(IDLE),
|
2019-04-23 23:17:57 -04:00
|
|
|
condvar: Condvar::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
/// Clears any previously received wakes, avoiding potential spurrious
|
|
|
|
|
/// wake notifications. This should only be called immediately before running the
|
2019-04-23 23:17:57 -04:00
|
|
|
/// task.
|
|
|
|
|
fn clear(&self) {
|
2019-06-24 12:34:30 -07:00
|
|
|
*self.state.lock().unwrap() = IDLE;
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
fn is_woken(&self) -> bool {
|
|
|
|
|
match *self.state.lock().unwrap() {
|
2019-04-23 23:17:57 -04:00
|
|
|
IDLE => false,
|
2019-06-24 12:34:30 -07:00
|
|
|
WAKE => true,
|
2019-04-23 23:17:57 -04:00
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
fn wake(&self) {
|
2019-04-23 23:17:57 -04:00
|
|
|
// First, try transitioning from IDLE -> NOTIFY, this does not require a
|
|
|
|
|
// lock.
|
2019-06-24 12:34:30 -07:00
|
|
|
let mut state = self.state.lock().unwrap();
|
|
|
|
|
let prev = *state;
|
|
|
|
|
|
|
|
|
|
if prev == WAKE {
|
|
|
|
|
return;
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
*state = WAKE;
|
2019-04-23 23:17:57 -04:00
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
if prev == IDLE {
|
|
|
|
|
return;
|
2019-04-23 23:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-06-24 12:34:30 -07:00
|
|
|
// The other half is sleeping, so we wake it up.
|
|
|
|
|
assert_eq!(prev, SLEEP);
|
2019-04-23 23:17:57 -04:00
|
|
|
self.condvar.notify_one();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-24 12:34:30 -07:00
|
|
|
|
|
|
|
|
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
|
|
|
|
|
|
|
|
|
unsafe fn to_raw(waker: Arc<ThreadWaker>) -> RawWaker {
|
|
|
|
|
RawWaker::new(Arc::into_raw(waker) as *const (), &VTABLE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn from_raw(raw: *const ()) -> Arc<ThreadWaker> {
|
|
|
|
|
Arc::from_raw(raw as *const ThreadWaker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn clone(raw: *const ()) -> RawWaker {
|
|
|
|
|
let waker = from_raw(raw);
|
|
|
|
|
|
|
|
|
|
// Increment the ref count
|
|
|
|
|
mem::forget(waker.clone());
|
|
|
|
|
|
|
|
|
|
to_raw(waker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn wake(raw: *const ()) {
|
|
|
|
|
let waker = from_raw(raw);
|
|
|
|
|
waker.wake();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn wake_by_ref(raw: *const ()) {
|
|
|
|
|
let waker = from_raw(raw);
|
|
|
|
|
waker.wake();
|
|
|
|
|
|
|
|
|
|
// We don't actually own a reference to the unparker
|
|
|
|
|
mem::forget(waker);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn drop(raw: *const ()) {
|
|
|
|
|
let _ = from_raw(raw);
|
|
|
|
|
}
|