feat(server): allow !Send Servers
Until this commit, servers have required that `Service` and their `Future` to be `Send`, since the server needs to spawn some internal tasks to an executor, and by default, that is `tokio::spawn`, which could be spawning to a threadpool. This was true even if the user were certain there was no threadpool involved, and was instead using a different single-threaded runtime, like `tokio::runtime::current_thread`. This changes makes all the server pieces generic over an `E`, which is essentially `Executor<PrivateTypes<Server::Future>>`. There's a new set of internal traits, `H2Exec` and `NewSvcExec`, which allow for the type signature to only show the generics that the user is providing. The traits cannot be implemented explicitly, but there are blanket implementations for `E: Executor<SpecificType>`. If the user provides their own executor, it simply needs to have a generic `impl<F> Executor<F> for MyExec`. That impl can have bounds deciding whether to require `F: Send`. If the executor does require `Send`, and the `Service` futures are `!Send`, there will be compiler errors. To prevent a breaking change, all the types that gained the `E` generic have a default type set, which is the original `tokio::spawn` executor.
This commit is contained in:
@@ -3,14 +3,28 @@ use std::sync::Arc;
|
||||
|
||||
use futures::future::{Executor, Future};
|
||||
|
||||
/// Either the user provides an executor for background tasks, or we use
|
||||
/// `tokio::spawn`.
|
||||
use body::Payload;
|
||||
use proto::h2::server::H2Stream;
|
||||
use server::conn::spawn_all::{NewSvcTask, Watcher};
|
||||
use service::Service;
|
||||
|
||||
pub trait H2Exec<F, B: Payload>: Clone {
|
||||
fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()>;
|
||||
}
|
||||
|
||||
pub trait NewSvcExec<I, N, S: Service, E, W: Watcher<I, S, E>>: Clone {
|
||||
fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()>;
|
||||
}
|
||||
|
||||
// Either the user provides an executor for background tasks, or we use
|
||||
// `tokio::spawn`.
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum Exec {
|
||||
pub enum Exec {
|
||||
Default,
|
||||
Executor(Arc<Executor<Box<Future<Item=(), Error=()> + Send>> + Send + Sync>),
|
||||
}
|
||||
|
||||
// ===== impl Exec =====
|
||||
|
||||
impl Exec {
|
||||
pub(crate) fn execute<F>(&self, fut: F) -> ::Result<()>
|
||||
@@ -52,3 +66,58 @@ impl fmt::Debug for Exec {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<F, B> H2Exec<F, B> for Exec
|
||||
where
|
||||
H2Stream<F, B>: Future<Item=(), Error=()> + Send + 'static,
|
||||
B: Payload,
|
||||
{
|
||||
fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
|
||||
self.execute(fut)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
|
||||
where
|
||||
NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()> + Send + 'static,
|
||||
S: Service,
|
||||
W: Watcher<I, S, E>,
|
||||
{
|
||||
fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
|
||||
self.execute(fut)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== impl Executor =====
|
||||
|
||||
impl<E, F, B> H2Exec<F, B> for E
|
||||
where
|
||||
E: Executor<H2Stream<F, B>> + Clone,
|
||||
H2Stream<F, B>: Future<Item=(), Error=()>,
|
||||
B: Payload,
|
||||
{
|
||||
fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
|
||||
self.execute(fut)
|
||||
.map_err(|err| {
|
||||
warn!("executor error: {:?}", err.kind());
|
||||
::Error::new_execute()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
|
||||
where
|
||||
E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
|
||||
NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()>,
|
||||
S: Service,
|
||||
W: Watcher<I, S, E>,
|
||||
{
|
||||
fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
|
||||
self.execute(fut)
|
||||
.map_err(|err| {
|
||||
warn!("executor error: {:?}", err.kind());
|
||||
::Error::new_execute()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user