every IO realted futures polled 7 extra times after returning WOULDBLOCK
- Dominant language
- Rust
- Stars
- 4.1k
- Forks
- 339
- PR merge metrics
- No merged PRs in 30d
Description
I was using `Watcher` to warp my own async io. It seems every time `inner.recvmsg(buf)` return `WouldBlock`, `inner.recvmsg(buf)` is called 8 times.
```
recv 1 packets
size: 84, bytes: [69, 0, 0, 84, 94, 226, 0, 0, 64, 1, 134, 115, 192, 168, 10, 1, 192, 168, 10, 2, 8, 0, 37, 235, 12, 127, 0, 12, 93, 228, 172, 117, 0, 11, 208, 33, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55]
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg: [84]
recv 1 packets
size: 84, bytes: [69, 0, 0, 84, 53, 22, 0, 0, 64, 1, 176, 63, 192, 168, 10, 1, 192, 168, 10, 2, 8, 0, 20, 9, 12, 127, 0, 13, 93, 228, 172, 118, 0, 11, 226, 1, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55]
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
recvmmsg error: Resource temporarily unavailable (os error 35), WouldBlock
```
```rust
pub(crate) struct TunSocket {
mtu: usize,
name: String,
watcher: Watcher,
}
#[allow(dead_code)]
impl TunSocket {
pub fn new(name: &str) -> TunSocket {
let watcher = Watcher::new(sys::TunSocket::new(name).expect("TunSocket::new"));
TunSocket {
name: watcher.get_ref().name().expect("get name"),
mtu: watcher.get_ref().mtu().expect("get mut"),
watcher,
}
}
pub fn poll_recvmsg(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> {
self.watcher.poll_read_with(cx, |inner| inner.recvmsg(buf))
}
}
```
After digging into `Watcher`'s code, i find out runtime will try yield 3 times until park. And in each `watcher.poll_read_with`, future is tried twice.
https://github.com/async-rs/async-std/blob/128a6bc6ce98810c3ef519c9902a1a5f3b746a99/src/task/executor/pool.rs#L107-L129
```rust
pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll>
where
F: FnMut(&'a T) -> io::Result,
{
// If the operation isn't blocked, return its result.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Lock the waker list.
let mut list = self.entry.readers.lock().unwrap();
// Try running the operation again.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Register the task if it isn't registered already.
if list.iter().all(|w| !w.will_wake(cx.waker())) {
eprintln!("add waker");
list.push(cx.waker().clone());
}
Poll::Pending
}
```
Contributor guide
Assessment
This issue has not been assessed yet.