-
Notifications
You must be signed in to change notification settings - Fork 818
Description
Hi! I wanted to continue the issue of tokio-rs/tokio#3789
I used the following code as reproduction:
use tokio::io::AsyncReadExt;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let listener = tokio::net::TcpListener::bind("0.0.0.0:2222").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1];
let _ = socket.read(&mut buf).await;
let _ = socket.peek(&mut buf).await;
});
}
}So I don't know much about Tokio internals, but I spent the last couple of hours debugging.
I found that the waker for the Readiness future used by peek is never called after there was one call to read. Digging deeper the GetQueuedCompletionStatusEx is blocking when issuing the peek after read.
I think this is because Windows doesn't know we are interested by READABLE because peek never registers the fact that it has the READABLE interest at the OS level. It does register the interest at the ScheduledIO level but we never get there obviously because the thread is parked.
When we issue a read we use the IoSourceState which reregisters our interests with the IOCP if it would block, but when we peek we directly use the STD IO. See:
Lines 210 to 212 in 08ee541
| pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { | |
| self.inner.peek(buf) | |
| } |
I think the fix is to use IoSourceState::do_io when we peek, but I must test it.