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 futures::{Async, AsyncSink, Poll, StartSend};
|
||||
use futures::task::Task;
|
||||
use http::{Method, Version};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
@@ -40,7 +39,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
keep_alive: KA::Busy,
|
||||
method: None,
|
||||
title_case_headers: false,
|
||||
read_task: None,
|
||||
notify_read: false,
|
||||
reading: Reading::Init,
|
||||
writing: Writing::Init,
|
||||
// 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());
|
||||
|
||||
if self.is_mid_message() {
|
||||
self.maybe_park_read();
|
||||
} else {
|
||||
if !self.is_mid_message() {
|
||||
self.require_empty_read().map_err(::Error::new_io)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -253,20 +250,11 @@ where I: AsyncRead + AsyncWrite,
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_park_read(&mut self) {
|
||||
if !self.io.is_read_blocked() {
|
||||
// the Io object is ready to read, which means it will never alert
|
||||
// us that it is ready until we drain it. However, we're currently
|
||||
// finished reading, so we need to park the task to be able to
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
pub fn wants_read_again(&mut self) -> bool {
|
||||
let ret = self.state.notify_read;
|
||||
self.state.notify_read = false;
|
||||
trace!("wants_read_again? {}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
// 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
|
||||
// determined we couldn't keep reading until we knew how writing
|
||||
// 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::KeepAlive => return,
|
||||
Reading::Init => true,
|
||||
Reading::Closed => false,
|
||||
Reading::KeepAlive |
|
||||
Reading::Closed => return,
|
||||
Reading::Init => (),
|
||||
};
|
||||
|
||||
match self.state.writing {
|
||||
@@ -361,7 +346,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
}
|
||||
|
||||
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() {
|
||||
Ok(Async::Ready(_)) => (),
|
||||
Ok(Async::NotReady) => {
|
||||
@@ -374,12 +359,7 @@ where I: AsyncRead + AsyncWrite,
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref task) = self.state.read_task {
|
||||
trace!("maybe_notify; notifying task");
|
||||
task.notify();
|
||||
} else {
|
||||
trace!("maybe_notify; no task to notify");
|
||||
}
|
||||
self.state.notify_read = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +595,7 @@ struct State {
|
||||
keep_alive: KA,
|
||||
method: Option<Method>,
|
||||
title_case_headers: bool,
|
||||
read_task: Option<Task>,
|
||||
notify_read: bool,
|
||||
reading: Reading,
|
||||
writing: Writing,
|
||||
version: Version,
|
||||
@@ -645,7 +625,6 @@ impl fmt::Debug for State {
|
||||
.field("error", &self.error)
|
||||
//.field("method", &self.method)
|
||||
//.field("title_case_headers", &self.title_case_headers)
|
||||
.field("read_task", &self.read_task)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -713,7 +692,6 @@ impl State {
|
||||
fn close_read(&mut self) {
|
||||
trace!("State::close_read()");
|
||||
self.reading = Reading::Closed;
|
||||
self.read_task = None;
|
||||
self.keep_alive.disable();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,9 +88,23 @@ where
|
||||
}
|
||||
|
||||
fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
|
||||
self.poll_read()?;
|
||||
self.poll_write()?;
|
||||
self.poll_flush()?;
|
||||
loop {
|
||||
self.poll_read()?;
|
||||
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 should_shutdown {
|
||||
|
||||
Reference in New Issue
Block a user