mirror of
https://github.com/fafhrd91/actix-net
synced 2025-08-31 03:57:00 +02:00
Migrate actix-net to std::future (#64)
* Migrate actix-codec, actix-rt, and actix-threadpool to std::future * update to latest tokio alpha and futures-rs * Migrate actix-service to std::future, This is a squash of ~8 commits, since it included a lot of experimentation. To see the commits, look into the semtexzv/std-future-service-tmp branch. * update futures-rs and tokio * Migrate actix-threadpool to std::future (#59) * Migrate actix-threadpool to std::future * Cosmetic refactor - turn log::error! into log::warn! as it doesn't throw any error - add Clone and Copy impls for Cancelled making it cheap to operate with - apply rustfmt * Bump up crate version to 0.2.0 and pre-fill its changelog * Disable patching 'actix-threadpool' crate in global workspace as unnecessary * Revert patching and fix 'actix-rt' * Migrate actix-rt to std::future (#47) * remove Pin from Service::poll_ready(); simplify combinators api; make code compile * disable tests * update travis config * refactor naming * drop IntoFuture trait * Migrate actix-server to std::future (#50) Still not finished, this is more WIP, this is an aggregation of several commits, which can be found in semtexzv/std-future-server-tmp branch * update actix-server * rename Factor to ServiceFactory * start server worker in start mehtod * update actix-utils * remove IntoTransform trait * Migrate actix-server::ssl::nativetls to std futures (#61) * Refactor 'nativetls' module * Migrate 'actix-server-config' to std futures - remove "uds" feature - disable features by default * Switch NativeTlsAcceptor to use 'tokio-tls' crate * Bikeshed features names and remove unnecessary dependencies for 'actix-server-config' crate * update openssl impl * migrate actix-connect to std::future * migrate actix-ioframe to std::future * update version to alpha.1 * fix boxed service * migrate server rustls support * migratte openssl and rustls connecttors * store the thread's handle with arbiter (#62) * update ssl connect tests * restore service tests * update readme
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{mem, time};
|
||||
|
||||
use actix_rt::{spawn, Arbiter};
|
||||
use futures::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures::sync::oneshot;
|
||||
use futures::{future, Async, Future, Poll, Stream};
|
||||
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::{join_all, LocalBoxFuture, MapOk};
|
||||
use futures::{Future, FutureExt, Stream, TryFutureExt};
|
||||
use log::{error, info, trace};
|
||||
use tokio_timer::{sleep, Delay};
|
||||
use tokio_timer::{delay, Delay};
|
||||
|
||||
use crate::accept::AcceptNotify;
|
||||
use crate::counter::Counter;
|
||||
use crate::services::{BoxedServerService, InternalServiceFactory, ServerMessage};
|
||||
use crate::service::{BoxedServerService, InternalServiceFactory, ServerMessage};
|
||||
use crate::socket::{SocketAddr, StdStream};
|
||||
use crate::Token;
|
||||
|
||||
@@ -153,31 +156,38 @@ impl Worker {
|
||||
state: WorkerState::Unavailable(Vec::new()),
|
||||
});
|
||||
|
||||
let mut fut = Vec::new();
|
||||
let mut fut: Vec<MapOk<LocalBoxFuture<'static, _>, _>> = Vec::new();
|
||||
for (idx, factory) in wrk.factories.iter().enumerate() {
|
||||
fut.push(factory.create().map(move |res| {
|
||||
res.into_iter()
|
||||
.map(|(t, s)| (idx, t, s))
|
||||
fut.push(factory.create().map_ok(move |r| {
|
||||
r.into_iter()
|
||||
.map(|(t, s): (Token, _)| (idx, t, s))
|
||||
.collect::<Vec<_>>()
|
||||
}));
|
||||
}
|
||||
|
||||
spawn(
|
||||
future::join_all(fut)
|
||||
.map_err(|e| {
|
||||
error!("Can not start worker: {:?}", e);
|
||||
Arbiter::current().stop();
|
||||
})
|
||||
.and_then(move |services| {
|
||||
for item in services {
|
||||
for (idx, token, service) in item {
|
||||
while token.0 >= wrk.services.len() {
|
||||
wrk.services.push(None);
|
||||
async move {
|
||||
let res = join_all(fut).await;
|
||||
let res: Result<Vec<_>, _> = res.into_iter().collect();
|
||||
match res {
|
||||
Ok(services) => {
|
||||
for item in services {
|
||||
for (idx, token, service) in item {
|
||||
while token.0 >= wrk.services.len() {
|
||||
wrk.services.push(None);
|
||||
}
|
||||
wrk.services[token.0] = Some((idx, service));
|
||||
}
|
||||
wrk.services[token.0] = Some((idx, service));
|
||||
}
|
||||
}
|
||||
wrk
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Can not start worker: {:?}", e);
|
||||
Arbiter::current().stop();
|
||||
}
|
||||
}
|
||||
wrk.await
|
||||
}
|
||||
.boxed_local(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -198,13 +208,17 @@ impl Worker {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_readiness(&mut self, trace: bool) -> Result<bool, (Token, usize)> {
|
||||
let mut ready = self.conns.available();
|
||||
fn check_readiness(
|
||||
&mut self,
|
||||
trace: bool,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Result<bool, (Token, usize)> {
|
||||
let mut ready = self.conns.available(cx);
|
||||
let mut failed = None;
|
||||
for (token, service) in &mut self.services.iter_mut().enumerate() {
|
||||
if let Some(service) = service {
|
||||
match service.1.poll_ready() {
|
||||
Ok(Async::Ready(_)) => {
|
||||
match service.1.poll_ready(cx) {
|
||||
Poll::Ready(Ok(_)) => {
|
||||
if trace {
|
||||
trace!(
|
||||
"Service {:?} is available",
|
||||
@@ -212,8 +226,8 @@ impl Worker {
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(Async::NotReady) => ready = false,
|
||||
Err(_) => {
|
||||
Poll::Pending => ready = false,
|
||||
Poll::Ready(Err(_)) => {
|
||||
error!(
|
||||
"Service {:?} readiness check returned error, restarting",
|
||||
self.factories[service.0].name(Token(token))
|
||||
@@ -238,43 +252,44 @@ enum WorkerState {
|
||||
Restarting(
|
||||
usize,
|
||||
Token,
|
||||
Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>>,
|
||||
Pin<Box<dyn Future<Output = Result<Vec<(Token, BoxedServerService)>, ()>>>>,
|
||||
),
|
||||
Shutdown(Delay, Delay, oneshot::Sender<bool>),
|
||||
}
|
||||
|
||||
impl Future for Worker {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
type Output = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// `StopWorker` message handler
|
||||
if let Ok(Async::Ready(Some(StopCommand { graceful, result }))) = self.rx2.poll() {
|
||||
if let Poll::Ready(Some(StopCommand { graceful, result })) =
|
||||
Pin::new(&mut self.rx2).poll_next(cx)
|
||||
{
|
||||
self.availability.set(false);
|
||||
let num = num_connections();
|
||||
if num == 0 {
|
||||
info!("Shutting down worker, 0 connections");
|
||||
let _ = result.send(true);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
} else if graceful {
|
||||
self.shutdown(false);
|
||||
let num = num_connections();
|
||||
if num != 0 {
|
||||
info!("Graceful worker shutdown, {} connections", num);
|
||||
self.state = WorkerState::Shutdown(
|
||||
sleep(time::Duration::from_secs(1)),
|
||||
sleep(self.shutdown_timeout),
|
||||
delay(time::Instant::now() + time::Duration::from_secs(1)),
|
||||
delay(time::Instant::now() + self.shutdown_timeout),
|
||||
result,
|
||||
);
|
||||
} else {
|
||||
let _ = result.send(true);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
} else {
|
||||
info!("Force shutdown worker, {} connections", num);
|
||||
self.shutdown(true);
|
||||
let _ = result.send(false);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,13 +297,13 @@ impl Future for Worker {
|
||||
|
||||
match state {
|
||||
WorkerState::Unavailable(mut conns) => {
|
||||
match self.check_readiness(true) {
|
||||
match self.check_readiness(true, cx) {
|
||||
Ok(true) => {
|
||||
self.state = WorkerState::Available;
|
||||
|
||||
// process requests from wait queue
|
||||
while let Some(msg) = conns.pop() {
|
||||
match self.check_readiness(false) {
|
||||
match self.check_readiness(false, cx) {
|
||||
Ok(true) => {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[msg.token.0]
|
||||
@@ -300,7 +315,7 @@ impl Future for Worker {
|
||||
Ok(false) => {
|
||||
trace!("Worker is unavailable");
|
||||
self.state = WorkerState::Unavailable(conns);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Err((token, idx)) => {
|
||||
trace!(
|
||||
@@ -312,16 +327,16 @@ impl Future for Worker {
|
||||
token,
|
||||
self.factories[idx].create(),
|
||||
);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.availability.set(true);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Ok(false) => {
|
||||
self.state = WorkerState::Unavailable(conns);
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Err((token, idx)) => {
|
||||
trace!(
|
||||
@@ -330,13 +345,13 @@ impl Future for Worker {
|
||||
);
|
||||
self.state =
|
||||
WorkerState::Restarting(idx, token, self.factories[idx].create());
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
WorkerState::Restarting(idx, token, mut fut) => {
|
||||
match fut.poll() {
|
||||
Ok(Async::Ready(item)) => {
|
||||
match Pin::new(&mut fut).poll(cx) {
|
||||
Poll::Ready(Ok(item)) => {
|
||||
for (token, service) in item {
|
||||
trace!(
|
||||
"Service {:?} has been restarted",
|
||||
@@ -346,55 +361,55 @@ impl Future for Worker {
|
||||
self.state = WorkerState::Unavailable(Vec::new());
|
||||
}
|
||||
}
|
||||
Ok(Async::NotReady) => {
|
||||
self.state = WorkerState::Restarting(idx, token, fut);
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
Err(_) => {
|
||||
Poll::Ready(Err(_)) => {
|
||||
panic!(
|
||||
"Can not restart {:?} service",
|
||||
self.factories[idx].name(token)
|
||||
);
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.state = WorkerState::Restarting(idx, token, fut);
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
WorkerState::Shutdown(mut t1, mut t2, tx) => {
|
||||
let num = num_connections();
|
||||
if num == 0 {
|
||||
let _ = tx.send(true);
|
||||
Arbiter::current().stop();
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
// check graceful timeout
|
||||
match t2.poll().unwrap() {
|
||||
Async::NotReady => (),
|
||||
Async::Ready(_) => {
|
||||
match Pin::new(&mut t2).poll(cx) {
|
||||
Poll::Pending => (),
|
||||
Poll::Ready(_) => {
|
||||
self.shutdown(true);
|
||||
let _ = tx.send(false);
|
||||
Arbiter::current().stop();
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
// sleep for 1 second and then check again
|
||||
match t1.poll().unwrap() {
|
||||
Async::NotReady => (),
|
||||
Async::Ready(_) => {
|
||||
t1 = sleep(time::Duration::from_secs(1));
|
||||
let _ = t1.poll();
|
||||
match Pin::new(&mut t1).poll(cx) {
|
||||
Poll::Pending => (),
|
||||
Poll::Ready(_) => {
|
||||
t1 = delay(time::Instant::now() + time::Duration::from_secs(1));
|
||||
let _ = Pin::new(&mut t1).poll(cx);
|
||||
}
|
||||
}
|
||||
self.state = WorkerState::Shutdown(t1, t2, tx);
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
WorkerState::Available => {
|
||||
loop {
|
||||
match self.rx.poll() {
|
||||
match Pin::new(&mut self.rx).poll_next(cx) {
|
||||
// handle incoming tcp stream
|
||||
Ok(Async::Ready(Some(WorkerCommand(msg)))) => {
|
||||
match self.check_readiness(false) {
|
||||
Poll::Ready(Some(WorkerCommand(msg))) => {
|
||||
match self.check_readiness(false, cx) {
|
||||
Ok(true) => {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[msg.token.0]
|
||||
@@ -422,13 +437,13 @@ impl Future for Worker {
|
||||
);
|
||||
}
|
||||
}
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Ok(Async::NotReady) => {
|
||||
Poll::Pending => {
|
||||
self.state = WorkerState::Available;
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())),
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user