From 8a58a341a464db928e60309a4b5a66081ee7ecea Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 14:15:42 +0000 Subject: [PATCH] service improvements (#233) --- .github/workflows/bench.yml | 29 -- actix-connect/src/connector.rs | 4 +- actix-connect/src/resolve.rs | 4 +- actix-connect/src/service.rs | 8 +- actix-connect/src/ssl/openssl.rs | 8 +- actix-connect/src/ssl/rustls.rs | 4 +- actix-service/CHANGES.md | 8 +- actix-service/Cargo.toml | 13 +- actix-service/benches/and_then.rs | 334 ------------------ .../benches/unsafecell_vs_refcell.rs | 110 ------ actix-service/src/and_then.rs | 103 +++--- actix-service/src/and_then_apply_fn.rs | 334 ------------------ actix-service/src/apply.rs | 40 +-- actix-service/src/apply_cfg.rs | 85 +++-- actix-service/src/boxed.rs | 19 +- actix-service/src/fn_service.rs | 14 +- actix-service/src/lib.rs | 39 +- actix-service/src/map.rs | 58 +-- actix-service/src/map_config.rs | 2 +- actix-service/src/map_err.rs | 50 +-- actix-service/src/map_init_err.rs | 31 +- actix-service/src/pipeline.rs | 62 +--- actix-service/src/then.rs | 106 +++--- actix-service/src/transform.rs | 61 ++-- actix-service/src/transform_err.rs | 27 +- actix-tracing/src/lib.rs | 7 +- actix-utils/src/timeout.rs | 6 +- 27 files changed, 387 insertions(+), 1179 deletions(-) delete mode 100644 .github/workflows/bench.yml delete mode 100644 actix-service/benches/and_then.rs delete mode 100644 actix-service/benches/unsafecell_vs_refcell.rs delete mode 100644 actix-service/src/and_then_apply_fn.rs diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml deleted file mode 100644 index 7c76e171..00000000 --- a/.github/workflows/bench.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Benchmark (Linux) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - check_benchmark: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - override: true - - - name: Check benchmark - uses: actions-rs/cargo@v1 - with: - command: bench - args: --package=actix-service diff --git a/actix-connect/src/connector.rs b/actix-connect/src/connector.rs index e4c86d91..d3ef9813 100644 --- a/actix-connect/src/connector.rs +++ b/actix-connect/src/connector.rs @@ -75,9 +75,7 @@ impl Service> for TcpConnector { #[allow(clippy::type_complexity)] type Future = Either, Ready>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { let port = req.port(); diff --git a/actix-connect/src/resolve.rs b/actix-connect/src/resolve.rs index 85edf0d8..2c75cc0d 100644 --- a/actix-connect/src/resolve.rs +++ b/actix-connect/src/resolve.rs @@ -110,9 +110,7 @@ impl Service> for Resolver { Ready, Self::Error>>, >; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, mut req: Connect) -> Self::Future { if req.addr.is_some() { diff --git a/actix-connect/src/service.rs b/actix-connect/src/service.rs index ef5d04da..b942d230 100644 --- a/actix-connect/src/service.rs +++ b/actix-connect/src/service.rs @@ -94,9 +94,7 @@ impl Service> for ConnectService { type Error = ConnectError; type Future = ConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { ConnectServiceResponse { @@ -163,9 +161,7 @@ impl Service> for TcpConnectService { type Error = ConnectError; type Future = TcpConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { TcpConnectServiceResponse { diff --git a/actix-connect/src/ssl/openssl.rs b/actix-connect/src/ssl/openssl.rs index e1c6b6fb..a9bcc3c7 100644 --- a/actix-connect/src/ssl/openssl.rs +++ b/actix-connect/src/ssl/openssl.rs @@ -100,9 +100,7 @@ where #[allow(clippy::type_complexity)] type Future = Either, Ready>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); @@ -220,9 +218,7 @@ impl Service for OpensslConnectService { type Error = ConnectError; type Future = OpensslConnectServiceResponse; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, req: Connect) -> Self::Future { OpensslConnectServiceResponse { diff --git a/actix-connect/src/ssl/rustls.rs b/actix-connect/src/ssl/rustls.rs index 3e646082..984fbe49 100644 --- a/actix-connect/src/ssl/rustls.rs +++ b/actix-connect/src/ssl/rustls.rs @@ -96,9 +96,7 @@ where type Error = std::io::Error; type Future = ConnectAsyncExt; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 971741e8..3b9a9cc0 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -3,10 +3,14 @@ ## Unreleased - 2020-xx-xx * `Service`, other traits, and many type signatures now take the the request type as a type parameter instead of an associated type. [#232] -* Upgrade `pin-project` to `1.0`. - +* Add `always_ready!` and `forward_ready!` macros. [#233] +* Crate is now `no_std`. [#233] +* Migrate pin projections to `pin-project-lite`. [#233] +* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the + `.and_then(apply_fn(...))` construction. [#233] [#232]: https://github.com/actix/actix-net/pull/232 +[#233]: https://github.com/actix/actix-net/pull/233 ## 1.0.6 - 2020-08-09 diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 1505873b..60818968 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -17,17 +17,10 @@ name = "actix_service" path = "src/lib.rs" [dependencies] -futures-util = "0.3.1" -pin-project = "1.0.0" +pin-project-lite = "0.2" +futures-util = { version = "0.3.7", default-features = false } +futures-core = { version = "0.3.7", default-features = false } [dev-dependencies] actix-rt = "1.0.0" criterion = "0.3" - -[[bench]] -name = "unsafecell_vs_refcell" -harness = false - -[[bench]] -name = "and_then" -harness = false diff --git a/actix-service/benches/and_then.rs b/actix-service/benches/and_then.rs deleted file mode 100644 index c8aa315d..00000000 --- a/actix-service/benches/and_then.rs +++ /dev/null @@ -1,334 +0,0 @@ -use actix_service::boxed::BoxFuture; -use actix_service::IntoService; -use actix_service::Service; -/// Benchmark various implementations of and_then -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::TryFutureExt; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{ - cell::{RefCell, UnsafeCell}, - marker::PhantomData, -}; - -/* - * Test services A,B for AndThen service implementations - */ - -async fn svc1(_: ()) -> Result { - Ok(1) -} - -async fn svc2(req: usize) -> Result { - Ok(req + 1) -} - -/* - * AndThenUC - original AndThen service based on UnsafeCell - * Cut down version of actix_service::AndThenService based on actix-service::Cell - */ - -struct AndThenUC(Rc>, PhantomData); - -impl AndThenUC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(UnsafeCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenUC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenUC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponse; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = unsafe { &mut *(*self.0).get() }.0.call(req); - AndThenServiceResponse { - state: State::A(fut, Some(self.0.clone())), - _phantom: PhantomData, - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse -where - A: Service, - B: Service, -{ - #[pin] - state: State, - _phantom: PhantomData, -} - -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponse -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty(PhantomData)); // drop fut A - let fut = unsafe { &mut (*b.get()).1 }.call(res); - this.state.set(State::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty(PhantomData)); - r - }), - StateProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRC - AndThen service based on RefCell - */ - -struct AndThenRC(Rc>, PhantomData); - -impl AndThenRC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponseRC; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - AndThenServiceResponseRC { - state: StateRC::A(fut, Some(self.0.clone())), - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - #[pin] - state: StateRC, -} - -#[pin_project::pin_project(project = StateRCProj)] -enum StateRC -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateRCProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(StateRC::Empty(PhantomData)); // drop fut A - let fut = b.borrow_mut().1.call(res); - this.state.set(StateRC::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateRCProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(StateRC::Empty(PhantomData)); - r - }), - StateRCProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRCFuture - AndThen service based on RefCell - * and standard futures::future::and_then combinator in a Box - */ - -struct AndThenRCFuture(Rc>, PhantomData); - -impl AndThenRCFuture { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRCFuture { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRCFuture -where - A: Service + 'static, - A::Future: 'static, - B: Service + 'static, - B::Future: 'static, -{ - type Response = B::Response; - type Error = A::Error; - type Future = BoxFuture; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - let core = self.0.clone(); - let fut2 = move |res| (*core).borrow_mut().1.call(res); - Box::pin(fut.and_then(fut2)) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// Usable for benching Service wrappers: -/// Using minimum service code implementation we first measure -/// time to run minimum service, then measure time with wrapper. -/// -/// Sample output -/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] -pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenRCFuture::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell via future::and_then", - ); -} - -criterion_main!(service_benches); diff --git a/actix-service/benches/unsafecell_vs_refcell.rs b/actix-service/benches/unsafecell_vs_refcell.rs deleted file mode 100644 index cdf91233..00000000 --- a/actix-service/benches/unsafecell_vs_refcell.rs +++ /dev/null @@ -1,110 +0,0 @@ -use actix_service::Service; -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::{ok, Ready}; -use std::cell::{RefCell, UnsafeCell}; -use std::rc::Rc; -use std::task::{Context, Poll}; - -struct SrvUC(Rc>); - -impl Default for SrvUC { - fn default() -> Self { - Self(Rc::new(UnsafeCell::new(0))) - } -} - -impl Clone for SrvUC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvUC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - unsafe { *(*self.0).get() = *(*self.0).get() + 1 }; - ok(unsafe { *self.0.get() }) - } -} - -struct SrvRC(Rc>); - -impl Default for SrvRC { - fn default() -> Self { - Self(Rc::new(RefCell::new(0))) - } -} - -impl Clone for SrvRC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvRC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - let prev = *self.0.borrow(); - *(*self.0).borrow_mut() = prev + 1; - ok(*self.0.borrow()) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// Usable for benching Service wrappers: -/// Using minimum service code implementation we first measure -/// time to run minimum service, then measure time with wrapper. -/// -/// Sample output -/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us] -pub fn bench_async_service(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); -} -criterion_main!(service_benches); diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index 04caf79d..17d62e8f 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,8 +1,13 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{cell::RefCell, marker::PhantomData}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -50,30 +55,43 @@ where fn call(&mut self, req: Req) -> Self::Future { AndThenServiceResponse { - state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), + state: State::A { + fut: self.0.borrow_mut().0.call(req), + b: Some(self.0.clone()), + }, } } } -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse -where - A: Service, - B: Service, -{ - #[pin] - state: State, +pin_project! { + pub(crate) struct AndThenServiceResponse + where + A: Service, + B: Service, + { + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty, +pin_project! { + #[project = StateProj] + enum State + where + A: Service, + B: Service, + { + A { + #[pin] + fut: A::Future, + b: Option>>, + }, + B { + #[pin] + fut: B::Future, + }, + Empty, + } } impl Future for AndThenServiceResponse @@ -87,17 +105,17 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { + StateProj::A { fut, b } => match fut.poll(cx)? { Poll::Ready(res) => { let b = b.take().unwrap(); this.state.set(State::Empty); // drop fut A let fut = b.borrow_mut().1.call(res); - this.state.set(State::B(fut)); + this.state.set(State::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::B(fut) => fut.poll(cx).map(|r| { + StateProj::B { fut } => fut.poll(cx).map(|r| { this.state.set(State::Empty); r }), @@ -191,19 +209,20 @@ where } } -#[pin_project::pin_project] -pub(crate) struct AndThenServiceFactoryResponse -where - A: ServiceFactory, - B: ServiceFactory, -{ - #[pin] - fut_a: A::Future, - #[pin] - fut_b: B::Future, +pin_project! { + pub(crate) struct AndThenServiceFactoryResponse + where + A: ServiceFactory, + B: ServiceFactory, + { + #[pin] + fut_a: A::Future, + #[pin] + fut_b: B::Future, - a: Option, - b: Option, + a: Option, + b: Option, + } } impl AndThenServiceFactoryResponse @@ -254,9 +273,11 @@ where #[cfg(test)] mod tests { - use std::cell::Cell; - use std::rc::Rc; - use std::task::{Context, Poll}; + use alloc::rc::Rc; + use core::{ + cell::Cell, + task::{Context, Poll}, + }; use futures_util::future::{lazy, ok, ready, Ready}; diff --git a/actix-service/src/and_then_apply_fn.rs b/actix-service/src/and_then_apply_fn.rs deleted file mode 100644 index c7bd098c..00000000 --- a/actix-service/src/and_then_apply_fn.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::cell::RefCell; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use crate::{Service, ServiceFactory}; - -/// `Apply` service combinator -pub(crate) struct AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - svc: Rc>, - _phantom: PhantomData<(Fut, Req, In, Res, Err)>, -} - -impl AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - /// Create new `Apply` combinator - pub(crate) fn new(a: S1, b: S2, wrap_fn: F) -> Self { - Self { - svc: Rc::new(RefCell::new((a, b, wrap_fn))), - _phantom: PhantomData, - } - } -} - -impl Clone - for AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - fn clone(&self) -> Self { - AndThenApplyFn { - svc: self.svc.clone(), - _phantom: PhantomData, - } - } -} - -impl Service - for AndThenApplyFn -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - type Response = Res; - type Error = Err; - type Future = AndThenApplyFnFuture; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.svc.borrow_mut(); - let not_ready = inner.0.poll_ready(cx)?.is_pending(); - if inner.1.poll_ready(cx)?.is_pending() || not_ready { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.svc.borrow_mut().0.call(req); - AndThenApplyFnFuture { - state: State::A(fut, Some(self.svc.clone())), - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFuture -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - #[pin] - state: State, -} - -#[pin_project::pin_project(project = StateProj)] -enum State -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - A(#[pin] S1::Future, Option>>), - B(#[pin] Fut), - Empty(PhantomData), -} - -impl Future - for AndThenApplyFnFuture -where - S1: Service, - S2: Service, - F: FnMut(S1::Response, &mut S2) -> Fut, - Fut: Future>, - Err: From + From, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = Option::take(b).unwrap(); - this.state.set(State::Empty(PhantomData)); - let (_, b, f) = &mut *b.borrow_mut(); - let fut = f(res, b); - this.state.set(State::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty(PhantomData)); - r - }), - StateProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/// `AndThenApplyFn` service factory -pub(crate) struct AndThenApplyFnFactory { - srv: Rc<(SF1, SF2, F)>, - _phantom: PhantomData<(Fut, Req, In, Res, Err)>, -} - -impl - AndThenApplyFnFactory -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - /// Create new `ApplyNewService` new service instance - pub(crate) fn new(a: SF1, b: SF2, wrap_fn: F) -> Self { - Self { - srv: Rc::new((a, b, wrap_fn)), - _phantom: PhantomData, - } - } -} - -impl Clone - for AndThenApplyFnFactory -{ - fn clone(&self) -> Self { - Self { - srv: self.srv.clone(), - _phantom: PhantomData, - } - } -} - -impl ServiceFactory - for AndThenApplyFnFactory -where - SF1: ServiceFactory, - SF1::Config: Clone, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - type Response = Res; - type Error = Err; - type Service = AndThenApplyFn; - type Config = SF1::Config; - type InitError = SF1::InitError; - type Future = AndThenApplyFnFactoryResponse; - - fn new_service(&self, cfg: SF1::Config) -> Self::Future { - let srv = &*self.srv; - AndThenApplyFnFactoryResponse { - s1: None, - s2: None, - wrap_fn: srv.2.clone(), - fut_s1: srv.0.new_service(cfg.clone()), - fut_s2: srv.1.new_service(cfg), - _phantom: PhantomData, - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenApplyFnFactoryResponse -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From, - Err: From, -{ - #[pin] - fut_s1: SF1::Future, - #[pin] - fut_s2: SF2::Future, - wrap_fn: F, - s1: Option, - s2: Option, - _phantom: PhantomData, -} - -impl Future - for AndThenApplyFnFactoryResponse -where - SF1: ServiceFactory, - SF2: ServiceFactory, - F: FnMut(SF1::Response, &mut SF2::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, -{ - type Output = Result< - AndThenApplyFn, - SF1::InitError, - >; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - - if this.s1.is_none() { - if let Poll::Ready(service) = this.fut_s1.poll(cx)? { - *this.s1 = Some(service); - } - } - - if this.s2.is_none() { - if let Poll::Ready(service) = this.fut_s2.poll(cx)? { - *this.s2 = Some(service); - } - } - - if this.s1.is_some() && this.s2.is_some() { - Poll::Ready(Ok(AndThenApplyFn { - svc: Rc::new(RefCell::new(( - Option::take(this.s1).unwrap(), - Option::take(this.s2).unwrap(), - this.wrap_fn.clone(), - ))), - _phantom: PhantomData, - })) - } else { - Poll::Pending - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use futures_util::future::{lazy, ok, Ready, TryFutureExt}; - - use crate::{fn_service, pipeline, pipeline_factory, Service, ServiceFactory}; - - #[derive(Clone)] - struct Srv; - - impl Service for Srv { - type Response = (); - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: u8) -> Self::Future { - let _ = req; - ok(()) - } - } - - #[actix_rt::test] - async fn test_service() { - let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| { - s.call(1).map_ok(move |res| (req, res)) - }); - let res = lazy(|cx| srv.poll_ready(cx)).await; - assert!(res.is_ready()); - - let res = srv.call("srv").await; - assert!(res.is_ok()); - assert_eq!(res.unwrap(), ("srv", ())); - } - - #[actix_rt::test] - async fn test_service_factory() { - let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn( - || ok(Srv), - |req: &'static str, s| s.call(1).map_ok(move |res| (req, res)), - ); - let mut srv = new_srv.new_service(()).await.unwrap(); - let res = lazy(|cx| srv.poll_ready(cx)).await; - assert!(res.is_ready()); - - let res = srv.call("srv").await; - assert!(res.is_ok()); - assert_eq!(res.unwrap(), ("srv", ())); - } -} diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index 27a09684..8db6018f 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -1,11 +1,12 @@ -use std::{ +use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; -use futures_util::ready; +use futures_core::ready; +use pin_project_lite::pin_project; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; @@ -94,9 +95,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - Poll::Ready(ready!(self.service.poll_ready(cx))) - } + crate::forward_ready!(service); fn call(&mut self, req: Req) -> Self::Future { (self.wrap_fn)(req, &mut self.service) @@ -162,17 +161,18 @@ where } } -#[pin_project::pin_project] -pub struct ApplyServiceFactoryResponse -where - SF: ServiceFactory, - F: FnMut(Req, &mut SF::Service) -> Fut, - Fut: Future>, -{ - #[pin] - fut: SF::Future, - wrap_fn: Option, - _phantom: PhantomData<(Req, Res)>, +pin_project! { + pub struct ApplyServiceFactoryResponse + where + SF: ServiceFactory, + F: FnMut(Req, &mut SF::Service) -> Fut, + Fut: Future>, + { + #[pin] + fut: SF::Future, + wrap_fn: Option, + _phantom: PhantomData<(Req, Res)>, + } } impl ApplyServiceFactoryResponse @@ -203,13 +203,13 @@ where let this = self.project(); let svc = ready!(this.fut.poll(cx))?; - Poll::Ready(Ok(Apply::new(svc, Option::take(this.wrap_fn).unwrap()))) + Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap()))) } } #[cfg(test)] mod tests { - use std::task::{Context, Poll}; + use core::task::Poll; use futures_util::future::{lazy, ok, Ready}; @@ -224,9 +224,7 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { ok(()) diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index da24e87d..3e111231 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -1,9 +1,13 @@ -use std::cell::RefCell; -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use crate::{Service, ServiceFactory}; @@ -156,37 +160,42 @@ where ApplyConfigServiceFactoryResponse { cfg: Some(cfg), store: self.srv.clone(), - state: State::A(self.srv.borrow().0.new_service(())), + state: State::A { + fut: self.srv.borrow().0.new_service(()), + }, } } } -#[pin_project::pin_project] -struct ApplyConfigServiceFactoryResponse -where - SF: ServiceFactory, - SF::InitError: From, - F: FnMut(Cfg, &mut SF::Service) -> Fut, - Fut: Future>, - S: Service, -{ - cfg: Option, - store: Rc>, - #[pin] - state: State, +pin_project! { + struct ApplyConfigServiceFactoryResponse + where + SF: ServiceFactory, + SF::InitError: From, + F: FnMut(Cfg, &mut SF::Service) -> Fut, + Fut: Future>, + S: Service, + { + cfg: Option, + store: Rc>, + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - SF: ServiceFactory, - SF::InitError: From, - Fut: Future>, - S: Service, -{ - A(#[pin] SF::Future), - B(SF::Service), - C(#[pin] Fut), +pin_project! { + #[project = StateProj] + enum State + where + SF: ServiceFactory, + SF::InitError: From, + Fut: Future>, + S: Service, + { + A { #[pin] fut: SF::Future }, + B { svc: SF::Service }, + C { #[pin] fut: Fut }, + } } impl Future @@ -204,25 +213,25 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut) => match fut.poll(cx)? { + StateProj::A { fut } => match fut.poll(cx)? { Poll::Pending => Poll::Pending, - Poll::Ready(srv) => { - this.state.set(State::B(srv)); + Poll::Ready(svc) => { + this.state.set(State::B { svc }); self.poll(cx) } }, - StateProj::B(srv) => match srv.poll_ready(cx)? { + StateProj::B { svc } => match svc.poll_ready(cx)? { Poll::Ready(_) => { { let (_, f) = &mut *this.store.borrow_mut(); - let fut = f(this.cfg.take().unwrap(), srv); - this.state.set(State::C(fut)); + let fut = f(this.cfg.take().unwrap(), svc); + this.state.set(State::C { fut }); } self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::C(fut) => fut.poll(cx), + StateProj::C { fut } => fut.poll(cx), } } } diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 35a10dac..203d575c 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,6 +1,10 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use alloc::boxed::Box; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; use futures_util::future::FutureExt; @@ -28,7 +32,7 @@ where { BoxServiceFactory(Box::new(FactoryWrapper { factory, - _t: std::marker::PhantomData, + _t: PhantomData, })) } @@ -75,12 +79,9 @@ where } } -struct FactoryWrapper -where - SF: ServiceFactory, -{ +struct FactoryWrapper { factory: SF, - _t: PhantomData<(C, Req)>, + _t: PhantomData<(Req, Cfg)>, } impl ServiceFactory for FactoryWrapper diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 7d15304d..59792564 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -1,6 +1,4 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::task::{Context, Poll}; +use core::{future::Future, marker::PhantomData, task::Poll}; use futures_util::future::{ok, Ready}; @@ -143,9 +141,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, req: Req) -> Self::Future { (self.f)(req) @@ -200,9 +196,7 @@ where type Error = Err; type Future = Fut; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, req: Req) -> Self::Future { (self.f)(req) @@ -361,7 +355,7 @@ where #[cfg(test)] mod tests { - use std::task::Poll; + use core::task::Poll; use futures_util::future::{lazy, ok}; diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 2dfa0dd7..d66d5221 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -1,18 +1,21 @@ //! See [`Service`] docs for information on this crate's foundational trait. +#![no_std] #![deny(rust_2018_idioms, nonstandard_style)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::cell::RefCell; -use std::future::Future; -use std::rc::Rc; -use std::sync::Arc; -use std::task::{self, Context, Poll}; +extern crate alloc; + +use alloc::{boxed::Box, rc::Rc, sync::Arc}; +use core::{ + cell::RefCell, + future::Future, + task::{self, Context, Poll}, +}; mod and_then; -mod and_then_apply_fn; mod apply; mod apply_cfg; pub mod boxed; @@ -359,3 +362,27 @@ pub mod dev { pub use crate::transform::ApplyTransform; pub use crate::transform_err::TransformMapInitErr; } + +#[macro_export] +macro_rules! always_ready { + () => { + fn poll_ready( + &mut self, + _: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + Poll::Ready(Ok(())) + } + }; +} + +#[macro_export] +macro_rules! forward_ready { + ($field:ident) => { + fn poll_ready( + &mut self, + cx: &mut ::core::task::Context<'_>, + ) -> ::core::task::Poll> { + self.$field.poll_ready(cx) + } + }; +} diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index 04ef8c5f..a8afa25f 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -52,24 +56,23 @@ where type Error = A::Error; type Future = MapFuture; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(ctx) - } + crate::forward_ready!(service); fn call(&mut self, req: Req) -> Self::Future { MapFuture::new(self.service.call(req), self.f.clone()) } } -#[pin_project::pin_project] -pub struct MapFuture -where - A: Service, - F: FnMut(A::Response) -> Res, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapFuture + where + A: Service, + F: FnMut(A::Response) -> Res, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapFuture @@ -154,15 +157,16 @@ where } } -#[pin_project::pin_project] -pub struct MapServiceFuture -where - A: ServiceFactory, - F: FnMut(A::Response) -> Res, -{ - #[pin] - fut: A::Future, - f: Option, +pin_project! { + pub struct MapServiceFuture + where + A: ServiceFactory, + F: FnMut(A::Response) -> Res, + { + #[pin] + fut: A::Future, + f: Option, + } } impl MapServiceFuture @@ -207,9 +211,7 @@ mod tests { type Error = (); type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + crate::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { ok(()) diff --git a/actix-service/src/map_config.rs b/actix-service/src/map_config.rs index 82b1789b..d6d6f6b2 100644 --- a/actix-service/src/map_config.rs +++ b/actix-service/src/map_config.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use super::{IntoServiceFactory, ServiceFactory}; diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index ae7442cc..f0bf134b 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -62,15 +66,16 @@ where } } -#[pin_project::pin_project] -pub struct MapErrFuture -where - A: Service, - F: Fn(A::Error) -> E, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapErrFuture + where + A: Service, + F: Fn(A::Error) -> E, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapErrFuture @@ -157,15 +162,16 @@ where } } -#[pin_project::pin_project] -pub struct MapErrServiceFuture -where - A: ServiceFactory, - F: Fn(A::Error) -> E, -{ - #[pin] - fut: A::Future, - f: F, +pin_project! { + pub struct MapErrServiceFuture + where + A: ServiceFactory, + F: Fn(A::Error) -> E, + { + #[pin] + fut: A::Future, + f: F, + } } impl MapErrServiceFuture diff --git a/actix-service/src/map_init_err.rs b/actix-service/src/map_init_err.rs index 518daaf6..9fc383aa 100644 --- a/actix-service/src/map_init_err.rs +++ b/actix-service/src/map_init_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::ServiceFactory; @@ -59,15 +63,16 @@ where } } -#[pin_project::pin_project] -pub struct MapInitErrFuture -where - A: ServiceFactory, - F: Fn(A::InitError) -> E, -{ - f: F, - #[pin] - fut: A::Future, +pin_project! { + pub struct MapInitErrFuture + where + A: ServiceFactory, + F: Fn(A::InitError) -> E, + { + f: F, + #[pin] + fut: A::Future, + } } impl MapInitErrFuture diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs index cba7ce78..580d7b4c 100644 --- a/actix-service/src/pipeline.rs +++ b/actix-service/src/pipeline.rs @@ -1,8 +1,9 @@ -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use core::{ + marker::PhantomData, + task::{Context, Poll}, +}; use crate::and_then::{AndThenService, AndThenServiceFactory}; -use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory}; use crate::map::{Map, MapServiceFactory}; use crate::map_err::{MapErr, MapErrServiceFactory}; use crate::map_init_err::MapInitErr; @@ -67,28 +68,6 @@ where } } - /// Apply function to specified service and use it as a next service in chain. - /// - /// Short version of `pipeline_factory(...).and_then(apply_fn(...))` - pub fn and_then_apply_fn( - self, - service: I, - wrap_fn: F, - ) -> Pipeline + Clone, Req> - where - Self: Sized, - I: IntoService, - S1: Service, - F: FnMut(S::Response, &mut S1) -> Fut, - Fut: Future>, - Err: From + From, - { - Pipeline { - service: AndThenApplyFn::new(self.service, service.into_service(), wrap_fn), - _phantom: PhantomData, - } - } - /// Chain on a computation for when a call to the service finished, /// passing the result of the call to the next service `U`. /// @@ -219,39 +198,6 @@ where } } - /// Apply function to specified service and use it as a next service in chain. - /// - /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` - pub fn and_then_apply_fn( - self, - factory: I, - wrap_fn: F, - ) -> PipelineFactory< - impl ServiceFactory< - Req, - Response = Res, - Error = Err, - Config = SF::Config, - InitError = SF::InitError, - Service = impl Service + Clone, - > + Clone, - Req, - > - where - Self: Sized, - SF::Config: Clone, - I: IntoServiceFactory, - SF1: ServiceFactory, - F: FnMut(SF::Response, &mut SF1::Service) -> Fut + Clone, - Fut: Future>, - Err: From + From, - { - PipelineFactory { - factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), wrap_fn), - _phantom: PhantomData, - } - } - /// Create `NewService` to chain on a computation for when a call to the /// service finished, passing the result of the call to the next /// service `U`. diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 021e5484..179713ac 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,8 +1,13 @@ -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{cell::RefCell, marker::PhantomData}; +use alloc::rc::Rc; +use core::{ + cell::RefCell, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; @@ -50,30 +55,36 @@ where fn call(&mut self, req: Req) -> Self::Future { ThenServiceResponse { - state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())), + state: State::A { + fut: self.0.borrow_mut().0.call(req), + b: Some(self.0.clone()), + }, } } } -#[pin_project::pin_project] -pub(crate) struct ThenServiceResponse -where - A: Service, - B: Service>, -{ - #[pin] - state: State, +pin_project! { + pub(crate) struct ThenServiceResponse + where + A: Service, + B: Service>, + { + #[pin] + state: State, + } } -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service>, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty, +pin_project! { + #[project = StateProj] + enum State + where + A: Service, + B: Service>, + { + A { #[pin] fut: A::Future, b: Option>> }, + B { #[pin] fut: B::Future }, + Empty, + } } impl Future for ThenServiceResponse @@ -87,17 +98,17 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx) { + StateProj::A { fut, b } => match fut.poll(cx) { Poll::Ready(res) => { let b = b.take().unwrap(); this.state.set(State::Empty); // drop fut A let fut = b.borrow_mut().1.call(res); - this.state.set(State::B(fut)); + this.state.set(State::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - StateProj::B(fut) => fut.poll(cx).map(|r| { + StateProj::B { fut } => fut.poll(cx).map(|r| { this.state.set(State::Empty); r }), @@ -159,23 +170,24 @@ impl Clone for ThenServiceFactory { } } -#[pin_project::pin_project] -pub(crate) struct ThenServiceFactoryResponse -where - A: ServiceFactory, - B: ServiceFactory< - Result, - Config = A::Config, - Error = A::Error, - InitError = A::InitError, - >, -{ - #[pin] - fut_b: B::Future, - #[pin] - fut_a: A::Future, - a: Option, - b: Option, +pin_project! { + pub(crate) struct ThenServiceFactoryResponse + where + A: ServiceFactory, + B: ServiceFactory< + Result, + Config = A::Config, + Error = A::Error, + InitError = A::InitError, + >, + { + #[pin] + fut_b: B::Future, + #[pin] + fut_a: A::Future, + a: Option, + b: Option, + } } impl ThenServiceFactoryResponse @@ -236,9 +248,11 @@ where #[cfg(test)] mod tests { - use std::cell::Cell; - use std::rc::Rc; - use std::task::{Context, Poll}; + use alloc::rc::Rc; + use core::{ + cell::Cell, + task::{Context, Poll}, + }; use futures_util::future::{err, lazy, ok, ready, Ready}; diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index d4d49417..76e4547a 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,8 +1,12 @@ -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::{future::Future, marker::PhantomData}; +use alloc::{rc::Rc, sync::Arc}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use crate::transform_err::TransformMapInitErr; use crate::{IntoServiceFactory, Service, ServiceFactory}; @@ -185,30 +189,35 @@ where fn new_service(&self, cfg: S::Config) -> Self::Future { ApplyTransformFuture { store: self.0.clone(), - state: ApplyTransformFutureState::A(self.0.as_ref().1.new_service(cfg)), + state: ApplyTransformFutureState::A { + fut: self.0.as_ref().1.new_service(cfg), + }, } } } -#[pin_project::pin_project] -pub struct ApplyTransformFuture -where - S: ServiceFactory, - T: Transform, -{ - store: Rc<(T, S)>, - #[pin] - state: ApplyTransformFutureState, +pin_project! { + pub struct ApplyTransformFuture + where + S: ServiceFactory, + T: Transform, + { + store: Rc<(T, S)>, + #[pin] + state: ApplyTransformFutureState, + } } -#[pin_project::pin_project(project = ApplyTransformFutureStateProj)] -pub enum ApplyTransformFutureState -where - S: ServiceFactory, - T: Transform, -{ - A(#[pin] S::Future), - B(#[pin] T::Future), +pin_project! { + #[project = ApplyTransformFutureStateProj] + pub enum ApplyTransformFutureState + where + S: ServiceFactory, + T: Transform, + { + A { #[pin] fut: S::Future }, + B { #[pin] fut: T::Future }, + } } impl Future for ApplyTransformFuture @@ -222,15 +231,15 @@ where let mut this = self.as_mut().project(); match this.state.as_mut().project() { - ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? { + ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? { Poll::Ready(srv) => { let fut = this.store.0.new_transform(srv); - this.state.set(ApplyTransformFutureState::B(fut)); + this.state.set(ApplyTransformFutureState::B { fut }); self.poll(cx) } Poll::Pending => Poll::Pending, }, - ApplyTransformFutureStateProj::B(fut) => fut.poll(cx), + ApplyTransformFutureStateProj::B { fut } => fut.poll(cx), } } } diff --git a/actix-service/src/transform_err.rs b/actix-service/src/transform_err.rs index 1d1b9576..cbf5fe3b 100644 --- a/actix-service/src/transform_err.rs +++ b/actix-service/src/transform_err.rs @@ -1,7 +1,11 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; +use core::{ + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use pin_project_lite::pin_project; use super::Transform; @@ -63,15 +67,16 @@ where } } -#[pin_project::pin_project] -pub struct TransformMapInitErrFuture -where +pin_project! { + pub struct TransformMapInitErrFuture + where T: Transform, F: Fn(T::InitError) -> E, -{ - #[pin] - fut: T::Future, - f: F, + { + #[pin] + fut: T::Future, + f: F, + } } impl Future for TransformMapInitErrFuture diff --git a/actix-tracing/src/lib.rs b/actix-tracing/src/lib.rs index 36aa21f2..6d37d9b3 100644 --- a/actix-tracing/src/lib.rs +++ b/actix-tracing/src/lib.rs @@ -4,8 +4,7 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] -use std::marker::PhantomData; -use std::task::{Context, Poll}; +use core::marker::PhantomData; use actix_service::{ apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, @@ -36,9 +35,7 @@ where type Error = S::Error; type Future = Either>; - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(ctx) - } + actix_service::forward_ready!(inner); fn call(&mut self, req: Req) -> Self::Future { let span = (self.make_span)(&req); diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index 17647206..a27e9ffb 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -201,7 +201,7 @@ where #[cfg(test)] mod tests { - use std::task::{Context, Poll}; + use std::task::Poll; use std::time::Duration; use super::*; @@ -215,9 +215,7 @@ mod tests { type Error = (); type Future = LocalBoxFuture<'static, Result<(), ()>>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } + actix_service::always_ready!(); fn call(&mut self, _: ()) -> Self::Future { actix_rt::time::delay_for(self.0)