perf(server): reduce task system wake up in new dispatcher
This commit is contained in:
@@ -4,7 +4,6 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
use futures::{Async, AsyncSink, Poll, StartSend};
|
use futures::{Async, AsyncSink, Poll, StartSend};
|
||||||
use futures::task::Task;
|
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
keep_alive: KA::Busy,
|
keep_alive: KA::Busy,
|
||||||
method: None,
|
method: None,
|
||||||
title_case_headers: false,
|
title_case_headers: false,
|
||||||
read_task: None,
|
notify_read: false,
|
||||||
reading: Reading::Init,
|
reading: Reading::Init,
|
||||||
writing: Writing::Init,
|
writing: Writing::Init,
|
||||||
// We assume a modern world where the remote speaks HTTP/1.1.
|
// We assume a modern world where the remote speaks HTTP/1.1.
|
||||||
@@ -238,9 +237,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
|
|
||||||
trace!("read_keep_alive; is_mid_message={}", self.is_mid_message());
|
trace!("read_keep_alive; is_mid_message={}", self.is_mid_message());
|
||||||
|
|
||||||
if self.is_mid_message() {
|
if !self.is_mid_message() {
|
||||||
self.maybe_park_read();
|
|
||||||
} else {
|
|
||||||
self.require_empty_read().map_err(::Error::new_io)?;
|
self.require_empty_read().map_err(::Error::new_io)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -253,20 +250,11 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_park_read(&mut self) {
|
pub fn wants_read_again(&mut self) -> bool {
|
||||||
if !self.io.is_read_blocked() {
|
let ret = self.state.notify_read;
|
||||||
// the Io object is ready to read, which means it will never alert
|
self.state.notify_read = false;
|
||||||
// us that it is ready until we drain it. However, we're currently
|
trace!("wants_read_again? {}", ret);
|
||||||
// finished reading, so we need to park the task to be able to
|
ret
|
||||||
// wake back up later when more reading should happen.
|
|
||||||
let park = self.state.read_task.as_ref()
|
|
||||||
.map(|t| !t.will_notify_current())
|
|
||||||
.unwrap_or(true);
|
|
||||||
if park {
|
|
||||||
trace!("parking current task");
|
|
||||||
self.state.read_task = Some(::futures::task::current());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will check to make sure the io object read is empty.
|
// This will check to make sure the io object read is empty.
|
||||||
@@ -340,17 +328,14 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
// exhausted the underlying Io. We would have done this when we
|
// exhausted the underlying Io. We would have done this when we
|
||||||
// determined we couldn't keep reading until we knew how writing
|
// determined we couldn't keep reading until we knew how writing
|
||||||
// would finish.
|
// would finish.
|
||||||
//
|
|
||||||
// When writing finishes, we need to wake the task up in case there
|
|
||||||
// is more reading that can be done, to start a new message.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let wants_read = match self.state.reading {
|
match self.state.reading {
|
||||||
Reading::Body(..) |
|
Reading::Body(..) |
|
||||||
Reading::KeepAlive => return,
|
Reading::KeepAlive |
|
||||||
Reading::Init => true,
|
Reading::Closed => return,
|
||||||
Reading::Closed => false,
|
Reading::Init => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.state.writing {
|
match self.state.writing {
|
||||||
@@ -361,7 +346,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.io.is_read_blocked() {
|
if !self.io.is_read_blocked() {
|
||||||
if wants_read && self.io.read_buf().is_empty() {
|
if self.io.read_buf().is_empty() {
|
||||||
match self.io.read_from_io() {
|
match self.io.read_from_io() {
|
||||||
Ok(Async::Ready(_)) => (),
|
Ok(Async::Ready(_)) => (),
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::NotReady) => {
|
||||||
@@ -374,12 +359,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ref task) = self.state.read_task {
|
self.state.notify_read = true;
|
||||||
trace!("maybe_notify; notifying task");
|
|
||||||
task.notify();
|
|
||||||
} else {
|
|
||||||
trace!("maybe_notify; no task to notify");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +595,7 @@ struct State {
|
|||||||
keep_alive: KA,
|
keep_alive: KA,
|
||||||
method: Option<Method>,
|
method: Option<Method>,
|
||||||
title_case_headers: bool,
|
title_case_headers: bool,
|
||||||
read_task: Option<Task>,
|
notify_read: bool,
|
||||||
reading: Reading,
|
reading: Reading,
|
||||||
writing: Writing,
|
writing: Writing,
|
||||||
version: Version,
|
version: Version,
|
||||||
@@ -645,7 +625,6 @@ impl fmt::Debug for State {
|
|||||||
.field("error", &self.error)
|
.field("error", &self.error)
|
||||||
//.field("method", &self.method)
|
//.field("method", &self.method)
|
||||||
//.field("title_case_headers", &self.title_case_headers)
|
//.field("title_case_headers", &self.title_case_headers)
|
||||||
.field("read_task", &self.read_task)
|
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,7 +692,6 @@ impl State {
|
|||||||
fn close_read(&mut self) {
|
fn close_read(&mut self) {
|
||||||
trace!("State::close_read()");
|
trace!("State::close_read()");
|
||||||
self.reading = Reading::Closed;
|
self.reading = Reading::Closed;
|
||||||
self.read_task = None;
|
|
||||||
self.keep_alive.disable();
|
self.keep_alive.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,23 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
|
fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
|
||||||
self.poll_read()?;
|
loop {
|
||||||
self.poll_write()?;
|
self.poll_read()?;
|
||||||
self.poll_flush()?;
|
self.poll_write()?;
|
||||||
|
self.poll_flush()?;
|
||||||
|
|
||||||
|
// This could happen if reading paused before blocking on IO,
|
||||||
|
// such as getting to the end of a framed message, but then
|
||||||
|
// writing/flushing set the state back to Init. In that case,
|
||||||
|
// if the read buffer still had bytes, we'd want to try poll_read
|
||||||
|
// again, or else we wouldn't ever be woken up again.
|
||||||
|
//
|
||||||
|
// Using this instead of task::current() and notify() inside
|
||||||
|
// the Conn is noticeably faster in pipelined benchmarks.
|
||||||
|
if !self.conn.wants_read_again() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.is_done() {
|
if self.is_done() {
|
||||||
if should_shutdown {
|
if should_shutdown {
|
||||||
|
|||||||
Reference in New Issue
Block a user