refactor(lib): Use pin-project crate to perform pin projections

Remove all pin-related `unsafe` code from Hyper, as well as the
now-unused 'pin-utils' dependency.
This commit is contained in:
Aaron Hill
2019-08-22 23:06:12 -04:00
committed by Sean McArthur
parent d406602c5d
commit 4c552a4960
6 changed files with 147 additions and 125 deletions

View File

@@ -34,7 +34,8 @@ iovec = "0.1"
itoa = "0.4.1" itoa = "0.4.1"
log = "0.4" log = "0.4"
net2 = { version = "0.2.32", optional = true } net2 = { version = "0.2.32", optional = true }
pin-utils = "=0.1.0-alpha.4" pin-project = { version = "0.4.0-alpha.7", features = ["project_attr"] }
time = "0.1" time = "0.1"
tokio = { version = "=0.2.0-alpha.4", optional = true, default-features = false, features = ["rt-full"] } tokio = { version = "=0.2.0-alpha.4", optional = true, default-features = false, features = ["rt-full"] }
tower-service = "=0.3.0-alpha.1" tower-service = "=0.3.0-alpha.1"

View File

@@ -2,6 +2,7 @@ use std::mem;
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use tokio_sync::{mpsc, watch}; use tokio_sync::{mpsc, watch};
use pin_project::pin_project;
use super::{Future, Never, Poll, Pin, task}; use super::{Future, Never, Poll, Pin, task};
@@ -43,7 +44,9 @@ pub struct Watch {
} }
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[pin_project]
pub struct Watching<F, FN> { pub struct Watching<F, FN> {
#[pin]
future: F, future: F,
state: State<FN>, state: State<FN>,
watch: Watch, watch: Watch,
@@ -95,10 +98,10 @@ where
{ {
type Output = F::Output; type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let me = unsafe { self.get_unchecked_mut() };
loop { loop {
match mem::replace(&mut me.state, State::Draining) { let me = self.project();
match mem::replace(me.state, State::Draining) {
State::Watch(on_drain) => { State::Watch(on_drain) => {
let recv = me.watch.rx.recv_ref(); let recv = me.watch.rx.recv_ref();
futures_util::pin_mut!(recv); futures_util::pin_mut!(recv);
@@ -106,17 +109,17 @@ where
match recv.poll_unpin(cx) { match recv.poll_unpin(cx) {
Poll::Ready(None) => { Poll::Ready(None) => {
// Drain has been triggered! // Drain has been triggered!
on_drain(unsafe { Pin::new_unchecked(&mut me.future) }); on_drain(me.future);
}, },
Poll::Ready(Some(_/*State::Open*/)) | Poll::Ready(Some(_/*State::Open*/)) |
Poll::Pending => { Poll::Pending => {
me.state = State::Watch(on_drain); *me.state = State::Watch(on_drain);
return unsafe { Pin::new_unchecked(&mut me.future) }.poll(cx); return me.future.poll(cx);
}, },
} }
}, },
State::Draining => { State::Draining => {
return unsafe { Pin::new_unchecked(&mut me.future) }.poll(cx); return me.future.poll(cx)
}, },
} }
} }

View File

@@ -1,6 +1,7 @@
use std::error::Error as StdError; use std::error::Error as StdError;
use std::marker::Unpin; use std::marker::Unpin;
use pin_project::{pin_project, project};
use h2::Reason; use h2::Reason;
use h2::server::{Builder, Connection, Handshake, SendResponse}; use h2::server::{Builder, Connection, Handshake, SendResponse};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
@@ -199,19 +200,22 @@ where
} }
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[pin_project]
pub struct H2Stream<F, B> pub struct H2Stream<F, B>
where where
B: Payload, B: Payload,
{ {
reply: SendResponse<SendBuf<B::Data>>, reply: SendResponse<SendBuf<B::Data>>,
#[pin]
state: H2StreamState<F, B>, state: H2StreamState<F, B>,
} }
#[pin_project]
enum H2StreamState<F, B> enum H2StreamState<F, B>
where where
B: Payload, B: Payload,
{ {
Service(F), Service(#[pin] F),
Body(PipeToSendStream<B>), Body(PipeToSendStream<B>),
} }
@@ -229,6 +233,19 @@ where
} }
} }
macro_rules! reply {
($me:expr, $res:expr, $eos:expr) => ({
match $me.reply.send_response($res, $eos) {
Ok(tx) => tx,
Err(e) => {
debug!("send response error: {}", e);
$me.reply.send_reset(Reason::INTERNAL_ERROR);
return Poll::Ready(Err(crate::Error::new_h2(e)));
}
}
})
}
impl<F, B, E> H2Stream<F, B> impl<F, B, E> H2Stream<F, B>
where where
F: Future<Output = Result<Response<B>, E>>, F: Future<Output = Result<Response<B>, E>>,
@@ -236,13 +253,14 @@ where
B::Data: Unpin, B::Data: Unpin,
E: Into<Box<dyn StdError + Send + Sync>>, E: Into<Box<dyn StdError + Send + Sync>>,
{ {
fn poll2(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> { #[project]
// Safety: State::{Service, Body} futures are never moved fn poll2(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
let me = unsafe { self.get_unchecked_mut() };
loop { loop {
let next = match me.state { let mut me = self.project();
H2StreamState::Service(ref mut h) => { #[project]
let res = match unsafe { Pin::new_unchecked(h) }.poll(cx) { let next = match me.state.project() {
H2StreamState::Service(h) => {
let res = match h.poll(cx) {
Poll::Ready(Ok(r)) => r, Poll::Ready(Ok(r)) => r,
Poll::Pending => { Poll::Pending => {
// Response is not yet ready, so we want to check if the client has sent a // Response is not yet ready, so we want to check if the client has sent a
@@ -274,18 +292,7 @@ where
.expect("DATE is a valid HeaderName") .expect("DATE is a valid HeaderName")
.or_insert_with(crate::proto::h1::date::update_and_header_value); .or_insert_with(crate::proto::h1::date::update_and_header_value);
macro_rules! reply {
($eos:expr) => ({
match me.reply.send_response(res, $eos) {
Ok(tx) => tx,
Err(e) => {
debug!("send response error: {}", e);
me.reply.send_reset(Reason::INTERNAL_ERROR);
return Poll::Ready(Err(crate::Error::new_h2(e)));
}
}
})
}
// automatically set Content-Length from body... // automatically set Content-Length from body...
if let Some(len) = body.size_hint().exact() { if let Some(len) = body.size_hint().exact() {
@@ -293,10 +300,10 @@ where
} }
if !body.is_end_stream() { if !body.is_end_stream() {
let body_tx = reply!(false); let body_tx = reply!(me, res, false);
H2StreamState::Body(PipeToSendStream::new(body, body_tx)) H2StreamState::Body(PipeToSendStream::new(body, body_tx))
} else { } else {
reply!(true); reply!(me, res, true);
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
}, },
@@ -304,7 +311,7 @@ where
return Pin::new(pipe).poll(cx); return Pin::new(pipe).poll(cx);
} }
}; };
me.state = next; me.state.set(next);
} }
} }
} }

View File

@@ -16,8 +16,8 @@ use std::mem;
use bytes::Bytes; use bytes::Bytes;
use futures_core::Stream; use futures_core::Stream;
use pin_utils::{unsafe_pinned, unsafe_unpinned};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use pin_project::{pin_project, project};
#[cfg(feature = "runtime")] use tokio_net::driver::Handle; #[cfg(feature = "runtime")] use tokio_net::driver::Handle;
use crate::body::{Body, Payload}; use crate::body::{Body, Payload};
@@ -69,8 +69,10 @@ enum ConnectionMode {
/// ///
/// Yields `Connecting`s that are futures that should be put on a reactor. /// Yields `Connecting`s that are futures that should be put on a reactor.
#[must_use = "streams do nothing unless polled"] #[must_use = "streams do nothing unless polled"]
#[pin_project]
#[derive(Debug)] #[derive(Debug)]
pub struct Serve<I, S, E = Exec> { pub struct Serve<I, S, E = Exec> {
#[pin]
incoming: I, incoming: I,
make_service: S, make_service: S,
protocol: Http<E>, protocol: Http<E>,
@@ -81,16 +83,20 @@ pub struct Serve<I, S, E = Exec> {
/// Wraps the future returned from `MakeService` into one that returns /// Wraps the future returned from `MakeService` into one that returns
/// a `Connection`. /// a `Connection`.
#[must_use = "futures do nothing unless polled"] #[must_use = "futures do nothing unless polled"]
#[pin_project]
#[derive(Debug)] #[derive(Debug)]
pub struct Connecting<I, F, E = Exec> { pub struct Connecting<I, F, E = Exec> {
#[pin]
future: F, future: F,
io: Option<I>, io: Option<I>,
protocol: Http<E>, protocol: Http<E>,
} }
#[must_use = "futures do nothing unless polled"] #[must_use = "futures do nothing unless polled"]
#[pin_project]
#[derive(Debug)] #[derive(Debug)]
pub(super) struct SpawnAll<I, S, E> { pub(super) struct SpawnAll<I, S, E> {
#[pin]
pub(super) serve: Serve<I, S, E>, pub(super) serve: Serve<I, S, E>,
} }
@@ -98,6 +104,7 @@ pub(super) struct SpawnAll<I, S, E> {
/// ///
/// Polling this future will drive HTTP forward. /// Polling this future will drive HTTP forward.
#[must_use = "futures do nothing unless polled"] #[must_use = "futures do nothing unless polled"]
#[pin_project]
pub struct Connection<T, S, E = Exec> pub struct Connection<T, S, E = Exec>
where where
S: Service<Body>, S: Service<Body>,
@@ -119,9 +126,10 @@ where
fallback: Fallback<E>, fallback: Fallback<E>,
} }
#[pin_project]
pub(super) enum Either<A, B> { pub(super) enum Either<A, B> {
A(A), A(#[pin] A),
B(B), B(#[pin] B),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -484,10 +492,8 @@ where
/// ///
/// This `Connection` should continue to be polled until shutdown /// This `Connection` should continue to be polled until shutdown
/// can finish. /// can finish.
pub fn graceful_shutdown(self: Pin<&mut Self>) { pub fn graceful_shutdown(mut self: Pin<&mut Self>) {
// Safety: neither h1 nor h2 poll any of the generic futures match self.project().conn.as_mut().unwrap() {
// in these methods.
match unsafe { self.get_unchecked_mut() }.conn.as_mut().unwrap() {
Either::A(ref mut h1) => { Either::A(ref mut h1) => {
h1.disable_keep_alive(); h1.disable_keep_alive();
}, },
@@ -672,9 +678,6 @@ where
// ===== impl Serve ===== // ===== impl Serve =====
impl<I, S, E> Serve<I, S, E> { impl<I, S, E> Serve<I, S, E> {
unsafe_pinned!(incoming: I);
unsafe_unpinned!(make_service: S);
/// Spawn all incoming connections onto the executor in `Http`. /// Spawn all incoming connections onto the executor in `Http`.
pub(super) fn spawn_all(self) -> SpawnAll<I, S, E> { pub(super) fn spawn_all(self) -> SpawnAll<I, S, E> {
SpawnAll { SpawnAll {
@@ -709,7 +712,7 @@ where
type Item = crate::Result<Connecting<IO, S::Future, E>>; type Item = crate::Result<Connecting<IO, S::Future, E>>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
match ready!(self.as_mut().make_service().poll_ready_ref(cx)) { match ready!(self.project().make_service.poll_ready_ref(cx)) {
Ok(()) => (), Ok(()) => (),
Err(e) => { Err(e) => {
trace!("make_service closed"); trace!("make_service closed");
@@ -717,9 +720,9 @@ where
} }
} }
if let Some(item) = ready!(self.as_mut().incoming().poll_next(cx)) { if let Some(item) = ready!(self.project().incoming.poll_next(cx)) {
let io = item.map_err(crate::Error::new_accept)?; let io = item.map_err(crate::Error::new_accept)?;
let new_fut = self.as_mut().make_service().make_service_ref(&io); let new_fut = self.project().make_service.make_service_ref(&io);
Poll::Ready(Some(Ok(Connecting { Poll::Ready(Some(Ok(Connecting {
future: new_fut, future: new_fut,
io: Some(io), io: Some(io),
@@ -733,10 +736,6 @@ where
// ===== impl Connecting ===== // ===== impl Connecting =====
impl<I, F, E> Connecting<I, F, E> {
unsafe_pinned!(future: F);
unsafe_unpinned!(io: Option<I>);
}
impl<I, F, S, FE, E, B> Future for Connecting<I, F, E> impl<I, F, S, FE, E, B> Future for Connecting<I, F, E>
where where
@@ -750,8 +749,8 @@ where
type Output = Result<Connection<I, S, E>, FE>; type Output = Result<Connection<I, S, E>, FE>;
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let service = ready!(self.as_mut().future().poll(cx))?; let service = ready!(self.project().future.poll(cx))?;
let io = self.as_mut().io().take().expect("polled after complete"); let io = self.project().io.take().expect("polled after complete");
Poll::Ready(Ok(self.protocol.serve_connection(io, service))) Poll::Ready(Ok(self.protocol.serve_connection(io, service)))
} }
} }
@@ -784,17 +783,15 @@ where
B: Payload, B: Payload,
E: H2Exec<<S::Service as Service<Body>>::Future, B>, E: H2Exec<<S::Service as Service<Body>>::Future, B>,
{ {
pub(super) fn poll_watch<W>(self: Pin<&mut Self>, cx: &mut task::Context<'_>, watcher: &W) -> Poll<crate::Result<()>> pub(super) fn poll_watch<W>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, watcher: &W) -> Poll<crate::Result<()>>
where where
E: NewSvcExec<IO, S::Future, S::Service, E, W>, E: NewSvcExec<IO, S::Future, S::Service, E, W>,
W: Watcher<IO, S::Service, E>, W: Watcher<IO, S::Service, E>,
{ {
// Safety: futures are never moved... lolwtf
let me = unsafe { self.get_unchecked_mut() };
loop { loop {
if let Some(connecting) = ready!(unsafe { Pin::new_unchecked(&mut me.serve) }.poll_next(cx)?) { if let Some(connecting) = ready!(self.project().serve.poll_next(cx)?) {
let fut = NewSvcTask::new(connecting, watcher.clone()); let fut = NewSvcTask::new(connecting, watcher.clone());
me.serve.protocol.exec.execute_new_svc(fut)?; self.project().serve.project().protocol.exec.execute_new_svc(fut)?;
} else { } else {
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
@@ -810,13 +807,13 @@ where
{ {
type Output = A::Output; type Output = A::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { #[project]
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
// Just simple pin projection to the inner variants // Just simple pin projection to the inner variants
unsafe { #[project]
match self.get_unchecked_mut() { match self.project() {
Either::A(a) => Pin::new_unchecked(a).poll(cx), Either::A(a) => a.poll(cx),
Either::B(b) => Pin::new_unchecked(b).poll(cx), Either::B(b) => b.poll(cx),
}
} }
} }
} }
@@ -830,6 +827,7 @@ pub(crate) mod spawn_all {
use crate::common::{Future, Pin, Poll, Unpin, task}; use crate::common::{Future, Pin, Poll, Unpin, task};
use crate::service::Service; use crate::service::Service;
use super::{Connecting, UpgradeableConnection}; use super::{Connecting, UpgradeableConnection};
use pin_project::{pin_project, project};
// Used by `SpawnAll` to optionally watch a `Connection` future. // Used by `SpawnAll` to optionally watch a `Connection` future.
// //
@@ -872,14 +870,18 @@ pub(crate) mod spawn_all {
// //
// Users cannot import this type, nor the associated `NewSvcExec`. Instead, // Users cannot import this type, nor the associated `NewSvcExec`. Instead,
// a blanket implementation for `Executor<impl Future>` is sufficient. // a blanket implementation for `Executor<impl Future>` is sufficient.
#[pin_project]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct NewSvcTask<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> { pub struct NewSvcTask<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> {
#[pin]
state: State<I, N, S, E, W>, state: State<I, N, S, E, W>,
} }
enum State<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> { #[pin_project]
Connecting(Connecting<I, N, E>, W), pub enum State<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> {
Connected(W::Future), Connecting(#[pin] Connecting<I, N, E>, W),
Connected(#[pin] W::Future),
} }
impl<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> NewSvcTask<I, N, S, E, W> { impl<I, N, S: Service<Body>, E, W: Watcher<I, S, E>> NewSvcTask<I, N, S, E, W> {
@@ -903,39 +905,43 @@ pub(crate) mod spawn_all {
{ {
type Output = (); type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { #[project]
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
// If it weren't for needing to name this type so the `Send` bounds // If it weren't for needing to name this type so the `Send` bounds
// could be projected to the `Serve` executor, this could just be // could be projected to the `Serve` executor, this could just be
// an `async fn`, and much safer. Woe is me. // an `async fn`, and much safer. Woe is me.
let me = unsafe { self.get_unchecked_mut() };
loop { loop {
let next = match me.state { let mut me = self.project();
State::Connecting(ref mut connecting, ref watcher) => { let next = {
let res = ready!(unsafe { Pin::new_unchecked(connecting).poll(cx) }); #[project]
let conn = match res { match me.state.project() {
Ok(conn) => conn, State::Connecting(connecting, watcher) => {
Err(err) => { let res = ready!(connecting.poll(cx));
let err = crate::Error::new_user_make_service(err); let conn = match res {
debug!("connecting error: {}", err); Ok(conn) => conn,
return Poll::Ready(()); Err(err) => {
} let err = crate::Error::new_user_make_service(err);
}; debug!("connecting error: {}", err);
let connected = watcher.watch(conn.with_upgrades()); return Poll::Ready(());
State::Connected(connected)
},
State::Connected(ref mut future) => {
return unsafe { Pin::new_unchecked(future) }
.poll(cx)
.map(|res| {
if let Err(err) = res {
debug!("connection error: {}", err);
} }
}); };
let connected = watcher.watch(conn.with_upgrades());
State::Connected(connected)
},
State::Connected(future) => {
return future
.poll(cx)
.map(|res| {
if let Err(err) = res {
debug!("connection error: {}", err);
}
});
}
} }
}; };
me.state = next; me.state.set(next);
} }
} }
} }

View File

@@ -59,8 +59,8 @@ use std::fmt;
#[cfg(feature = "runtime")] use std::time::Duration; #[cfg(feature = "runtime")] use std::time::Duration;
use futures_core::Stream; use futures_core::Stream;
use pin_utils::unsafe_pinned;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use pin_project::pin_project;
use crate::body::{Body, Payload}; use crate::body::{Body, Payload};
use crate::common::exec::{Exec, H2Exec, NewSvcExec}; use crate::common::exec::{Exec, H2Exec, NewSvcExec};
@@ -78,7 +78,9 @@ use self::shutdown::{Graceful, GracefulWatcher};
/// handlers. It is built using the [`Builder`](Builder), and the future /// handlers. It is built using the [`Builder`](Builder), and the future
/// completes when the server has been shutdown. It should be run by an /// completes when the server has been shutdown. It should be run by an
/// `Executor`. /// `Executor`.
#[pin_project]
pub struct Server<I, S, E = Exec> { pub struct Server<I, S, E = Exec> {
#[pin]
spawn_all: SpawnAll<I, S, E>, spawn_all: SpawnAll<I, S, E>,
} }
@@ -101,11 +103,6 @@ impl<I> Server<I, ()> {
} }
} }
impl<I, S, E> Server<I, S, E> {
// Never moved, just projected
unsafe_pinned!(spawn_all: SpawnAll<I, S, E>);
}
#[cfg(feature = "runtime")] #[cfg(feature = "runtime")]
impl Server<AddrIncoming, ()> { impl Server<AddrIncoming, ()> {
/// Binds to the provided address, and returns a [`Builder`](Builder). /// Binds to the provided address, and returns a [`Builder`](Builder).
@@ -216,8 +213,8 @@ where
{ {
type Output = crate::Result<()>; type Output = crate::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.spawn_all().poll_watch(cx, &NoopWatcher) self.project().spawn_all.poll_watch(cx, &NoopWatcher)
} }
} }

View File

@@ -2,6 +2,7 @@ use std::error::Error as StdError;
use futures_core::Stream; use futures_core::Stream;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use pin_project::{pin_project, project};
use crate::body::{Body, Payload}; use crate::body::{Body, Payload};
use crate::common::drain::{self, Draining, Signal, Watch, Watching}; use crate::common::drain::{self, Draining, Signal, Watch, Watching};
@@ -11,14 +12,19 @@ use crate::service::{MakeServiceRef, Service};
use super::conn::{SpawnAll, UpgradeableConnection, Watcher}; use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[pin_project]
pub struct Graceful<I, S, F, E> { pub struct Graceful<I, S, F, E> {
#[pin]
state: State<I, S, F, E>, state: State<I, S, F, E>,
} }
enum State<I, S, F, E> { #[pin_project]
pub(super) enum State<I, S, F, E> {
Running { Running {
drain: Option<(Signal, Watch)>, drain: Option<(Signal, Watch)>,
#[pin]
spawn_all: SpawnAll<I, S, E>, spawn_all: SpawnAll<I, S, E>,
#[pin]
signal: F, signal: F,
}, },
Draining(Draining), Draining(Draining),
@@ -54,39 +60,41 @@ where
{ {
type Output = crate::Result<()>; type Output = crate::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { #[project]
// Safety: the futures are NEVER moved, self.state is overwritten instead. fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let me = unsafe { self.get_unchecked_mut() }; let mut me = self.project();
loop { loop {
let next = match me.state { let next = {
State::Running { #[project]
ref mut drain, match me.state.project() {
ref mut spawn_all, State::Running {
ref mut signal, drain,
} => match unsafe { Pin::new_unchecked(signal) }.poll(cx) { spawn_all,
Poll::Ready(()) => { signal,
debug!("signal received, starting graceful shutdown"); } => match signal.poll(cx) {
let sig = drain Poll::Ready(()) => {
.take() debug!("signal received, starting graceful shutdown");
.expect("drain channel") let sig = drain
.0; .take()
State::Draining(sig.drain()) .expect("drain channel")
.0;
State::Draining(sig.drain())
},
Poll::Pending => {
let watch = drain
.as_ref()
.expect("drain channel")
.1
.clone();
return spawn_all.poll_watch(cx, &GracefulWatcher(watch));
},
}, },
Poll::Pending => { State::Draining(ref mut draining) => {
let watch = drain return Pin::new(draining).poll(cx).map(Ok);
.as_ref() }
.expect("drain channel")
.1
.clone();
return unsafe { Pin::new_unchecked(spawn_all) }.poll_watch(cx, &GracefulWatcher(watch));
},
},
State::Draining(ref mut draining) => {
return Pin::new(draining).poll(cx).map(Ok);
} }
}; };
// It's important to just assign, not mem::replace or anything. me.state.set(next);
me.state = next;
} }
} }
} }