// run-fail
// only-windows

fn main() {
    use std::fs;
    use std::io::prelude::*;
    use std::os::windows::prelude::*;
    use std::ptr;
    use std::sync::Arc;
    use std::thread;
    use std::time::Duration;

    const FILE_FLAG_OVERLAPPED: u32 = 0x40000000;

    fn create_pipe_server(path: &str) -> fs::File {
        let mut path0 = path.as_bytes().to_owned();
        path0.push(0);
        extern "system" {
            fn CreateNamedPipeA(
                lpName: *const u8,
                dwOpenMode: u32,
                dwPipeMode: u32,
                nMaxInstances: u32,
                nOutBufferSize: u32,
                nInBufferSize: u32,
                nDefaultTimeOut: u32,
                lpSecurityAttributes: *mut u8,
            ) -> RawHandle;
        }

        unsafe {
            let h = CreateNamedPipeA(path0.as_ptr(), 3, 0, 1, 0, 0, 0, ptr::null_mut());
            assert_ne!(h as isize, -1);
            fs::File::from_raw_handle(h)
        }
    }

    let path = "\\\\.\\pipe\\repro";
    let mut server = create_pipe_server(path);

    let client = Arc::new(
        fs::OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).read(true).open(path).unwrap(),
    );

    let spawn_read = |is_first: bool| {
        thread::spawn({
            let f = client.clone();
            move || {
                let mut buf = [0xcc; 1];
                let mut f = f.as_ref();
                f.read(&mut buf).unwrap();
                if is_first {
                    assert_ne!(buf[0], 0xcc);
                } else {
                    let b = buf[0]; // capture buf[0]
                    thread::sleep(Duration::from_millis(200));

                    // Check the buffer hasn't been written to after read.
                    dbg!(buf[0], b);
                    assert_eq!(buf[0], b);
                }
            }
        })
    };

    let t1 = spawn_read(true);
    thread::sleep(Duration::from_millis(20));
    let t2 = spawn_read(false);
    thread::sleep(Duration::from_millis(100));
    let _ = server.write(b"x");
    thread::sleep(Duration::from_millis(100));
    let _ = server.write(b"y");

    // This is run fail because we need to test for the `abort`.
    // That failing to run is the success case.
    if t1.join().is_err() || t2.join().is_err() {
        return;
    } else {
        panic!("success");
    }
}
