Skip to content

Commit f61d606

Browse files
carllercheNoah-Kennedy
authored andcommitted
io: reduce syscalls in poll_read
As the [epoll documentation points out](https://man7.org/linux/man-pages/man7/epoll.7.html), a read that only partially fills a buffer is sufficient to show that the socket buffer has been drained. We can take advantage of this by clearing readiness in this case, which seems to significantly improve performance under certain conditions.
1 parent ffff9e6 commit f61d606

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

tokio/src/io/driver/registration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ impl Registration {
115115

116116
// Uses the poll path, requiring the caller to ensure mutual exclusion for
117117
// correctness. Only the last task to call this function is notified.
118+
#[cfg(not(all(target_arch = "wasm32", target_os = "wasi")))]
118119
pub(crate) fn poll_read_io<R>(
119120
&self,
120121
cx: &mut Context<'_>,

tokio/src/io/poll_evented.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,32 @@ feature! {
153153
{
154154
use std::io::Read;
155155

156-
let n = ready!(self.registration.poll_read_io(cx, || {
156+
loop {
157+
let evt = ready!(self.registration.poll_read_ready(cx))?;
158+
157159
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
158-
self.io.as_ref().unwrap().read(b)
159-
}))?;
160-
161-
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
162-
// buffer.
163-
buf.assume_init(n);
164-
buf.advance(n);
165-
Poll::Ready(Ok(()))
160+
let len = b.len();
161+
162+
match self.io.as_ref().unwrap().read(b) {
163+
Ok(n) => {
164+
// if we read a partially full buffer, this is sufficient on unix to show
165+
// that the socket buffer has been drained
166+
if n > 0 && (!cfg!(windows) && n < len) {
167+
self.registration.clear_readiness(evt);
168+
}
169+
170+
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
171+
// buffer.
172+
buf.assume_init(n);
173+
buf.advance(n);
174+
return Poll::Ready(Ok(()));
175+
},
176+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
177+
self.registration.clear_readiness(evt);
178+
}
179+
Err(e) => return Poll::Ready(Err(e)),
180+
}
181+
}
166182
}
167183

168184
pub(crate) fn poll_write<'a>(&'a self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>>

0 commit comments

Comments
 (0)