Repository: rust
Issue: windows: `TcpStream::shutdown` does not wake up blocking reads
Labels: O-windows,T-libs-api,A-docs,C-bug,A-io

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->

### Description

The [docs for `TcpStream::shutdown`](https://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.shutdown) currently guarantee that a shutdown with `Shutdown::Read` must wake up *pending* operations *immediately*:

> This function will cause all pending and future I/O on the specified portions to return immediately with an appropriate value (see the documentation of [Shutdown](https://doc.rust-lang.org/nightly/std/net/enum.Shutdown.html)).

The [docs for `Shutdown::Read`](https://doc.rust-lang.org/nightly/std/net/enum.Shutdown.html#variant.Read) explicitly says that *currently blocked reads* must return:

> All currently blocked and future [reads](https://doc.rust-lang.org/nightly/std/io/trait.Read.html) will return [Ok](https://doc.rust-lang.org/nightly/std/result/enum.Result.html#variant.Ok)(0).

However, that is currently not the case on `*-pc-windows-*` platforms. Instead, a *currently blocked read* will stay blocked forever when shutdown is called with `Shutdown::Read` in a different thread. The same also happens with `Shutdown::Both`.

This is causing the [`close_read_wakes_up`](https://github.com/rust-lang/rust/blob/26cd5d862e22c013ecb3396b177d3af80e95c836/library/std/src/net/tcp/tests.rs#L548) test to fail spuriously due to a race condition, for example in https://github.com/rust-lang/rust/pull/121523#issuecomment-1962581666 and https://github.com/rust-lang/rust/pull/120543#issuecomment-1925748911. The test only passes if the `shutdown` happens before the `read`.

### Repro

I tried this code:

```rust
use std::io::Read;
use std::net::{Ipv6Addr, Shutdown, TcpListener, TcpStream};
use std::thread;
use std::time::Duration;

fn main() {
    let listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 0)).unwrap();
    let listener_addr = listener.local_addr().unwrap();
    let mut serverbound_stream = None;
    thread::scope(|scope| {
        // 1. `accept` and `connect` concurrently
        let _clientbound_stream = scope.spawn(|| {
            let (clientbound_stream, _) = listener.accept().unwrap();
            clientbound_stream
        });
        let serverbound_stream =
            serverbound_stream.insert(TcpStream::connect(listener_addr).unwrap());

        // 3. shutdown read during blocking read
        scope.spawn(|| {
            thread::sleep(Duration::from_secs(1)); // just to be sure
            serverbound_stream.shutdown(Shutdown::Read).unwrap();
        });

        // 2. blocking read
        let count = (&*serverbound_stream).read(&mut [0]).unwrap();
        assert_eq!(count, 0);
    });
}
```

I expected to see this happen: The `.shutdown(Shutdown::Read)` makes the blocking read return immediately with `Ok(0)`.

Instead, this happened: The blocking read does not return, making the program hang indefinitely.

### Meta
<!--
If you're using the stable version of the compiler, you should also check if the
bug also exists in the beta or nightly versions.
-->

`rustc --version --verbose`:
```
rustc 1.78.0-nightly (381d69953 2024-02-24)
binary: rustc
commit-hash: 381d69953bb7c3390cec0fee200f24529cb6320f
commit-date: 2024-02-24
host: x86_64-unknown-linux-gnu
release: 1.78.0-nightly
LLVM version: 18.1.0
```

Tested on these platforms:
* `x86_64-pc-windows-msvc` with Microsoft Windows Server 2019 (GitHub CI)
* `x86_64-pc-windows-gnu` with wine 9.1 on Linux

### Related

* https://github.com/rust-lang/rust/pull/38867 disabled the test due to "We don't document this as a guarantee for `shutdown`", except that we do and also already did when that PR was opened.
* https://github.com/rust-lang/rust/pull/94714 enabled the test again as "works on my machine".