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:
		| @@ -2,26 +2,27 @@ use futures::{Async, Future, Stream, Poll}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use body::{Body, Payload}; | ||||
| use common::drain::{self, Draining, Signal, Watch}; | ||||
| use common::drain::{self, Draining, Signal, Watch, Watching}; | ||||
| use common::exec::{H2Exec, NewSvcExec}; | ||||
| use service::{Service, NewService}; | ||||
| use super::SpawnAll; | ||||
| use super::conn::{SpawnAll, UpgradeableConnection, Watcher}; | ||||
|  | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct Graceful<I, S, F> { | ||||
|     state: State<I, S, F>, | ||||
| pub struct Graceful<I, S, F, E> { | ||||
|     state: State<I, S, F, E>, | ||||
| } | ||||
|  | ||||
| enum State<I, S, F> { | ||||
| enum State<I, S, F, E> { | ||||
|     Running { | ||||
|         drain: Option<(Signal, Watch)>, | ||||
|         spawn_all: SpawnAll<I, S>, | ||||
|         spawn_all: SpawnAll<I, S, E>, | ||||
|         signal: F, | ||||
|     }, | ||||
|     Draining(Draining), | ||||
| } | ||||
|  | ||||
| impl<I, S, F> Graceful<I, S, F> { | ||||
|     pub(super) fn new(spawn_all: SpawnAll<I, S>, signal: F) -> Self { | ||||
| impl<I, S, F, E> Graceful<I, S, F, E> { | ||||
|     pub(super) fn new(spawn_all: SpawnAll<I, S, E>, signal: F) -> Self { | ||||
|         let drain = Some(drain::channel()); | ||||
|         Graceful { | ||||
|             state: State::Running { | ||||
| @@ -34,18 +35,18 @@ impl<I, S, F> Graceful<I, S, F> { | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<I, S, B, F> Future for Graceful<I, S, F> | ||||
| impl<I, S, B, F, E> Future for Graceful<I, S, F, E> | ||||
| where | ||||
|     I: Stream, | ||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||
|     S: NewService<ReqBody=Body, ResBody=B> + Send + 'static, | ||||
|     S: NewService<ReqBody=Body, ResBody=B>, | ||||
|     S::Service: 'static, | ||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     S::Service: Send, | ||||
|     S::Future: Send + 'static, | ||||
|     <S::Service as Service>::Future: Send + 'static, | ||||
|     B: Payload, | ||||
|     F: Future<Item=()>, | ||||
|     E: H2Exec<<S::Service as Service>::Future, B>, | ||||
|     E: NewSvcExec<I::Item, S::Future, S::Service, E, GracefulWatcher>, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ::Error; | ||||
| @@ -67,19 +68,12 @@ where | ||||
|                         State::Draining(sig.drain()) | ||||
|                     }, | ||||
|                     Ok(Async::NotReady) => { | ||||
|                         let watch = &drain | ||||
|                         let watch = drain | ||||
|                             .as_ref() | ||||
|                             .expect("drain channel") | ||||
|                             .1; | ||||
|                         return spawn_all.poll_with(|| { | ||||
|                             let watch = watch.clone(); | ||||
|                             move |conn| { | ||||
|                                 watch.watch(conn, |conn| { | ||||
|                                     // on_drain, start conn graceful shutdown | ||||
|                                     conn.graceful_shutdown() | ||||
|                                 }) | ||||
|                             } | ||||
|                         }); | ||||
|                             .1 | ||||
|                             .clone(); | ||||
|                         return spawn_all.poll_watch(&GracefulWatcher(watch)); | ||||
|                     }, | ||||
|                 }, | ||||
|                 State::Draining(ref mut draining) => { | ||||
| @@ -91,3 +85,35 @@ where | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[allow(missing_debug_implementations)] | ||||
| #[derive(Clone)] | ||||
| pub struct GracefulWatcher(Watch); | ||||
|  | ||||
| impl<I, S, E> Watcher<I, S, E> for GracefulWatcher | ||||
| where | ||||
|     I: AsyncRead + AsyncWrite + Send + 'static, | ||||
|     S: Service<ReqBody=Body> + 'static, | ||||
|     E: H2Exec<S::Future, S::ResBody>, | ||||
| { | ||||
|     type Future = Watching<UpgradeableConnection<I, S, E>, fn(&mut UpgradeableConnection<I, S, E>)>; | ||||
|  | ||||
|     fn watch(&self, conn: UpgradeableConnection<I, S, E>) -> Self::Future { | ||||
|         self | ||||
|             .0 | ||||
|             .clone() | ||||
|             .watch(conn, on_drain) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn on_drain<I, S, E>(conn: &mut UpgradeableConnection<I, S, E>) | ||||
| where | ||||
|     S: Service<ReqBody=Body>, | ||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||
|     I: AsyncRead + AsyncWrite, | ||||
|     S::ResBody: Payload + 'static, | ||||
|     E: H2Exec<S::Future, S::ResBody>, | ||||
| { | ||||
|     conn.graceful_shutdown() | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user