Skip to content

peek blocks after read on windows #1755

@Sytten

Description

@Sytten

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:

mio/src/net/tcp/stream.rs

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions