mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-08 00:00:13 +02:00
Update Tokio to use std::future. (#1120)
A first pass at updating Tokio to use `std::future`. Implementations of `Future` from the futures crate are updated to implement `Future` from std. Implementations of `Stream` are moved to a feature flag. This commits disables a number of crates that have not yet been updated.
This commit is contained in:
@@ -24,4 +24,6 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = { version = "0.2.0", path = "../tokio-executor" }
|
||||
futures = "0.1.19"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-sync = { version = "0.2.0", path = "../tokio-sync" }
|
||||
|
||||
@@ -30,13 +30,14 @@
|
||||
mod scheduler;
|
||||
|
||||
use crate::scheduler::Scheduler;
|
||||
use futures::future::{ExecuteError, ExecuteErrorKind, Executor};
|
||||
use futures::{executor, Async, Future};
|
||||
use std::cell::Cell;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_executor::park::{Park, ParkThread, Unpark};
|
||||
@@ -60,7 +61,7 @@ pub struct CurrentThread<P: Park = ParkThread> {
|
||||
spawn_handle: Handle,
|
||||
|
||||
/// Receiver for futures spawned from other threads
|
||||
spawn_receiver: mpsc::Receiver<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
spawn_receiver: mpsc::Receiver<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
|
||||
|
||||
/// The thread-local ID assigned to this executor.
|
||||
id: u64,
|
||||
@@ -182,11 +183,7 @@ struct Borrow<'a, U> {
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
);
|
||||
fn spawn_local(&mut self, future: Pin<Box<dyn Future<Output = ()>>>, already_counted: bool);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
@@ -225,7 +222,7 @@ thread_local! {
|
||||
///
|
||||
/// [`CurrentThread`]: struct.CurrentThread.html
|
||||
/// [mod]: index.html
|
||||
pub fn block_on_all<F>(future: F) -> Result<F::Item, F::Error>
|
||||
pub fn block_on_all<F>(future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
@@ -233,8 +230,7 @@ where
|
||||
|
||||
let ret = current_thread.block_on(future);
|
||||
current_thread.run().unwrap();
|
||||
|
||||
ret.map_err(|e| e.into_inner().expect("unexpected execution error"))
|
||||
ret
|
||||
}
|
||||
|
||||
/// Executes a future on the current thread.
|
||||
@@ -252,10 +248,10 @@ where
|
||||
/// [`tokio::spawn`]: ../fn.spawn.html
|
||||
pub fn spawn<F>(future: F)
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
TaskExecutor::current()
|
||||
.spawn_local(Box::new(future))
|
||||
.spawn_local(Box::pin(future))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -283,7 +279,7 @@ impl<P: Park> CurrentThread<P> {
|
||||
});
|
||||
|
||||
let scheduler = Scheduler::new(unpark);
|
||||
let notify = scheduler.notify();
|
||||
let waker = scheduler.waker();
|
||||
|
||||
let num_futures = Arc::new(atomic::AtomicUsize::new(0));
|
||||
|
||||
@@ -294,10 +290,10 @@ impl<P: Park> CurrentThread<P> {
|
||||
id,
|
||||
spawn_handle: Handle {
|
||||
sender: spawn_sender,
|
||||
num_futures: num_futures,
|
||||
notify: notify,
|
||||
num_futures,
|
||||
waker,
|
||||
shut_down: Cell::new(false),
|
||||
thread: thread,
|
||||
thread,
|
||||
id,
|
||||
},
|
||||
spawn_receiver: spawn_receiver,
|
||||
@@ -319,9 +315,9 @@ impl<P: Park> CurrentThread<P> {
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.borrow().spawn_local(Box::new(future), false);
|
||||
self.borrow().spawn_local(Box::pin(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -338,7 +334,7 @@ impl<P: Park> CurrentThread<P> {
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F) -> Result<F::Item, BlockError<F::Error>>
|
||||
pub fn block_on<F>(&mut self, future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
@@ -424,7 +420,7 @@ impl<P: Park> Drop for CurrentThread<P> {
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(future, false);
|
||||
Ok(())
|
||||
@@ -433,10 +429,10 @@ impl tokio_executor::Executor for CurrentThread {
|
||||
|
||||
impl<T> tokio_executor::TypedExecutor<T> for CurrentThread
|
||||
where
|
||||
T: Future<Item = (), Error = ()> + 'static,
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(Box::new(future), false);
|
||||
self.borrow().spawn_local(Box::pin(future), false);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -461,9 +457,9 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
/// This internally queues the future to be executed once `run` is called.
|
||||
pub fn spawn<F>(&mut self, future: F) -> &mut Self
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.executor.borrow().spawn_local(Box::new(future), false);
|
||||
self.executor.borrow().spawn_local(Box::pin(future), false);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -480,29 +476,35 @@ impl<'a, P: Park> Entered<'a, P> {
|
||||
///
|
||||
/// The caller is responsible for ensuring that other spawned futures
|
||||
/// complete execution.
|
||||
pub fn block_on<F>(&mut self, future: F) -> Result<F::Item, BlockError<F::Error>>
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the `Park` call returns an error.
|
||||
pub fn block_on<F>(&mut self, mut future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let mut future = executor::spawn(future);
|
||||
let notify = self.executor.scheduler.notify();
|
||||
// Safety: we shadow the original `future`, so it will never move
|
||||
// again.
|
||||
let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
||||
let waker = self.executor.scheduler.waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
loop {
|
||||
let res = self
|
||||
.executor
|
||||
.borrow()
|
||||
.enter(self.enter, || future.poll_future_notify(¬ify, 0));
|
||||
.enter(self.enter, || future.as_mut().poll(&mut cx));
|
||||
|
||||
match res {
|
||||
Ok(Async::Ready(e)) => return Ok(e),
|
||||
Err(e) => return Err(BlockError { inner: Some(e) }),
|
||||
Ok(Async::NotReady) => {}
|
||||
Poll::Ready(e) => return e,
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
||||
self.tick();
|
||||
|
||||
if let Err(_) = self.executor.park.park() {
|
||||
return Err(BlockError { inner: None });
|
||||
panic!("block_on park failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,10 +631,11 @@ impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` instance
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
sender: mpsc::Sender<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
sender: mpsc::Sender<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
|
||||
num_futures: Arc<atomic::AtomicUsize>,
|
||||
shut_down: Cell<bool>,
|
||||
notify: executor::NotifyHandle,
|
||||
/// Waker to the Scheduler
|
||||
waker: Waker,
|
||||
thread: thread::ThreadId,
|
||||
|
||||
/// The thread-local ID assigned to this Handle's executor.
|
||||
@@ -657,12 +660,12 @@ impl Handle {
|
||||
/// instance of the `Handle` does not exist anymore.
|
||||
pub fn spawn<F>(&self, future: F) -> Result<(), SpawnError>
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + Send + 'static,
|
||||
F: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
if thread::current().id() == self.thread {
|
||||
let mut e = TaskExecutor::current();
|
||||
if e.id() == Some(self.id) {
|
||||
return e.spawn_local(Box::new(future));
|
||||
return e.spawn_local(Box::pin(future));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,10 +686,9 @@ impl Handle {
|
||||
}
|
||||
|
||||
self.sender
|
||||
.send(Box::new(future))
|
||||
.send(Box::pin(future))
|
||||
.expect("CurrentThread does not exist anymore");
|
||||
// use 0 for the id, CurrentThread does not make use of it
|
||||
self.notify.notify(0);
|
||||
self.waker.wake_by_ref();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -731,7 +733,7 @@ impl TaskExecutor {
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
CURRENT.with(|current| match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
@@ -746,7 +748,7 @@ impl TaskExecutor {
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.spawn_local(future)
|
||||
}
|
||||
@@ -754,25 +756,10 @@ impl tokio_executor::Executor for TaskExecutor {
|
||||
|
||||
impl<F> tokio_executor::TypedExecutor<F> for TaskExecutor
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
fn spawn(&mut self, future: F) -> Result<(), SpawnError> {
|
||||
self.spawn_local(Box::new(future))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Executor<F> for TaskExecutor
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
{
|
||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
||||
CURRENT.with(|current| match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
unsafe { (*spawn).spawn_local(Box::new(future), false) };
|
||||
Ok(())
|
||||
}
|
||||
None => Err(ExecuteError::new(ExecuteErrorKind::Shutdown, future)),
|
||||
})
|
||||
self.spawn_local(Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,11 +778,7 @@ impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
) {
|
||||
fn spawn_local(&mut self, future: Pin<Box<dyn Future<Output = ()>>>, already_counted: bool) {
|
||||
if !already_counted {
|
||||
// NOTE: we have a borrow of the Runtime, so we know that it isn't shut down.
|
||||
// NOTE: += 2 since LSB is the shutdown bit
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::Borrow;
|
||||
use futures::executor::{self, NotifyHandle, Spawn, UnsafeNotify};
|
||||
use futures::{Async, Future};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker::PhantomData;
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
use std::thread;
|
||||
use std::usize;
|
||||
use tokio_executor::park::Unpark;
|
||||
@@ -22,8 +22,6 @@ pub struct Scheduler<U> {
|
||||
nodes: List<U>,
|
||||
}
|
||||
|
||||
pub struct Notify<'a, U>(&'a Arc<Node<U>>);
|
||||
|
||||
// A linked-list of nodes
|
||||
struct List<U> {
|
||||
len: usize,
|
||||
@@ -78,12 +76,6 @@ struct Inner<U> {
|
||||
unsafe impl<U: Sync + Send> Send for Inner<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for Inner<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for Inner<U> {
|
||||
fn notify(&self, _: usize) {
|
||||
self.unpark.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
struct Node<U> {
|
||||
// The item
|
||||
item: UnsafeCell<Option<Task>>,
|
||||
@@ -123,12 +115,12 @@ enum Dequeue<U> {
|
||||
}
|
||||
|
||||
/// Wraps a spawned boxed future
|
||||
struct Task(Spawn<Box<dyn Future<Item = (), Error = ()>>>);
|
||||
struct Task(Pin<Box<dyn Future<Output = ()>>>);
|
||||
|
||||
/// A task that is scheduled. `turn` must be called
|
||||
pub struct Scheduled<'a, U> {
|
||||
task: &'a mut Task,
|
||||
notify: &'a Notify<'a, U>,
|
||||
node: &'a Arc<Node<U>>,
|
||||
done: &'a mut bool,
|
||||
}
|
||||
|
||||
@@ -165,11 +157,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) -> NotifyHandle {
|
||||
self.inner.clone().into()
|
||||
pub fn waker(&self) -> Waker {
|
||||
waker_inner(self.inner.clone())
|
||||
}
|
||||
|
||||
pub fn schedule(&mut self, item: Box<dyn Future<Item = (), Error = ()>>) {
|
||||
pub fn schedule(&mut self, item: Pin<Box<dyn Future<Output = ()>>>) {
|
||||
// Get the current scheduler tick
|
||||
let tick_num = self.inner.tick_num.load(SeqCst);
|
||||
|
||||
@@ -317,11 +309,10 @@ where
|
||||
// deallocating the node if need be.
|
||||
let borrow = &mut *bomb.borrow;
|
||||
let enter = &mut *bomb.enter;
|
||||
let notify = Notify(bomb.node.as_ref().unwrap());
|
||||
|
||||
let mut scheduled = Scheduled {
|
||||
task: item,
|
||||
notify: ¬ify,
|
||||
node: bomb.node.as_ref().unwrap(),
|
||||
done: &mut done,
|
||||
};
|
||||
|
||||
@@ -345,10 +336,15 @@ where
|
||||
impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
/// Polls the task, returns `true` if the task has completed.
|
||||
pub fn tick(&mut self) -> bool {
|
||||
// Tick the future
|
||||
let ret = match self.task.0.poll_future_notify(self.notify, 0) {
|
||||
Ok(Async::Ready(_)) | Err(_) => true,
|
||||
Ok(Async::NotReady) => false,
|
||||
let waker = unsafe {
|
||||
// Safety: we don't hold this waker ref longer than
|
||||
// this `tick` function
|
||||
waker_ref(self.node)
|
||||
};
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
let ret = match self.task.0.as_mut().poll(&mut cx) {
|
||||
Poll::Ready(()) => true,
|
||||
Poll::Pending => false,
|
||||
};
|
||||
|
||||
*self.done = ret;
|
||||
@@ -357,8 +353,8 @@ impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(future: Box<dyn Future<Item = (), Error = ()> + 'static>) -> Self {
|
||||
Task(executor::spawn(future))
|
||||
pub fn new(future: Pin<Box<dyn Future<Output = ()> + 'static>>) -> Self {
|
||||
Task(future)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,63 +626,101 @@ impl<U> List<U> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U> Clone for Notify<'a, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Notify(self.0)
|
||||
}
|
||||
unsafe fn noop(_: *const ()) {}
|
||||
|
||||
// ===== Raw Waker Inner<U> ======
|
||||
|
||||
fn waker_inner<U: Unpark>(inner: Arc<Inner<U>>) -> Waker {
|
||||
let ptr = Arc::into_raw(inner) as *const ();
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_inner::<U>,
|
||||
wake_inner::<U>,
|
||||
wake_by_ref_inner::<U>,
|
||||
drop_inner::<U>,
|
||||
);
|
||||
|
||||
unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) }
|
||||
}
|
||||
|
||||
impl<'a, U> fmt::Debug for Notify<'a, U> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("Notify").finish()
|
||||
}
|
||||
unsafe fn clone_inner<U: Unpark>(data: *const ()) -> RawWaker {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
let clone = arc.clone();
|
||||
// forget both Arcs so the refcounts don't get decremented
|
||||
mem::forget(arc);
|
||||
mem::forget(clone);
|
||||
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_inner::<U>,
|
||||
wake_inner::<U>,
|
||||
wake_by_ref_inner::<U>,
|
||||
drop_inner::<U>,
|
||||
);
|
||||
RawWaker::new(data, vtable)
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> From<Notify<'a, U>> for NotifyHandle {
|
||||
fn from(handle: Notify<'a, U>) -> NotifyHandle {
|
||||
unsafe {
|
||||
let ptr = handle.0.clone();
|
||||
let ptr = mem::transmute::<Arc<Node<U>>, *mut ArcNode<U>>(ptr);
|
||||
NotifyHandle::new(hide_lt(ptr))
|
||||
}
|
||||
}
|
||||
unsafe fn wake_inner<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
arc.unpark.unpark();
|
||||
}
|
||||
|
||||
struct ArcNode<U>(PhantomData<U>);
|
||||
|
||||
// We should never touch `Task` on any thread other than the one owning
|
||||
// `Scheduler`, so this should be a safe operation.
|
||||
unsafe impl<U: Sync + Send> Send for ArcNode<U> {}
|
||||
unsafe impl<U: Sync + Send> Sync for ArcNode<U> {}
|
||||
|
||||
impl<U: Unpark> executor::Notify for ArcNode<U> {
|
||||
fn notify(&self, _id: usize) {
|
||||
unsafe {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = me as *const Arc<Node<U>>;
|
||||
Node::notify(&*me)
|
||||
}
|
||||
}
|
||||
unsafe fn wake_by_ref_inner<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Inner<U>> = Arc::from_raw(data as *const Inner<U>);
|
||||
arc.unpark.unpark();
|
||||
// by_ref means we don't own the Node, so forget the Arc
|
||||
mem::forget(arc);
|
||||
}
|
||||
|
||||
unsafe impl<U: Unpark> UnsafeNotify for ArcNode<U> {
|
||||
unsafe fn clone_raw(&self) -> NotifyHandle {
|
||||
let me: *const ArcNode<U> = self;
|
||||
let me: *const *const ArcNode<U> = &me;
|
||||
let me = &*(me as *const Arc<Node<U>>);
|
||||
Notify(me).into()
|
||||
}
|
||||
unsafe fn drop_inner<U>(data: *const ()) {
|
||||
drop(Arc::<Inner<U>>::from_raw(data as *const Inner<U>));
|
||||
}
|
||||
// ===== Raw Waker Node<U> ======
|
||||
|
||||
unsafe fn drop_raw(&self) {
|
||||
let mut me: *const ArcNode<U> = self;
|
||||
let me = &mut me as *mut *const ArcNode<U> as *mut Arc<Node<U>>;
|
||||
ptr::drop_in_place(me);
|
||||
}
|
||||
unsafe fn waker_ref<U: Unpark>(node: &Arc<Node<U>>) -> Waker {
|
||||
let ptr = &*node as &Node<U> as *const Node<U> as *const ();
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_node::<U>,
|
||||
wake_unreachable,
|
||||
wake_by_ref_node::<U>,
|
||||
noop,
|
||||
);
|
||||
|
||||
Waker::from_raw(RawWaker::new(ptr, vtable))
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<U: Unpark>(p: *mut ArcNode<U>) -> *mut dyn UnsafeNotify {
|
||||
mem::transmute(p as *mut dyn UnsafeNotify)
|
||||
unsafe fn wake_unreachable(_data: *const ()) {
|
||||
unreachable!("waker_ref::wake()");
|
||||
}
|
||||
|
||||
unsafe fn clone_node<U: Unpark>(data: *const ()) -> RawWaker {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
let clone = arc.clone();
|
||||
// forget both Arcs so the refcounts don't get decremented
|
||||
mem::forget(arc);
|
||||
mem::forget(clone);
|
||||
|
||||
let vtable = &RawWakerVTable::new(
|
||||
clone_node::<U>,
|
||||
wake_node::<U>,
|
||||
wake_by_ref_node::<U>,
|
||||
drop_node::<U>,
|
||||
);
|
||||
RawWaker::new(data, vtable)
|
||||
}
|
||||
|
||||
unsafe fn wake_node<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
Node::<U>::notify(&arc);
|
||||
}
|
||||
|
||||
unsafe fn wake_by_ref_node<U: Unpark>(data: *const ()) {
|
||||
let arc: Arc<Node<U>> = Arc::from_raw(data as *const Node<U>);
|
||||
Node::<U>::notify(&arc);
|
||||
// by_ref means we don't own the Node, so forget the Arc
|
||||
mem::forget(arc);
|
||||
}
|
||||
|
||||
unsafe fn drop_node<U>(data: *const ()) {
|
||||
drop(Arc::<Node<U>>::from_raw(data as *const Node<U>));
|
||||
}
|
||||
|
||||
impl<U: Unpark> Node<U> {
|
||||
|
||||
@@ -1,39 +1,34 @@
|
||||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![feature(async_await)]
|
||||
|
||||
use futures::future::{self, lazy};
|
||||
// This is not actually unused --- we need this trait to be in scope for
|
||||
// the tests that sue TaskExecutor::current().execute(). The compiler
|
||||
// doesn't realise that.
|
||||
#[allow(unused_imports)]
|
||||
use futures::future::Executor;
|
||||
use futures::prelude::*;
|
||||
use futures::sync::oneshot;
|
||||
use futures::task;
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio_current_thread::{block_on_all, CurrentThread};
|
||||
use tokio_executor::TypedExecutor;
|
||||
use tokio_sync::oneshot;
|
||||
|
||||
mod from_block_on_all {
|
||||
use super::*;
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
let msg = tokio_current_thread::block_on_all(lazy(move || {
|
||||
let msg = tokio_current_thread::block_on_all(async move {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
spawn(Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
}));
|
||||
|
||||
Ok::<_, ()>("hello")
|
||||
}))
|
||||
.unwrap();
|
||||
"hello"
|
||||
});
|
||||
|
||||
assert_eq!(2, cnt.get());
|
||||
assert_eq!(msg, "hello");
|
||||
@@ -48,7 +43,7 @@ mod from_block_on_all {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
@@ -66,11 +61,10 @@ fn block_waits() {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
block_on_all(rx.then(move |_| {
|
||||
block_on_all(async move {
|
||||
rx.await.unwrap();
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(1, cnt2.get());
|
||||
}
|
||||
@@ -84,10 +78,9 @@ fn spawn_many() {
|
||||
|
||||
for _ in 0..ITER {
|
||||
let cnt = cnt.clone();
|
||||
tokio_current_thread.spawn(lazy(move || {
|
||||
tokio_current_thread.spawn(async move {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok::<(), ()>(())
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
tokio_current_thread.run().unwrap();
|
||||
@@ -98,48 +91,36 @@ fn spawn_many() {
|
||||
mod does_not_set_global_executor_by_default {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()> + Send>) -> Result<(), E> + 'static, E>(
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<(), E> + 'static, E>(
|
||||
spawn: F,
|
||||
) {
|
||||
block_on_all(lazy(|| {
|
||||
spawn(Box::new(lazy(|| ok()))).unwrap_err();
|
||||
ok()
|
||||
}))
|
||||
.unwrap()
|
||||
block_on_all(async {
|
||||
spawn(Box::pin(async {})).unwrap_err();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn() {
|
||||
use tokio_executor::Executor;
|
||||
test(|f| tokio_executor::DefaultExecutor::current().spawn(f))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
test(|f| tokio_executor::DefaultExecutor::current().execute(f))
|
||||
}
|
||||
}
|
||||
|
||||
mod from_block_on_future {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>)>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>)>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let cnt2 = cnt.clone();
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
tokio_current_thread
|
||||
.block_on(lazy(|| {
|
||||
let cnt = cnt.clone();
|
||||
tokio_current_thread.block_on(async move {
|
||||
let cnt3 = cnt2.clone();
|
||||
|
||||
spawn(Box::new(lazy(move || {
|
||||
cnt.set(1 + cnt.get());
|
||||
Ok(())
|
||||
})));
|
||||
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
spawn(Box::pin(async move {
|
||||
cnt3.set(1 + cnt3.get());
|
||||
}));
|
||||
});
|
||||
|
||||
tokio_current_thread.run().unwrap();
|
||||
|
||||
@@ -155,35 +136,30 @@ mod from_block_on_future {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Never(Rc<()>);
|
||||
|
||||
impl Future for Never {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
|
||||
mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
use super::*;
|
||||
|
||||
async fn never(_rc: Rc<()>) {
|
||||
loop {
|
||||
yield_once().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
dotspawn(&mut tokio_current_thread, Box::new(Never(rc.clone())));
|
||||
dotspawn(&mut tokio_current_thread, Box::pin(never(rc.clone())));
|
||||
|
||||
drop(tokio_current_thread);
|
||||
|
||||
@@ -193,15 +169,13 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
// Using the global spawn fn
|
||||
|
||||
let mut rc = Rc::new(());
|
||||
let rc2 = rc.clone();
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
tokio_current_thread
|
||||
.block_on(lazy(|| {
|
||||
spawn(Box::new(Never(rc.clone())));
|
||||
Ok::<_, ()>(())
|
||||
}))
|
||||
.unwrap();
|
||||
tokio_current_thread.block_on(async move {
|
||||
spawn(Box::pin(never(rc2)));
|
||||
});
|
||||
|
||||
drop(tokio_current_thread);
|
||||
|
||||
@@ -221,7 +195,7 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -238,12 +212,9 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn nesting_run() {
|
||||
block_on_all(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async {
|
||||
block_on_all(async {});
|
||||
});
|
||||
}
|
||||
|
||||
mod run_in_future {
|
||||
@@ -252,29 +223,23 @@ mod run_in_future {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn spawn() {
|
||||
block_on_all(lazy(|| {
|
||||
tokio_current_thread::spawn(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
ok()
|
||||
}));
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async {
|
||||
tokio_current_thread::spawn(async {
|
||||
block_on_all(async {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn execute() {
|
||||
block_on_all(lazy(|| {
|
||||
block_on_all(async {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(lazy(|| {
|
||||
block_on_all(lazy(|| ok())).unwrap();
|
||||
ok()
|
||||
}))
|
||||
.spawn(async {
|
||||
block_on_all(async {});
|
||||
})
|
||||
.unwrap();
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,23 +247,15 @@ mod run_in_future {
|
||||
fn tick_on_infini_future() {
|
||||
let num = Rc::new(Cell::new(0));
|
||||
|
||||
struct Infini {
|
||||
num: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Future for Infini {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
self.num.set(1 + self.num.get());
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
async fn infini(num: Rc<Cell<usize>>) {
|
||||
loop {
|
||||
num.set(1 + num.get());
|
||||
yield_once().await
|
||||
}
|
||||
}
|
||||
|
||||
CurrentThread::new()
|
||||
.spawn(Infini { num: num.clone() })
|
||||
.spawn(infini(num.clone()))
|
||||
.turn(None)
|
||||
.unwrap();
|
||||
|
||||
@@ -307,56 +264,41 @@ fn tick_on_infini_future() {
|
||||
|
||||
mod tasks_are_scheduled_fairly {
|
||||
use super::*;
|
||||
struct Spin {
|
||||
state: Rc<RefCell<[i32; 2]>>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Future for Spin {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
async fn spin(state: Rc<RefCell<[i32; 2]>>, idx: usize) {
|
||||
loop {
|
||||
// borrow_mut scope
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
if idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
|
||||
if self.idx == 0 {
|
||||
let diff = state[0] - state[1];
|
||||
assert!(diff.abs() <= 1);
|
||||
|
||||
assert!(diff.abs() <= 1);
|
||||
if state[0] >= 50 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if state[0] >= 50 {
|
||||
return Ok(().into());
|
||||
state[idx] += 1;
|
||||
|
||||
if state[idx] >= 100 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state[self.idx] += 1;
|
||||
|
||||
if state[self.idx] >= 100 {
|
||||
return Ok(().into());
|
||||
}
|
||||
|
||||
task::current().notify();
|
||||
Ok(Async::NotReady)
|
||||
yield_once().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn test<F: Fn(Spin)>(spawn: F) {
|
||||
fn test<F: Fn(Pin<Box<dyn Future<Output = ()>>>)>(spawn: F) {
|
||||
let state = Rc::new(RefCell::new([0, 0]));
|
||||
|
||||
block_on_all(lazy(|| {
|
||||
spawn(Spin {
|
||||
state: state.clone(),
|
||||
idx: 0,
|
||||
});
|
||||
|
||||
spawn(Spin {
|
||||
state: state,
|
||||
idx: 1,
|
||||
});
|
||||
|
||||
ok()
|
||||
}))
|
||||
.unwrap();
|
||||
block_on_all(async move {
|
||||
spawn(Box::pin(spin(state.clone(), 0)));
|
||||
spawn(Box::pin(spin(state, 1)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -368,7 +310,7 @@ mod tasks_are_scheduled_fairly {
|
||||
fn execute() {
|
||||
test(|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
@@ -379,8 +321,8 @@ mod and_turn {
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -388,24 +330,21 @@ mod and_turn {
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
// Spawn a basic task to get the executor to turn
|
||||
dotspawn(&mut tokio_current_thread, Box::new(lazy(move || Ok(()))));
|
||||
dotspawn(&mut tokio_current_thread, Box::pin(async {}));
|
||||
|
||||
// Turn once...
|
||||
tokio_current_thread.turn(None).unwrap();
|
||||
|
||||
dotspawn(
|
||||
&mut tokio_current_thread,
|
||||
Box::new(lazy(move || {
|
||||
Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
|
||||
// Spawn!
|
||||
spawn(Box::new(lazy(move || {
|
||||
spawn(Box::pin(async move {
|
||||
c.set(1 + c.get());
|
||||
Ok::<(), ()>(())
|
||||
})));
|
||||
|
||||
Ok(())
|
||||
})),
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
// This does not run the newly spawned thread
|
||||
@@ -429,7 +368,7 @@ mod and_turn {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -454,23 +393,12 @@ mod in_drop {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyFuture {
|
||||
_data: Box<dyn Any>,
|
||||
}
|
||||
|
||||
impl Future for MyFuture {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<(), ()> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
async fn noop(_data: Box<dyn Any>) {}
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
F: Fn(Pin<Box<dyn Future<Output = ()>>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Pin<Box<dyn Future<Output = ()>>>),
|
||||
{
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
@@ -478,14 +406,11 @@ mod in_drop {
|
||||
|
||||
dotspawn(
|
||||
&mut tokio_current_thread,
|
||||
Box::new(MyFuture {
|
||||
_data: Box::new(OnDrop(Some(move || {
|
||||
spawn(Box::new(lazy(move || {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
})));
|
||||
}))),
|
||||
}),
|
||||
Box::pin(noop(Box::new(OnDrop(Some(move || {
|
||||
spawn(Box::pin(async move {
|
||||
tx.send(()).unwrap();
|
||||
}));
|
||||
}))))),
|
||||
);
|
||||
|
||||
tokio_current_thread.block_on(rx).unwrap();
|
||||
@@ -504,7 +429,7 @@ mod in_drop {
|
||||
test(
|
||||
|f| {
|
||||
tokio_current_thread::TaskExecutor::current()
|
||||
.execute(f)
|
||||
.spawn(f)
|
||||
.unwrap();
|
||||
},
|
||||
// Note: `CurrentThread` doesn't currently implement
|
||||
@@ -519,6 +444,7 @@ mod in_drop {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn hammer_turn() {
|
||||
use futures::sync::mpsc;
|
||||
@@ -572,6 +498,7 @@ fn hammer_turn() {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn turn_has_polled() {
|
||||
@@ -579,7 +506,9 @@ fn turn_has_polled() {
|
||||
|
||||
// Spawn oneshot receiver
|
||||
let (sender, receiver) = oneshot::channel::<()>();
|
||||
tokio_current_thread.spawn(receiver.then(|_| Ok(())));
|
||||
tokio_current_thread.spawn(async move {
|
||||
let _ = receiver.await;
|
||||
});
|
||||
|
||||
// Turn once...
|
||||
let res = tokio_current_thread
|
||||
@@ -674,30 +603,30 @@ fn turn_fair() {
|
||||
|
||||
// Once an item is received on the oneshot channel, it will immediately
|
||||
// immediately make the second oneshot channel ready
|
||||
tokio_current_thread.spawn(receiver.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver.await.unwrap();
|
||||
sender_2.send(()).unwrap();
|
||||
receiver_1_done_clone.set(true);
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
let receiver_2_done = Rc::new(Cell::new(false));
|
||||
let receiver_2_done_clone = receiver_2_done.clone();
|
||||
|
||||
tokio_current_thread.spawn(receiver_2.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver_2.await.unwrap();
|
||||
receiver_2_done_clone.set(true);
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
// The third receiver is only woken up from our Park implementation, it simulates
|
||||
// e.g. a socket that first has to be polled to know if it is ready now
|
||||
let receiver_3_done = Rc::new(Cell::new(false));
|
||||
let receiver_3_done_clone = receiver_3_done.clone();
|
||||
|
||||
tokio_current_thread.spawn(receiver_3.map_err(|_| unreachable!()).and_then(move |_| {
|
||||
tokio_current_thread.spawn(async move {
|
||||
receiver_3.await.unwrap();
|
||||
receiver_3_done_clone.set(true);
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
|
||||
// First turn should've polled both and considered them not ready
|
||||
let res = tokio_current_thread
|
||||
@@ -760,10 +689,9 @@ fn spawn_from_other_thread() {
|
||||
|
||||
thread::spawn(move || {
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
sender.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
@@ -784,10 +712,9 @@ fn spawn_from_other_thread_unpark() {
|
||||
let _ = receiver_2.recv().unwrap();
|
||||
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
sender_1.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
@@ -796,15 +723,14 @@ fn spawn_from_other_thread_unpark() {
|
||||
// lazy future below which will cause the future to be spawned from
|
||||
// the other thread. Then the executor will park but should be woken
|
||||
// up because *now* we have a new future to schedule
|
||||
let _ = current_thread
|
||||
.block_on(
|
||||
lazy(move || {
|
||||
sender_2.send(()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.and_then(|_| receiver_1),
|
||||
)
|
||||
.unwrap();
|
||||
let _ = current_thread.block_on(async move {
|
||||
// inlined 'lazy'
|
||||
async move {
|
||||
sender_2.send(()).unwrap();
|
||||
}
|
||||
.await;
|
||||
receiver_1.await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -813,21 +739,34 @@ fn spawn_from_executor_with_handle() {
|
||||
let handle = current_thread.handle();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
current_thread.spawn(lazy(move || {
|
||||
current_thread.spawn(async move {
|
||||
handle
|
||||
.spawn(lazy(move || {
|
||||
.spawn(async move {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
}))
|
||||
})
|
||||
.unwrap();
|
||||
Ok::<_, ()>(())
|
||||
}));
|
||||
});
|
||||
|
||||
current_thread.run().unwrap();
|
||||
|
||||
rx.wait().unwrap();
|
||||
current_thread.block_on(rx).unwrap();
|
||||
}
|
||||
|
||||
fn ok() -> future::FutureResult<(), ()> {
|
||||
future::ok(())
|
||||
async fn yield_once() {
|
||||
YieldOnce(false).await
|
||||
}
|
||||
|
||||
struct YieldOnce(bool);
|
||||
|
||||
impl Future for YieldOnce {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
if self.0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.0 = true;
|
||||
// Push to the back of the executor's queue
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user