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:
		| @@ -36,6 +36,7 @@ pub struct Watch { | ||||
|     rx: Shared<oneshot::Receiver<()>>, | ||||
| } | ||||
|  | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct Watching<F, FN> { | ||||
|     future: F, | ||||
|     state: State<FN>, | ||||
|   | ||||
| @@ -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() | ||||
|             }) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| mod buf; | ||||
| pub(crate) mod drain; | ||||
| mod exec; | ||||
| pub(crate) mod exec; | ||||
| pub(crate) mod io; | ||||
| mod lazy; | ||||
| mod never; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user