Add some missing future::Executor implementations (#563)

This adds an implementation of future::Executor for
`executor::DefaultExecutor` and `runtime::current_thread::Handle`.
This commit is contained in:
Carl Lerche
2018-09-17 22:23:48 -07:00
committed by GitHub
parent 24dc85dc5e
commit 4019198706
6 changed files with 622 additions and 255 deletions
+33 -1
View File
@@ -7,7 +7,7 @@ use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer};
use tokio_executor;
use futures::Future;
use futures::{future, Future};
use std::fmt;
use std::error::Error;
@@ -42,6 +42,38 @@ impl Handle {
where F: Future<Item = (), Error = ()> + Send + 'static {
self.0.spawn(future)
}
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
///
/// This function may return both false positives **and** false negatives.
/// If `status` returns `Ok`, then a call to `spawn` will *probably*
/// succeed, but may fail. If `status` returns `Err`, a call to `spawn` will
/// *probably* fail, but may succeed.
///
/// This allows a caller to avoid creating the task if the call to `spawn`
/// has a high likelihood of failing.
pub fn status(&self) -> Result<(), tokio_executor::SpawnError> {
self.0.status()
}
}
impl<T> future::Executor<T> for Handle
where T: Future<Item = (), Error = ()> + Send + 'static,
{
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
if let Err(e) = self.status() {
let kind = if e.is_at_capacity() {
future::ExecuteErrorKind::NoCapacity
} else {
future::ExecuteErrorKind::Shutdown
};
return Err(future::ExecuteError::new(kind, future));
}
let _ = self.spawn(future);
Ok(())
}
}
/// Error returned by the `run` function.
+229 -110
View File
@@ -11,6 +11,11 @@ use tokio::prelude::future::lazy;
use tokio::prelude::*;
use tokio::runtime::Runtime;
// this import is used in all child modules that have it in scope
// from importing super::*, but the compiler doesn't realise that
// and warns about it.
pub use futures::future::Executor;
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
@@ -72,78 +77,130 @@ fn runtime_single_threaded_block_on() {
tokio::runtime::current_thread::block_on_all(create_client_server_future()).unwrap();
}
#[test]
fn runtime_single_threaded_block_on_all() {
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
mod runtime_single_threaded_block_on_all {
use super::*;
let msg = tokio::runtime::current_thread::block_on_all(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
fn test<F>(spawn: F)
where
F: Fn(Box<Future<Item=(), Error=()> + Send>),
{
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
// Spawn!
tokio::spawn(lazy(move || {
let msg = tokio::runtime::current_thread::block_on_all(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
}));
Ok::<_, ()>("hello")
})).unwrap();
// Spawn!
spawn(Box::new(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
})));
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
Ok::<_, ()>("hello")
})).unwrap();
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
}
#[test]
fn spawn() {
test(|f| { tokio::spawn(f); })
}
#[test]
fn execute() {
test(|f| {
tokio::executor::DefaultExecutor::current()
.execute(f)
.unwrap();
})
}
}
#[test]
fn runtime_single_threaded_racy_spawn() {
let (trigger, exit) = futures::sync::oneshot::channel();
let (handle_tx, handle_rx) = ::std::sync::mpsc::channel();
let jh = ::std::thread::spawn(move || {
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
handle_tx.send(rt.handle()).unwrap();
mod runtime_single_threaded_racy {
use super::*;
fn test<F>(spawn: F)
where
F: Fn(
tokio::runtime::current_thread::Handle,
Box<Future<Item=(), Error=()> + Send>,
),
{
let (trigger, exit) = futures::sync::oneshot::channel();
let (handle_tx, handle_rx) = ::std::sync::mpsc::channel();
let jh = ::std::thread::spawn(move || {
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
handle_tx.send(rt.handle()).unwrap();
// don't exit until we are told to
rt.block_on(exit.map_err(|_| ())).unwrap();
// don't exit until we are told to
rt.block_on(exit.map_err(|_| ())).unwrap();
// run until all spawned futures (incl. the "exit" signal future) have completed.
rt.run().unwrap();
});
// run until all spawned futures (incl. the "exit" signal future) have completed.
rt.run().unwrap();
});
let (tx, rx) = futures::sync::oneshot::channel();
let (tx, rx) = futures::sync::oneshot::channel();
let handle = handle_rx.recv().unwrap();
handle
.spawn(futures::future::lazy(move || {
let handle = handle_rx.recv().unwrap();
spawn(handle, Box::new(futures::future::lazy(move || {
tx.send(()).unwrap();
Ok(())
}))
.unwrap();
})));
// signal runtime thread to exit
trigger.send(()).unwrap();
// signal runtime thread to exit
trigger.send(()).unwrap();
// wait for runtime thread to exit
jh.join().unwrap();
// wait for runtime thread to exit
jh.join().unwrap();
assert_eq!(rx.wait().unwrap(), ());
assert_eq!(rx.wait().unwrap(), ());
}
#[test]
fn spawn() {
test(|handle, f| { handle.spawn(f).unwrap(); })
}
#[test]
fn execute() {
test(|handle, f| { handle.execute(f).unwrap(); })
}
}
#[test]
fn runtime_multi_threaded() {
let _ = env_logger::try_init();
mod runtime_multi_threaded {
use super::*;
fn test<F>(spawn: F)
where
F: Fn(&mut Runtime) + Send + 'static,
{
let _ = env_logger::try_init();
let mut runtime = tokio::runtime::Builder::new()
.build()
.unwrap();
runtime.spawn(create_client_server_future());
runtime.shutdown_on_idle().wait().unwrap();
let mut runtime = tokio::runtime::Builder::new()
.build()
.unwrap();
spawn(&mut runtime);
runtime.shutdown_on_idle().wait().unwrap();
}
#[test]
fn spawn() {
test(|rt| { rt.spawn(create_client_server_future()); });
}
#[test]
fn execute() {
test(|rt| { rt.executor().execute(create_client_server_future()).unwrap(); });
}
}
#[test]
fn block_on_timer() {
use std::time::{Duration, Instant};
@@ -161,35 +218,57 @@ fn block_on_timer() {
runtime.shutdown_on_idle().wait().unwrap();
}
#[test]
fn spawn_from_block_on() {
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
mod from_block_on {
use super::*;
let mut runtime = Runtime::new().unwrap();
let msg = runtime
.block_on(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
fn test<F>(spawn: F)
where
F: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
{
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
// Spawn!
tokio::spawn(lazy(move || {
let mut runtime = Runtime::new().unwrap();
let msg = runtime
.block_on(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
}));
Ok::<_, ()>("hello")
}))
.unwrap();
// Spawn!
spawn(Box::new(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
})));
runtime.shutdown_on_idle().wait().unwrap();
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
Ok::<_, ()>("hello")
}))
.unwrap();
runtime.shutdown_on_idle().wait().unwrap();
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
}
#[test]
fn execute() {
test(|f| {
tokio::executor::DefaultExecutor::current()
.execute(f)
.unwrap();
})
}
#[test]
fn spawn() {
test(|f| {
tokio::spawn(f);
})
}
}
#[test]
@@ -220,54 +299,94 @@ fn block_waits() {
runtime.shutdown_on_idle().wait().unwrap();
}
#[test]
fn spawn_many() {
mod many {
use super::*;
const ITER: usize = 200;
fn test<F>(spawn: F)
where
F: Fn(&mut Runtime, Box<Future<Item=(), Error=()> + Send>),
{
let cnt = Arc::new(Mutex::new(0));
let mut runtime = Runtime::new().unwrap();
let cnt = Arc::new(Mutex::new(0));
let mut runtime = Runtime::new().unwrap();
for _ in 0..ITER {
let c = cnt.clone();
runtime.spawn(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
}));
}
runtime.shutdown_on_idle().wait().unwrap();
assert_eq!(ITER, *cnt.lock().unwrap());
}
#[test]
fn spawn_from_block_on_all() {
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
let runtime = Runtime::new().unwrap();
let msg = runtime
.block_on_all(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
// Spawn!
tokio::spawn(lazy(move || {
for _ in 0..ITER {
let c = cnt.clone();
spawn(&mut runtime, Box::new(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
}));
})));
}
Ok::<_, ()>("hello")
}))
.unwrap();
runtime.shutdown_on_idle().wait().unwrap();
assert_eq!(ITER, *cnt.lock().unwrap());
}
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
#[test]
fn spawn() {
test(|rt, f| { rt.spawn(f); })
}
#[test]
fn execute() {
test(|rt, f| {
rt.executor()
.execute(f)
.unwrap();
})
}
}
mod from_block_on_all {
use super::*;
fn test<F>(spawn: F)
where
F: Fn(Box<Future<Item=(), Error=()> + Send>) + Send + 'static,
{
let cnt = Arc::new(Mutex::new(0));
let c = cnt.clone();
let runtime = Runtime::new().unwrap();
let msg = runtime
.block_on_all(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
// Spawn!
spawn(Box::new(lazy(move || {
{
let mut x = c.lock().unwrap();
*x = 1 + *x;
}
Ok::<(), ()>(())
})));
Ok::<_, ()>("hello")
}))
.unwrap();
assert_eq!(2, *cnt.lock().unwrap());
assert_eq!(msg, "hello");
}
#[test]
fn execute() {
test(|f| {
tokio::executor::DefaultExecutor::current()
.execute(f)
.unwrap();
})
}
#[test]
fn spawn() {
test(|f| { tokio::spawn(f); })
}
}
+17
View File
@@ -682,6 +682,23 @@ impl Handle {
self.notify.notify(0);
Ok(())
}
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
///
/// This function may return both false positives **and** false negatives.
/// If `status` returns `Ok`, then a call to `spawn` will *probably*
/// succeed, but may fail. If `status` returns `Err`, a call to `spawn` will
/// *probably* fail, but may succeed.
///
/// This allows a caller to avoid creating the task if the call to `spawn`
/// has a high likelihood of failing.
pub fn status(&self) -> Result<(), SpawnError> {
if self.shut_down.get() {
return Err(SpawnError::shutdown());
}
Ok(())
}
}
// ===== impl TaskExecutor =====
+301 -138
View File
@@ -12,28 +12,49 @@ use std::time::Duration;
use futures::task;
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 as _futures_Executor;
use futures::prelude::*;
use futures::sync::oneshot;
#[test]
fn spawn_from_block_on_all() {
let cnt = Rc::new(Cell::new(0));
let c = cnt.clone();
mod from_block_on_all {
use super::*;
fn test<F: Fn(Box<Future<Item=(), Error=()>>) + 'static>(spawn: F) {
let cnt = Rc::new(Cell::new(0));
let c = cnt.clone();
let msg = tokio_current_thread::block_on_all(lazy(move || {
c.set(1 + c.get());
// Spawn!
tokio_current_thread::spawn(lazy(move || {
let msg = tokio_current_thread::block_on_all(lazy(move || {
c.set(1 + c.get());
Ok::<(), ()>(())
}));
Ok::<_, ()>("hello")
})).unwrap();
// Spawn!
spawn(Box::new(lazy(move || {
c.set(1 + c.get());
Ok::<(), ()>(())
})));
assert_eq!(2, cnt.get());
assert_eq!(msg, "hello");
Ok::<_, ()>("hello")
})).unwrap();
assert_eq!(2, cnt.get());
assert_eq!(msg, "hello");
}
#[test]
fn spawn() {
test(tokio_current_thread::spawn)
}
#[test]
fn execute() {
test(|f| {
tokio_current_thread::TaskExecutor::current()
.execute(f)
.unwrap();
});
}
}
#[test]
@@ -76,39 +97,61 @@ fn spawn_many() {
assert_eq!(cnt.get(), ITER);
}
#[test]
fn does_not_set_global_executor_by_default() {
use tokio_executor::Executor;
mod does_not_set_global_executor_by_default {
use super::*;
block_on_all(lazy(|| {
tokio_executor::DefaultExecutor::current()
.spawn(Box::new(lazy(|| ok())))
.unwrap_err();
fn test<F: Fn(Box<Future<Item=(), Error=()> + Send>) -> Result<(), E> + 'static, E>(spawn: F) {
block_on_all(lazy(|| {
spawn(Box::new(lazy(|| ok()))).unwrap_err();
ok()
})).unwrap()
}
ok()
})).unwrap();
#[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))
}
}
#[test]
fn spawn_from_block_on_future() {
let cnt = Rc::new(Cell::new(0));
mod from_block_on_future {
use super::*;
let mut tokio_current_thread = CurrentThread::new();
fn test<F: Fn(Box<Future<Item = (), Error = ()>>)>(spawn: F) {
let cnt = Rc::new(Cell::new(0));
tokio_current_thread.block_on(lazy(|| {
let cnt = cnt.clone();
let mut tokio_current_thread = CurrentThread::new();
tokio_current_thread::spawn(lazy(move || {
cnt.set(1 + cnt.get());
Ok(())
}));
tokio_current_thread.block_on(lazy(|| {
let cnt = cnt.clone();
Ok::<_, ()>(())
})).unwrap();
spawn(Box::new(lazy(move || {
cnt.set(1 + cnt.get());
Ok(())
})));
tokio_current_thread.run().unwrap();
Ok::<_, ()>(())
})).unwrap();
assert_eq!(1, cnt.get());
tokio_current_thread.run().unwrap();
assert_eq!(1, cnt.get());
}
#[test]
fn spawn() {
test(tokio_current_thread::spawn);
}
#[test]
fn execute() {
test(|f| { tokio_current_thread::TaskExecutor::current().execute(f).unwrap(); });
}
}
struct Never(Rc<()>);
@@ -122,33 +165,60 @@ impl Future for Never {
}
}
#[test]
fn outstanding_tasks_are_dropped_when_executor_is_dropped() {
let mut rc = Rc::new(());
mod outstanding_tasks_are_dropped_when_executor_is_dropped {
use super::*;
let mut tokio_current_thread = CurrentThread::new();
tokio_current_thread.spawn(Never(rc.clone()));
fn test<F, G>(spawn: F, dotspawn: G)
where
F: Fn(Box<Future<Item=(), Error=()>>) + 'static,
G: Fn(&mut CurrentThread, Box<Future<Item=(), Error=()>>)
{
let mut rc = Rc::new(());
drop(tokio_current_thread);
let mut tokio_current_thread = CurrentThread::new();
dotspawn(&mut tokio_current_thread, Box::new(Never(rc.clone())));
// Ensure the daemon is dropped
assert!(Rc::get_mut(&mut rc).is_some());
drop(tokio_current_thread);
// Using the global spawn fn
// Ensure the daemon is dropped
assert!(Rc::get_mut(&mut rc).is_some());
let mut rc = Rc::new(());
// Using the global spawn fn
let mut tokio_current_thread = CurrentThread::new();
let mut rc = Rc::new(());
tokio_current_thread.block_on(lazy(|| {
tokio_current_thread::spawn(Never(rc.clone()));
Ok::<_, ()>(())
})).unwrap();
let mut tokio_current_thread = CurrentThread::new();
drop(tokio_current_thread);
tokio_current_thread.block_on(lazy(|| {
spawn(Box::new(Never(rc.clone())));
Ok::<_, ()>(())
})).unwrap();
// Ensure the daemon is dropped
assert!(Rc::get_mut(&mut rc).is_some());
drop(tokio_current_thread);
// Ensure the daemon is dropped
assert!(Rc::get_mut(&mut rc).is_some());
}
#[test]
fn spawn() {
test(tokio_current_thread::spawn, |rt, f| { rt.spawn(f); })
}
#[test]
fn execute() {
test(|f| {
tokio_current_thread::TaskExecutor::current()
.execute(f)
.unwrap();
},
// Note: `CurrentThread` doesn't currently implement
// `futures::Executor`, so we'll call `.spawn(...)` rather than
// `.execute(...)` for now. If `CurrentThread` is changed to
// implement Executor, change this to `.execute(...).unwrap()`.
|rt, f| { rt.spawn(f); }
);
}
}
#[test]
@@ -163,20 +233,41 @@ fn nesting_run() {
})).unwrap();
}
#[test]
#[should_panic]
fn run_in_future() {
block_on_all(lazy(|| {
tokio_current_thread::spawn(lazy(|| {
block_on_all(lazy(|| {
mod run_in_future {
use super::*;
#[test]
#[should_panic]
fn spawn() {
block_on_all(lazy(|| {
tokio_current_thread::spawn(lazy(|| {
block_on_all(lazy(|| {
ok()
})).unwrap();
ok()
})).unwrap();
}));
ok()
}));
ok()
})).unwrap();
})).unwrap();
}
#[test]
#[should_panic]
fn execute() {
block_on_all(lazy(|| {
tokio_current_thread::TaskExecutor::current()
.execute(lazy(|| {
block_on_all(lazy(|| {
ok()
})).unwrap();
ok()
}))
.unwrap();
ok()
})).unwrap();
}
}
#[test]
fn tick_on_infini_future() {
let num = Rc::new(Cell::new(0));
@@ -206,10 +297,8 @@ fn tick_on_infini_future() {
assert_eq!(1, num.get());
}
#[test]
fn tasks_are_scheduled_fairly() {
let state = Rc::new(RefCell::new([0, 0]));
mod tasks_are_scheduled_fairly {
use super::*;
struct Spin {
state: Rc<RefCell<[i32; 2]>>,
idx: usize,
@@ -243,97 +332,171 @@ fn tasks_are_scheduled_fairly() {
}
}
block_on_all(lazy(|| {
tokio_current_thread::spawn(Spin {
state: state.clone(),
idx: 0,
});
fn test<F: Fn(Spin)>(spawn: F) {
let state = Rc::new(RefCell::new([0, 0]));
tokio_current_thread::spawn(Spin {
state: state,
idx: 1,
});
block_on_all(lazy(|| {
spawn(Spin {
state: state.clone(),
idx: 0,
});
ok()
})).unwrap();
spawn(Spin {
state: state,
idx: 1,
});
ok()
})).unwrap();
}
#[test]
fn spawn() {
test(tokio_current_thread::spawn)
}
#[test]
fn execute() {
test(|f| {
tokio_current_thread::TaskExecutor::current()
.execute(f)
.unwrap();
})
}
}
#[test]
fn spawn_and_turn() {
let cnt = Rc::new(Cell::new(0));
let c = cnt.clone();
mod and_turn {
use super::*;
let mut tokio_current_thread = CurrentThread::new();
fn test<F, G>(spawn: F, dotspawn: G)
where
F: Fn(Box<Future<Item=(), Error=()>>) + 'static,
G: Fn(&mut CurrentThread, Box<Future<Item=(), Error=()>>)
{
let cnt = Rc::new(Cell::new(0));
let c = cnt.clone();
// Spawn a basic task to get the executor to turn
tokio_current_thread.spawn(lazy(move || {
Ok(())
}));
let mut tokio_current_thread = CurrentThread::new();
// Turn once...
tokio_current_thread.turn(None).unwrap();
// Spawn a basic task to get the executor to turn
dotspawn(&mut tokio_current_thread, Box::new(lazy(move || {
Ok(())
})));
tokio_current_thread.spawn(lazy(move || {
c.set(1 + c.get());
// Turn once...
tokio_current_thread.turn(None).unwrap();
// Spawn!
tokio_current_thread::spawn(lazy(move || {
dotspawn(&mut tokio_current_thread, Box::new(lazy(move || {
c.set(1 + c.get());
Ok::<(), ()>(())
}));
Ok(())
}));
// Spawn!
spawn(Box::new(lazy(move || {
c.set(1 + c.get());
Ok::<(), ()>(())
})));
Ok(())
})));
// This does not run the newly spawned thread
tokio_current_thread.turn(None).unwrap();
assert_eq!(1, cnt.get());
// This runs the newly spawned thread
tokio_current_thread.turn(None).unwrap();
assert_eq!(2, cnt.get());
}
#[test]
fn spawn() {
test(tokio_current_thread::spawn, |rt, f| { rt.spawn(f); })
}
#[test]
fn execute() {
test(|f| {
tokio_current_thread::TaskExecutor::current()
.execute(f)
.unwrap();
},
// Note: `CurrentThread` doesn't currently implement
// `futures::Executor`, so we'll call `.spawn(...)` rather than
// `.execute(...)` for now. If `CurrentThread` is changed to
// implement Executor, change this to `.execute(...).unwrap()`.
|rt, f| { rt.spawn(f); }
);
}
// This does not run the newly spawned thread
tokio_current_thread.turn(None).unwrap();
assert_eq!(1, cnt.get());
// This runs the newly spawned thread
tokio_current_thread.turn(None).unwrap();
assert_eq!(2, cnt.get());
}
#[test]
fn spawn_in_drop() {
mod in_drop {
use super::*;
struct OnDrop<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
(self.0.take().unwrap())();
}
}
struct MyFuture {
_data: Box<Any>,
}
impl Future for MyFuture {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
Ok(().into())
}
}
fn test<F, G>(spawn: F, dotspawn: G)
where
F: Fn(Box<Future<Item=(), Error=()>>) + 'static,
G: Fn(&mut CurrentThread, Box<Future<Item=(), Error=()>>)
{
let mut tokio_current_thread = CurrentThread::new();
let (tx, rx) = oneshot::channel();
let (tx, rx) = oneshot::channel();
tokio_current_thread.spawn({
struct OnDrop<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
(self.0.take().unwrap())();
dotspawn(&mut tokio_current_thread, Box::new(
MyFuture {
_data: Box::new(OnDrop(Some(move || {
spawn(Box::new(lazy(move || {
tx.send(()).unwrap();
Ok(())
})));
}))),
}
}
));
struct MyFuture {
_data: Box<Any>,
}
tokio_current_thread.block_on(rx).unwrap();
tokio_current_thread.run().unwrap();
}
impl Future for MyFuture {
type Item = ();
type Error = ();
#[test]
fn spawn() {
test(tokio_current_thread::spawn, |rt, f| { rt.spawn(f); })
}
fn poll(&mut self) -> Poll<(), ()> {
Ok(().into())
}
}
#[test]
fn execute() {
test(|f| {
tokio_current_thread::TaskExecutor::current()
.execute(f)
.unwrap();
},
// Note: `CurrentThread` doesn't currently implement
// `futures::Executor`, so we'll call `.spawn(...)` rather than
// `.execute(...)` for now. If `CurrentThread` is changed to
// implement Executor, change this to `.execute(...).unwrap()`.
|rt, f| { rt.spawn(f); }
);
}
MyFuture {
_data: Box::new(OnDrop(Some(move || {
tokio_current_thread::spawn(lazy(move || {
tx.send(()).unwrap();
Ok(())
}));
}))),
}
});
tokio_current_thread.block_on(rx).unwrap();
tokio_current_thread.run().unwrap();
}
#[test]
+20 -1
View File
@@ -1,6 +1,6 @@
use super::{Executor, Enter, SpawnError};
use futures::Future;
use futures::{future, Future};
use std::cell::Cell;
@@ -83,6 +83,25 @@ impl super::Executor for DefaultExecutor {
}
}
impl<T> future::Executor<T> for DefaultExecutor
where T: Future<Item = (), Error = ()> + Send + 'static,
{
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
if let Err(e) = super::Executor::status(self) {
let kind = if e.is_at_capacity() {
future::ExecuteErrorKind::NoCapacity
} else {
future::ExecuteErrorKind::Shutdown
};
return Err(future::ExecuteError::new(kind, future));
}
let _ = DefaultExecutor::with_current(|executor| executor.spawn(Box::new(future)));
Ok(())
}
}
// ===== global spawn fns =====
/// Submits a future for execution on the default executor -- usually a
+22 -5
View File
@@ -2,10 +2,27 @@ extern crate tokio_executor;
extern crate futures;
use tokio_executor::*;
use futures::future::lazy;
use futures::{Future, future::lazy};
#[test]
fn spawn_out_of_executor_context() {
let res = DefaultExecutor::current().spawn(Box::new(lazy(|| Ok(()))));
assert!(res.is_err());
mod out_of_executor_context {
use super::*;
fn test<F, E>(spawn: F)
where
F: Fn(Box<Future<Item=(), Error=()> + Send>) -> Result<(), E>,
{
let res = spawn(Box::new(lazy(|| Ok(()))));
assert!(res.is_err());
}
#[test]
fn spawn() {
test(|f| DefaultExecutor::current().spawn(f));
}
#[test]
fn execute() {
use futures::future::Executor as FuturesExecutor;
test(|f| DefaultExecutor::current().execute(f));
}
}