mirror of https://github.com/fafhrd91/actix-net
convert AndThen to use RefCell
This commit is contained in:
parent
693d5132a9
commit
1efafd4d02
|
@ -10,7 +10,7 @@ use actix_service::IntoService;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use futures_util::future::TryFutureExt;
|
use futures_util::future::TryFutureExt;
|
||||||
use actix_service::boxed::BoxFuture;
|
use actix_service::{boxed::BoxFuture, pipeline};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -319,6 +319,7 @@ pub fn service_benches() {
|
||||||
bench_async_service(&mut criterion, AndThenUC::new(svc1.into_service(), svc2.into_service()), "AndThen with UnsafeCell");
|
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, 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");
|
bench_async_service(&mut criterion, AndThenRCFuture::new(svc1.into_service(), svc2.into_service()), "AndThen with RefCell via future::and_then");
|
||||||
|
bench_async_service(&mut criterion, pipeline(svc1.into_service()).and_then(svc2.into_service()), "Pipeline::and_then based RC<RefCell>");
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_main!(service_benches);
|
criterion_main!(service_benches);
|
||||||
|
|
|
@ -4,13 +4,13 @@ use std::rc::Rc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use super::{Service, ServiceFactory};
|
use super::{Service, ServiceFactory};
|
||||||
use crate::cell::Cell;
|
use crate::axcell::AXCell;
|
||||||
|
|
||||||
/// Service for the `and_then` combinator, chaining a computation onto the end
|
/// Service for the `and_then` combinator, chaining a computation onto the end
|
||||||
/// of another service which completes successfully.
|
/// of another service which completes successfully.
|
||||||
///
|
///
|
||||||
/// This is created by the `ServiceExt::and_then` method.
|
/// This is created by the `ServiceExt::and_then` method.
|
||||||
pub(crate) struct AndThenService<A, B>(Cell<(A, B)>);
|
pub(crate) struct AndThenService<A, B>(AXCell<(A, B)>);
|
||||||
|
|
||||||
impl<A, B> AndThenService<A, B> {
|
impl<A, B> AndThenService<A, B> {
|
||||||
/// Create new `AndThen` combinator
|
/// Create new `AndThen` combinator
|
||||||
|
@ -19,7 +19,7 @@ impl<A, B> AndThenService<A, B> {
|
||||||
A: Service,
|
A: Service,
|
||||||
B: Service<Request = A::Response, Error = A::Error>,
|
B: Service<Request = A::Response, Error = A::Error>,
|
||||||
{
|
{
|
||||||
Self(Cell::new((a, b)))
|
Self(AXCell::new((a, b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ where
|
||||||
type Future = AndThenServiceResponse<A, B>;
|
type Future = AndThenServiceResponse<A, B>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
let srv = self.0.get_mut();
|
let mut srv = self.0.get_mut();
|
||||||
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
|
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
|
||||||
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
|
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
|
@ -50,8 +50,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: A::Request) -> Self::Future {
|
fn call(&mut self, req: A::Request) -> Self::Future {
|
||||||
|
let fut = self.0.get_mut().0.call(req);
|
||||||
AndThenServiceResponse {
|
AndThenServiceResponse {
|
||||||
state: State::A(self.0.get_mut().0.call(req), Some(self.0.clone())),
|
state: State::A(fut, Some(self.0.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ where
|
||||||
A: Service,
|
A: Service,
|
||||||
B: Service<Request = A::Response, Error = A::Error>,
|
B: Service<Request = A::Response, Error = A::Error>,
|
||||||
{
|
{
|
||||||
A(#[pin] A::Future, Option<Cell<(A, B)>>),
|
A(#[pin] A::Future, Option<AXCell<(A, B)>>),
|
||||||
B(#[pin] B::Future),
|
B(#[pin] B::Future),
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
@ -257,9 +258,11 @@ mod tests {
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use futures_util::future::{lazy, ok, ready, Ready};
|
use futures_util::future::{lazy, ok, ready, Ready, join_all, Future, FutureExt};
|
||||||
|
use actix_rt::time::delay_for;
|
||||||
|
|
||||||
use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory};
|
use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory};
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
struct Srv1(Rc<Cell<usize>>);
|
struct Srv1(Rc<Cell<usize>>);
|
||||||
|
|
||||||
|
@ -298,6 +301,32 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct SrvSlow(Rc<Cell<usize>>);
|
||||||
|
|
||||||
|
impl Service for SrvSlow {
|
||||||
|
type Request = &'static str;
|
||||||
|
type Response = &'static str;
|
||||||
|
type Error = ();
|
||||||
|
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>>>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.0.set(self.0.get() + 1);
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, _: &'static str) -> Self::Future {
|
||||||
|
let cnt = self.0.clone();
|
||||||
|
Box::pin(
|
||||||
|
delay_for(std::time::Duration::from_millis(10))
|
||||||
|
.then(|()| async move {
|
||||||
|
cnt.set(cnt.get() + 1);
|
||||||
|
Ok("slow")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_poll_ready() {
|
async fn test_poll_ready() {
|
||||||
let cnt = Rc::new(Cell::new(0));
|
let cnt = Rc::new(Cell::new(0));
|
||||||
|
@ -329,4 +358,33 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(res.unwrap(), ("srv1", "srv2"));
|
assert_eq!(res.unwrap(), ("srv1", "srv2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_concurrency_slow_first() {
|
||||||
|
let cnt = Rc::new(Cell::new(0));
|
||||||
|
let mut srv = pipeline(SrvSlow(cnt.clone())).and_then(Srv1(cnt.clone()));
|
||||||
|
let futures = (1u16..=1000).map(|_| srv.call("req"));
|
||||||
|
let res = join_all(futures).await;
|
||||||
|
assert!(res.iter().all(|r| r.is_ok()));
|
||||||
|
assert_eq!(cnt.get(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_concurrency_slow_last() {
|
||||||
|
let cnt = Rc::new(Cell::new(0));
|
||||||
|
let mut srv = pipeline(Srv1(cnt.clone())).and_then(SrvSlow(cnt.clone()));
|
||||||
|
let futures = (1u16..=1000).map(|_| srv.call("req"));
|
||||||
|
let res = join_all(futures).await;
|
||||||
|
assert!(res.iter().all(|r| r.is_ok()));
|
||||||
|
assert_eq!(cnt.get(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_concurrency_fast() {
|
||||||
|
let cnt = Rc::new(Cell::new(0));
|
||||||
|
let mut srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone()));
|
||||||
|
let futures = (1u16..=1000).map(|_| srv.call("req"));
|
||||||
|
let res = join_all(futures).await;
|
||||||
|
assert!(res.iter().all(|r| r.is_ok()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
//! Custom cell impl, internal use only
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use std::{cell::{RefCell, RefMut}, fmt, rc::Rc};
|
||||||
|
|
||||||
|
pub(crate) struct AXCell<T> {
|
||||||
|
inner: Rc<RefCell<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for AXCell<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for AXCell<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.inner.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AXCell<T> {
|
||||||
|
pub(crate) fn new(inner: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Rc::new(RefCell::new(inner)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mut(&mut self) -> RefMut<'_, T> {
|
||||||
|
self.inner.borrow_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: crate::Service> crate::Service for AXCell<T> {
|
||||||
|
type Request = T::Request;
|
||||||
|
type Response = T::Response;
|
||||||
|
type Error = T::Error;
|
||||||
|
type Future = T::Future;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.get_mut().poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||||
|
self.get_mut().call(req)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ mod apply;
|
||||||
mod apply_cfg;
|
mod apply_cfg;
|
||||||
pub mod boxed;
|
pub mod boxed;
|
||||||
mod cell;
|
mod cell;
|
||||||
|
mod axcell;
|
||||||
mod fn_service;
|
mod fn_service;
|
||||||
mod map;
|
mod map;
|
||||||
mod map_config;
|
mod map_config;
|
||||||
|
|
Loading…
Reference in New Issue