executor: remove Executor & TypedExecutor traits (#1724)

The `Executor` trait is sub-optimal as it forces a `Box<dyn Future>` to
spawn. Instead, `tokio::spawn` delegates to the specific runtime
implementation set for the current execution context.

`TypedExecutor`, while useful, has seen limited adoption. As such, it is
removed from `tokio` proper. Moving it to `tokio-util` is a possibility
that can be explored as follow up work.
This commit is contained in:
Carl Lerche
2019-11-01 13:50:17 -07:00
committed by GitHub
parent d70c928d88
commit 3e7d0be51d
14 changed files with 26 additions and 662 deletions
-17
View File
@@ -1,6 +1,5 @@
use crate::executor::park::{Park, Unpark};
use crate::executor::task::{self, JoinHandle, Schedule, Task};
use crate::executor::Executor;
use std::cell::UnsafeCell;
use std::collections::VecDeque;
@@ -294,22 +293,6 @@ impl Schedule for Scheduler {
}
}
impl Executor for &Scheduler {
fn spawn(
&mut self,
future: std::pin::Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), crate::executor::SpawnError> {
// Safety: This implementation should only be called by `global.rs` from
// the thread local.
//
// TODO: Delete this implementation.
unsafe {
Scheduler::spawn_background(self, future);
}
Ok(())
}
}
impl<P> Drop for CurrentThread<P>
where
P: Park,
-49
View File
@@ -1,49 +0,0 @@
use std::error::Error;
use std::fmt;
/// Errors returned by `Executor::spawn`.
///
/// Spawn errors should represent relatively rare scenarios. Currently, the two
/// scenarios represented by `SpawnError` are:
///
/// * An executor being at capacity or full. As such, the executor is not able
/// to accept a new future. This error state is expected to be transient.
/// * An executor has been shutdown and can no longer accept new futures. This
/// error state is expected to be permanent.
#[derive(Debug)]
pub struct SpawnError {
is_shutdown: bool,
}
impl SpawnError {
/// Return a new `SpawnError` reflecting a shutdown executor failure.
pub fn shutdown() -> Self {
SpawnError { is_shutdown: true }
}
/// Return a new `SpawnError` reflecting an executor at capacity failure.
pub fn at_capacity() -> Self {
SpawnError { is_shutdown: false }
}
/// Returns `true` if the error reflects a shutdown executor failure.
pub fn is_shutdown(&self) -> bool {
self.is_shutdown
}
/// Returns `true` if the error reflects an executor at capacity failure.
pub fn is_at_capacity(&self) -> bool {
!self.is_shutdown
}
}
impl fmt::Display for SpawnError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"attempted to spawn task while the executor is at capacity or shut down"
)
}
}
impl Error for SpawnError {}
-181
View File
@@ -1,181 +0,0 @@
use crate::executor::SpawnError;
use futures_util::future::{FutureExt, RemoteHandle};
use std::future::Future;
use std::pin::Pin;
/// A value that executes futures.
///
/// The [`spawn`] function is used to submit a future to an executor. Once
/// submitted, the executor takes ownership of the future and becomes
/// responsible for driving the future to completion.
///
/// The strategy employed by the executor to handle the future is less defined
/// and is left up to the `Executor` implementation. The `Executor` instance is
/// expected to call [`poll`] on the future once it has been notified, however
/// the "when" and "how" can vary greatly.
///
/// For example, the executor might be a thread pool, in which case a set of
/// threads have already been spawned up and the future is inserted into a
/// queue. A thread will acquire the future and poll it.
///
/// The `Executor` trait is only for futures that **are** `Send`. These are most
/// common. There currently is no trait that describes executors that operate
/// entirely on the current thread (i.e., are able to spawn futures that are not
/// `Send`). Note that single threaded executors can still implement `Executor`,
/// but only futures that are `Send` can be spawned via the trait.
///
/// This trait is primarily intended to implemented by executors and used to
/// back `tokio::spawn`. Libraries and applications **may** use this trait to
/// bound generics, but doing so will limit usage to futures that implement
/// `Send`. Instead, libraries and applications are recommended to use
/// [`TypedExecutor`] as a bound.
///
/// # Errors
///
/// The [`spawn`] function returns `Result` with an error type of `SpawnError`.
/// This error type represents the reason that the executor was unable to spawn
/// the future. The two current represented scenarios are:
///
/// * An executor being at capacity or full. As such, the executor is not able
/// to accept a new future. This error state is expected to be transient.
/// * An executor has been shutdown and can no longer accept new futures. This
/// error state is expected to be permanent.
///
/// If a caller encounters an at capacity error, the caller should try to shed
/// load. This can be as simple as dropping the future that was spawned.
///
/// If the caller encounters a shutdown error, the caller should attempt to
/// gracefully shutdown.
///
/// # Examples
///
/// ```
/// use tokio::executor::Executor;
///
/// # fn docs(my_executor: &mut dyn Executor) {
/// my_executor.spawn(Box::pin(async {
/// println!("running on the executor");
/// })).unwrap();
/// # }
/// ```
///
/// [`spawn`]: #tymethod.spawn
/// [`poll`]: https://doc.rust-lang.org/std/future/trait.Future.html#tymethod.poll
/// [`TypedExecutor`]: ../trait.TypedExecutor.html
pub trait Executor {
/// Spawns a future object to run on this executor.
///
/// `future` is passed to the executor, which will begin running it. The
/// future may run on the current thread or another thread at the discretion
/// of the `Executor` implementation.
///
/// # Panics
///
/// Implementations are encouraged to avoid panics. However, panics are
/// permitted and the caller should check the implementation specific
/// documentation for more details on possible panics.
///
/// # Examples
///
/// ```
/// use tokio::executor::Executor;
///
/// # fn docs(my_executor: &mut dyn Executor) {
/// my_executor.spawn(Box::pin(async {
/// println!("running on the executor");
/// })).unwrap();
/// # }
/// ```
fn spawn(&mut self, future: Pin<Box<dyn Future<Output = ()> + Send>>)
-> Result<(), SpawnError>;
/// 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.
///
/// # Panics
///
/// This function must not panic. Implementers must ensure that panics do
/// not happen.
///
/// # Examples
///
/// ```
/// use tokio::executor::Executor;
///
/// # fn docs(my_executor: &mut dyn Executor) {
/// if my_executor.status().is_ok() {
/// my_executor.spawn(Box::pin(async {
/// println!("running on the executor");
/// })).unwrap();
/// } else {
/// println!("the executor is not in a good state");
/// }
/// # }
/// ```
fn status(&self) -> Result<(), SpawnError> {
Ok(())
}
}
impl dyn Executor {
/// Spawns a future object to run on this executor, returning a result of
/// its `RemoteHandle`.
///
/// `future` is passed to the executor, which will begin running it. The
/// future may run on the current thread or another thread at the discretion
/// of the `Executor` implementation.
///
/// # Panics
///
/// Implementations are encouraged to avoid panics. However, panics are
/// permitted and the caller should check the implementation specific
/// documentation for more details on possible panics.
///
/// # Examples
///
/// ```
/// use tokio::executor::Executor;
/// use futures_util::future::FutureExt;
///
/// # fn docs(my_executor: &'static mut (dyn Executor + 'static)) {
/// let handle = my_executor.spawn_with_handle(Box::pin(async {
/// println!("running on the executor");
/// })).unwrap();
///
/// let handle = handle.map(|_| println!("the future has completed"));
/// # }
/// ```
pub fn spawn_with_handle<Fut>(
&mut self,
future: Fut,
) -> Result<RemoteHandle<Fut::Output>, SpawnError>
where
Fut: Future + Send + 'static,
Fut::Output: Send,
{
let (future, handle) = future.remote_handle();
self.spawn(Box::pin(future))?;
Ok(handle)
}
}
impl<E: Executor + ?Sized> Executor for Box<E> {
fn spawn(
&mut self,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), SpawnError> {
(**self).spawn(future)
}
fn status(&self) -> Result<(), SpawnError> {
(**self).status()
}
}
+11 -133
View File
@@ -2,66 +2,10 @@
use crate::executor::current_thread;
#[cfg(feature = "rt-full")]
use crate::executor::thread_pool::ThreadPool;
use crate::executor::{Executor, SpawnError};
use crate::executor::thread_pool;
use std::cell::Cell;
use std::future::Future;
use std::pin::Pin;
/// Executes futures on the default executor for the current execution context.
///
/// `DefaultExecutor` implements `Executor` and can be used to spawn futures
/// without referencing a specific executor.
///
/// When an executor starts, it sets the `DefaultExecutor` handle to point to an
/// executor (usually itself) that is used to spawn new tasks.
///
/// The current `DefaultExecutor` reference is tracked using a thread-local
/// variable and is set using `tokio::executor::with_default`
#[derive(Debug, Clone)]
pub struct DefaultExecutor {
_dummy: (),
}
impl DefaultExecutor {
/// Returns a handle to the default executor for the current context.
///
/// Futures may be spawned onto the default executor using this handle.
///
/// The returned handle will reference whichever executor is configured as
/// the default **at the time `spawn` is called**. This enables
/// `DefaultExecutor::current()` to be called before an execution context is
/// setup, then passed **into** an execution context before it is used.
///
/// This is also true for sending the handle across threads, so calling
/// `DefaultExecutor::current()` on thread A and then sending the result to
/// thread B will _not_ reference the default executor that was set on thread A.
pub fn current() -> DefaultExecutor {
DefaultExecutor { _dummy: () }
}
#[inline]
fn with_current<F: FnOnce(&mut dyn Executor) -> R, R>(f: F) -> Option<R> {
EXECUTOR.with(|current_executor| match current_executor.get() {
State::Ready(executor_ptr) => {
let executor = unsafe { &mut *executor_ptr };
Some(f(executor))
}
#[cfg(feature = "rt-full")]
State::ThreadPool(threadpool_ptr) => {
let mut thread_pool = unsafe { &*threadpool_ptr };
Some(f(&mut thread_pool))
}
#[cfg(feature = "rt-current-thread")]
State::CurrentThread(current_thread_ptr) => {
let mut current_thread = unsafe { &*current_thread_ptr };
Some(f(&mut current_thread))
}
State::Empty => None,
})
}
}
#[derive(Clone, Copy)]
enum State {
@@ -70,14 +14,11 @@ enum State {
// default executor is a thread pool instance.
#[cfg(feature = "rt-full")]
ThreadPool(*const ThreadPool),
ThreadPool(*const thread_pool::Spawner),
// Current-thread executor
#[cfg(feature = "rt-current-thread")]
CurrentThread(*const current_thread::Scheduler),
// default executor is set to a custom executor.
Ready(*mut dyn Executor),
}
thread_local! {
@@ -85,36 +26,6 @@ thread_local! {
static EXECUTOR: Cell<State> = Cell::new(State::Empty)
}
// ===== impl DefaultExecutor =====
impl super::Executor for DefaultExecutor {
fn spawn(
&mut self,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), SpawnError> {
DefaultExecutor::with_current(|executor| executor.spawn(future))
.unwrap_or_else(|| Err(SpawnError::shutdown()))
}
fn status(&self) -> Result<(), SpawnError> {
DefaultExecutor::with_current(|executor| executor.status())
.unwrap_or_else(|| Err(SpawnError::shutdown()))
}
}
impl<T> super::TypedExecutor<T> for DefaultExecutor
where
T: Future<Output = ()> + Send + 'static,
{
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
super::Executor::spawn(self, Box::pin(future))
}
fn status(&self) -> Result<(), SpawnError> {
super::Executor::status(self)
}
}
// ===== global spawn fns =====
/// Spawns a future on the default executor.
@@ -163,10 +74,6 @@ where
T: Future<Output = ()> + Send + 'static,
{
EXECUTOR.with(|current_executor| match current_executor.get() {
State::Ready(executor_ptr) => {
let executor = unsafe { &mut *executor_ptr };
executor.spawn(Box::pin(future)).unwrap();
}
#[cfg(feature = "rt-full")]
State::ThreadPool(threadpool_ptr) => {
let thread_pool = unsafe { &*threadpool_ptr };
@@ -182,7 +89,12 @@ where
current_thread.spawn_background(future);
}
}
State::Empty => panic!("must be called from the context of Tokio runtime"),
State::Empty => {
// Explicit drop of `future` silences the warning that `future` is
// not used when neither rt-* feature flags are enabled.
drop(future);
panic!("must be called from the context of Tokio runtime");
}
})
}
@@ -206,33 +118,14 @@ pub(super) fn current_thread_is_current(current_thread: &current_thread::Schedul
}
#[cfg(feature = "rt-full")]
pub(super) fn with_threadpool<F, R>(thread_pool: &ThreadPool, f: F) -> R
pub(super) fn with_thread_pool<F, R>(thread_pool: &thread_pool::Spawner, f: F) -> R
where
F: FnOnce() -> R,
{
with_state(State::ThreadPool(thread_pool as *const ThreadPool), f)
}
/// Set the default executor for the duration of the closure
///
/// If a default executor is already set, it will be restored when the closure returns or if it
/// panics.
pub fn with_default<T, F, R>(executor: &mut T, f: F) -> R
where
T: Executor,
F: FnOnce() -> R,
{
// While scary, this is safe. The function takes a
// `&mut Executor`, which guarantees that the reference lives for the
// duration of `with_default`.
//
// Because we are always clearing the TLS value at the end of the
// function, we can cast the reference to 'static which thread-local
// cells require.
let executor = unsafe { hide_lt(executor as &mut _ as *mut _) };
with_state(State::Ready(executor), f)
with_state(State::ThreadPool(thread_pool as *const _), f)
}
#[cfg(feature = "rt-current-thread")]
fn with_state<F, R>(state: State, f: F) -> R
where
F: FnOnce() -> R,
@@ -252,23 +145,8 @@ where
let _reset = Reset(cell, was);
if let State::Ready(executor) = state {
let executor = unsafe { &mut *executor };
if executor.status().is_err() {
panic!("executor not active; is this because `with_default` is called with `DefaultExecutor`?");
}
}
cell.set(state);
f()
})
}
unsafe fn hide_lt<'a>(p: *mut (dyn Executor + 'a)) -> *mut (dyn Executor + 'static) {
use std::mem;
// false positive: https://github.com/rust-lang/rust-clippy/issues/2906
#[allow(clippy::transmute_ptr_to_ptr)]
mem::transmute(p)
}
+1 -11
View File
@@ -47,15 +47,8 @@ mod tests;
mod enter;
pub use self::enter::{enter, exit, Enter, EnterError};
mod error;
pub use self::error::SpawnError;
#[allow(clippy::module_inception)]
mod executor;
pub use self::executor::Executor;
mod global;
pub use self::global::{spawn, with_default, DefaultExecutor};
pub use self::global::spawn;
pub(crate) mod loom;
@@ -66,9 +59,6 @@ mod task;
#[cfg(feature = "rt-current-thread")]
pub use self::task::{JoinError, JoinHandle};
mod typed;
pub use self::typed::TypedExecutor;
#[cfg(feature = "rt-full")]
mod util;
-8
View File
@@ -37,14 +37,6 @@ mod tests;
#[cfg(feature = "blocking")]
pub use worker::blocking;
// These exports are used in tests
#[cfg(test)]
#[allow(warnings)]
pub(crate) use self::worker::create_set as create_pool;
pub(crate) type BoxFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'static>>;
#[cfg(not(loom))]
const LOCAL_QUEUE_CAPACITY: usize = 256;
+1 -20
View File
@@ -1,7 +1,6 @@
use crate::executor::blocking::PoolWaiter;
use crate::executor::task::JoinHandle;
use crate::executor::thread_pool::{shutdown, Builder, Spawner};
use crate::executor::Executor;
use std::fmt;
use std::future::Future;
@@ -52,14 +51,6 @@ impl ThreadPool {
self.spawner.spawn(future)
}
/// Spawn a task in the background
pub(crate) fn spawn_background<F>(&self, future: F)
where
F: Future<Output = ()> + Send + 'static,
{
self.spawner.spawn_background(future);
}
/// Block the current thread waiting for the future to complete.
///
/// The future will execute on the current thread, but all spawned tasks
@@ -68,7 +59,7 @@ impl ThreadPool {
where
F: Future,
{
crate::executor::global::with_threadpool(self, || {
crate::executor::global::with_thread_pool(self.spawner(), || {
let mut enter =
crate::executor::enter().expect("attempting to block while on a Tokio executor");
crate::executor::blocking::with_pool(self.spawner.blocking_pool(), || {
@@ -92,16 +83,6 @@ impl Default for ThreadPool {
}
}
impl Executor for &ThreadPool {
fn spawn(
&mut self,
future: std::pin::Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), crate::executor::SpawnError> {
ThreadPool::spawn_background(self, future);
Ok(())
}
}
impl fmt::Debug for ThreadPool {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ThreadPool").finish()
+2 -17
View File
@@ -6,14 +6,13 @@ use crate::executor::loom::rand::seed;
use crate::executor::loom::sync::Arc;
use crate::executor::park::Unpark;
use crate::executor::task::{self, JoinHandle, Task};
use crate::executor::thread_pool::{current, queue, BoxFuture, Idle, Owned, Shared};
use crate::executor::thread_pool::{current, queue, Idle, Owned, Shared};
use crate::executor::util::{CachePadded, FastRand};
use crate::executor::{Executor, SpawnError};
use std::cell::UnsafeCell;
use std::future::Future;
pub(crate) struct Set<P>
pub(super) struct Set<P>
where
P: 'static,
{
@@ -206,17 +205,3 @@ impl Set<Box<dyn Unpark>> {
handle
}
}
impl<P> Executor for &Set<P>
where
P: Unpark,
{
fn spawn(&mut self, future: BoxFuture) -> Result<(), SpawnError> {
self.spawn_background(future);
Ok(())
}
fn status(&self) -> Result<(), SpawnError> {
Ok(())
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ impl Spawner {
}
/// Spawn a task in the background
pub(super) fn spawn_background<F>(&self, future: F)
pub(crate) fn spawn_background<F>(&self, future: F)
where
F: Future<Output = ()> + Send + 'static,
{
@@ -13,7 +13,7 @@ macro_rules! pool {
(! $n:expr) => {{
let mut mock_park = crate::executor::tests::mock_park::MockPark::new();
let blocking = std::sync::Arc::new(crate::executor::blocking::Pool::default());
let (pool, workers) = thread_pool::create_pool(
let (pool, workers) = thread_pool::worker::create_set(
$n,
|index| Box::new(mock_park.mk_park(index)),
Arc::new(Box::new(|_| {
+9 -5
View File
@@ -1,7 +1,7 @@
use crate::executor::loom::sync::Arc;
use crate::executor::park::{Park, Unpark};
use crate::executor::task::Task;
use crate::executor::thread_pool::{current, Owned, Shared};
use crate::executor::thread_pool::{current, Owned, Shared, Spawner};
use std::cell::Cell;
use std::ops::{Deref, DerefMut};
@@ -71,7 +71,7 @@ pub(crate) struct Worker<P: Park + 'static> {
gone: Cell<bool>,
}
pub(crate) fn create_set<F, P>(
pub(super) fn create_set<F, P>(
pool_size: usize,
mk_park: F,
launch_worker: LaunchWorker<P>,
@@ -128,12 +128,16 @@ where
}
}
pub(super) fn run(mut self) {
pub(super) fn run(mut self)
where
P: Park<Unpark = Box<dyn Unpark>>,
{
let pool = Arc::clone(&self.entry.pool);
let pool = &pool;
let index = self.entry.index;
let mut executor = &**pool;
let executor = &**pool;
let spawner = Spawner::new(pool.clone());
let entry = &mut self.entry;
let launch_worker = &self.launch_worker;
@@ -146,7 +150,7 @@ where
current::set(&pool, index, || {
let _enter = crate::executor::enter().expect("executor already running on thread");
crate::executor::with_default(&mut executor, || {
crate::executor::global::with_thread_pool(&spawner, || {
crate::executor::blocking::with_pool(blocking, || {
ON_BLOCK.with(|ob| {
// Ensure that the ON_BLOCK is removed from the thread-local context
-178
View File
@@ -1,178 +0,0 @@
use crate::executor::SpawnError;
/// A value that spawns futures of a specific type.
///
/// The trait is generic over `T`: the type of future that can be spawened. This
/// is useful for implementing an executor that is only able to spawn a specific
/// type of future.
///
/// The [`spawn`] function is used to submit the future to the executor. Once
/// submitted, the executor takes ownership of the future and becomes
/// responsible for driving the future to completion.
///
/// This trait is useful as a bound for applications and libraries in order to
/// be generic over futures that are `Send` vs. `!Send`.
///
/// # Examples
///
/// Consider a function that provides an API for draining a `Stream` in the
/// background. To do this, a task must be spawned to perform the draining. As
/// such, the function takes a stream and an executor on which the background
/// task is spawned.
///
/// [`spawn`]: TypedExecutor::spawn
/// ```
/// use tokio::executor::TypedExecutor;
/// use tokio::sync::oneshot;
///
/// use futures_core::{ready, Stream};
/// use std::future::Future;
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
///
/// async fn drain<T, E>(stream: T, executor: &mut E)
/// where
/// T: Stream + Unpin,
/// E: TypedExecutor<Drain<T>>
/// {
/// let (tx, rx) = oneshot::channel();
///
/// executor.spawn(Drain {
/// stream,
/// tx: Some(tx),
/// }).unwrap();
///
/// rx.await.unwrap()
/// }
///
/// // The background task
/// pub struct Drain<T> {
/// stream: T,
/// tx: Option<oneshot::Sender<()>>,
/// }
///
/// impl<T: Stream + Unpin> Future for Drain<T> {
/// type Output = ();
///
/// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
/// loop {
/// let item = ready!(
/// Pin::new(&mut self.stream).poll_next(cx)
/// );
///
/// if item.is_none() { break; }
/// }
///
/// let _ = self.tx.take().unwrap().send(()).map_err(|_| ());
/// Poll::Ready(())
/// }
/// }
/// ```
///
/// By doing this, the `drain` fn can accept a stream that is `!Send` as long as
/// the supplied executor is able to spawn `!Send` types.
pub trait TypedExecutor<T> {
/// Spawns a future to run on this executor.
///
/// `future` is passed to the executor, which will begin running it. The
/// executor takes ownership of the future and becomes responsible for
/// driving the future to completion.
///
/// # Panics
///
/// Implementations are encouraged to avoid panics. However, panics are
/// permitted and the caller should check the implementation specific
/// documentation for more details on possible panics.
///
/// # Examples
///
/// ```rust
/// use tokio::executor::TypedExecutor;
///
/// use std::future::Future;
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
///
/// fn example<T>(my_executor: &mut T)
/// where
/// T: TypedExecutor<MyFuture>,
/// {
/// my_executor.spawn(MyFuture).unwrap();
/// }
///
/// struct MyFuture;
///
/// impl Future for MyFuture {
/// type Output = ();
///
/// fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
/// println!("running on the executor");
/// Poll::Ready(())
/// }
/// }
/// ```
fn spawn(&mut self, future: T) -> Result<(), SpawnError>;
/// 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.
///
/// # Panics
///
/// This function must not panic. Implementers must ensure that panics do
/// not happen.
///
/// # Examples
///
/// ```rust
/// use tokio::executor::TypedExecutor;
///
/// use std::future::Future;
/// use std::pin::Pin;
/// use std::task::{Context, Poll};
///
/// fn example<T>(my_executor: &mut T)
/// where
/// T: TypedExecutor<MyFuture>,
/// {
/// if my_executor.status().is_ok() {
/// my_executor.spawn(MyFuture).unwrap();
/// } else {
/// println!("the executor is not in a good state");
/// }
/// }
///
/// struct MyFuture;
///
/// impl Future for MyFuture {
/// type Output = ();
///
/// fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
/// println!("running on the executor");
/// Poll::Ready(())
/// }
/// }
/// ```
fn status(&self) -> Result<(), SpawnError> {
Ok(())
}
}
impl<E, T> TypedExecutor<T> for Box<E>
where
E: TypedExecutor<T>,
{
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
(**self).spawn(future)
}
fn status(&self) -> Result<(), SpawnError> {
(**self).status()
}
}
-24
View File
@@ -1,24 +0,0 @@
#![warn(rust_2018_idioms)]
use tokio::executor::DefaultExecutor;
use std::future::Future;
use std::pin::Pin;
mod out_of_executor_context {
use super::*;
use tokio::executor::Executor;
fn test<F, E>(spawn: F)
where
F: Fn(Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<(), E>,
{
let res = spawn(Box::pin(async {}));
assert!(res.is_err());
}
#[test]
fn spawn() {
test(|f| DefaultExecutor::current().spawn(f));
}
}
-17
View File
@@ -1,17 +0,0 @@
use tokio::executor::{with_default, DefaultExecutor};
#[test]
fn default_executor_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<DefaultExecutor>();
}
#[test]
#[should_panic]
fn nested_default_executor_status() {
let _enter = tokio::executor::enter().unwrap();
let mut executor = DefaultExecutor::current();
let _result = with_default(&mut executor, || ());
}