Skip to content

Commit 31c10a0

Browse files
committed
Try blocking reads first to avoid thundering herd problem
Linux 5.6 and up optimize the wakeups of pipe readers when pipes have multiple readers and those readers are blocking. Always polling defeats that optimization. torvalds/linux@0ddad21
1 parent 9d5e6da commit 31c10a0

File tree

1 file changed

+21
-16
lines changed

1 file changed

+21
-16
lines changed

src/unix.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,11 @@ impl Client {
122122
// fds are set to nonblocking and combined with `pselect`
123123
// internally.
124124
//
125-
// Here we try to be compatible with both strategies. We
126-
// unconditionally expect the file descriptor to be in nonblocking
127-
// mode and if it happens to be in blocking mode then most of this
128-
// won't end up actually being necessary!
125+
// Here we try to be compatible with both strategies. We optimistically
126+
// try to read from the file descriptor which then may block, return
127+
// a token or indicate that polling is needed.
128+
// Blocking reads (if possible) allows the kernel to be more selective
129+
// about which readers to wake up when a token is written to the pipe.
129130
//
130131
// We use `poll` here to block this thread waiting for read
131132
// readiness, and then afterwards we perform the `read` itself. If
@@ -139,17 +140,6 @@ impl Client {
139140
fd.fd = self.read.as_raw_fd();
140141
fd.events = libc::POLLIN;
141142
loop {
142-
fd.revents = 0;
143-
if libc::poll(&mut fd, 1, -1) == -1 {
144-
let e = io::Error::last_os_error();
145-
match e.kind() {
146-
io::ErrorKind::Interrupted => return Ok(None),
147-
_ => return Err(e),
148-
}
149-
}
150-
if fd.revents == 0 {
151-
continue;
152-
}
153143
let mut buf = [0];
154144
match (&self.read).read(&mut buf) {
155145
Ok(1) => return Ok(Some(Acquired { byte: buf[0] })),
@@ -160,10 +150,25 @@ impl Client {
160150
))
161151
}
162152
Err(e) => match e.kind() {
163-
io::ErrorKind::WouldBlock | io::ErrorKind::Interrupted => return Ok(None),
153+
io::ErrorKind::WouldBlock => { /* fall through to polling */ }
154+
io::ErrorKind::Interrupted => return Ok(None),
164155
_ => return Err(e),
165156
},
166157
}
158+
159+
loop {
160+
fd.revents = 0;
161+
if libc::poll(&mut fd, 1, -1) == -1 {
162+
let e = io::Error::last_os_error();
163+
return match e.kind() {
164+
io::ErrorKind::Interrupted => Ok(None),
165+
_ => Err(e),
166+
}
167+
}
168+
if fd.revents != 0 {
169+
break;
170+
}
171+
}
167172
}
168173
}
169174
}

0 commit comments

Comments
 (0)