use std::cmp;
use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::io;
use std::mem;
use std::os::windows::prelude::*;
use std::ptr;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use windows_sys::Win32::Foundation::BOOL;
use windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED;
use windows_sys::Win32::Foundation::ERROR_BROKEN_PIPE;
use windows_sys::Win32::Foundation::ERROR_HANDLE_EOF;
use windows_sys::Win32::Foundation::ERROR_IO_PENDING;
use windows_sys::Win32::Foundation::FALSE;
use windows_sys::Win32::Foundation::GENERIC_READ;
use windows_sys::Win32::Foundation::GENERIC_WRITE;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::Foundation::TRUE;
use windows_sys::Win32::Foundation::WAIT_OBJECT_0;
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_FIRST_PIPE_INSTANCE;
use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED;
use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING;
use windows_sys::Win32::Storage::FileSystem::PIPE_ACCESS_INBOUND;
use windows_sys::Win32::Storage::FileSystem::PIPE_ACCESS_OUTBOUND;
use windows_sys::Win32::Storage::FileSystem::ReadFile;
use windows_sys::Win32::System::IO::CancelIo;
use windows_sys::Win32::System::IO::GetOverlappedResult;
use windows_sys::Win32::System::IO::OVERLAPPED;
use windows_sys::Win32::System::Pipes::CreateNamedPipeW;
use windows_sys::Win32::System::Pipes::PIPE_READMODE_BYTE;
use windows_sys::Win32::System::Pipes::PIPE_REJECT_REMOTE_CLIENTS;
use windows_sys::Win32::System::Pipes::PIPE_TYPE_BYTE;
use windows_sys::Win32::System::Pipes::PIPE_WAIT;
use windows_sys::Win32::System::Threading::CreateEventW;
use windows_sys::Win32::System::Threading::GetCurrentProcessId;
use windows_sys::Win32::System::Threading::INFINITE;
use windows_sys::Win32::System::Threading::WaitForMultipleObjects;
pub type Handle = std::os::windows::io::OwnedHandle;
pub struct AnonPipe {
inner: Handle,
}
impl AnonPipe {
}
impl FromRawHandle for AnonPipe {
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
AnonPipe {
inner: unsafe { Handle::from_raw_handle(handle) },
}
}
}
fn get_last_error() -> u32 {
unsafe { GetLastError() }
}
pub struct Pipes {
pub ours: AnonPipe,
pub theirs: AnonPipe,
}
fn cvt(res: BOOL) -> io::Result<()> {
if res == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn anon_pipe(
ours_readable: bool,
their_handle_inheritable: bool,
) -> io::Result<Pipes> {
const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
unsafe {
let ours;
let mut name;
let mut tries = 0;
loop {
tries += 1;
name = format!(
r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
GetCurrentProcessId(),
random_number(),
);
let wide_name = OsStr::new(&name)
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
let mut flags = FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED;
if ours_readable {
flags |= PIPE_ACCESS_INBOUND;
} else {
flags |= PIPE_ACCESS_OUTBOUND;
}
let handle = CreateNamedPipeW(
wide_name.as_ptr(),
flags,
PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
| PIPE_REJECT_REMOTE_CLIENTS,
1,
PIPE_BUFFER_CAPACITY,
PIPE_BUFFER_CAPACITY,
0,
ptr::null_mut(),
);
if handle == INVALID_HANDLE_VALUE {
let error = get_last_error();
if tries < 10 && error == ERROR_ACCESS_DENIED {
continue;
} else {
return Err(io::Error::from_raw_os_error(error as i32));
}
}
ours = Handle::from_raw_handle(handle);
break;
}
#[allow(clippy::disallowed_methods, reason = "requires real OpenOptions")]
let mut opts = OpenOptions::new();
opts.write(ours_readable);
opts.read(!ours_readable);
opts.share_mode(0);
let access = if ours_readable {
GENERIC_WRITE
} else {
GENERIC_READ
};
let size = size_of::<SECURITY_ATTRIBUTES>();
let sa = SECURITY_ATTRIBUTES {
nLength: size as u32,
lpSecurityDescriptor: ptr::null_mut(),
bInheritHandle: their_handle_inheritable as i32,
};
let path_utf16 = OsStr::new(&name)
.encode_wide()
.chain(Some(0))
.collect::<Vec<_>>();
let handle2 = CreateFileW(
path_utf16.as_ptr(),
access,
0,
&sa,
OPEN_EXISTING,
0,
ptr::null_mut(),
);
let theirs = Handle::from_raw_handle(handle2);
Ok(Pipes {
ours: AnonPipe { inner: ours },
theirs: AnonPipe { inner: theirs },
})
}
}
fn random_number() -> usize {
static N: std::sync::atomic::AtomicUsize = AtomicUsize::new(0);
loop {
if N.load(Relaxed) != 0 {
return N.fetch_add(1, Relaxed);
}
N.store(fastrand::usize(..), Relaxed);
}
}
impl AnonPipe {
pub fn into_handle(self) -> Handle {
self.inner
}
}
pub fn read2(
p1: AnonPipe,
v1: &mut Vec<u8>,
p2: AnonPipe,
v2: &mut Vec<u8>,
) -> io::Result<()> {
let p1 = p1.into_handle();
let p2 = p2.into_handle();
let mut p1 = AsyncPipe::new(p1, v1)?;
let mut p2 = AsyncPipe::new(p2, v2)?;
let objs = [p1.event.as_raw_handle(), p2.event.as_raw_handle()];
loop {
let res =
unsafe { WaitForMultipleObjects(2, objs.as_ptr(), FALSE, INFINITE) };
if res == WAIT_OBJECT_0 {
if !p1.result()? || !p1.schedule_read()? {
return p2.finish();
}
} else if res == WAIT_OBJECT_0 + 1 {
if !p2.result()? || !p2.schedule_read()? {
return p1.finish();
}
} else {
return Err(io::Error::last_os_error());
}
}
}
struct AsyncPipe<'a> {
pipe: Handle,
event: Handle,
overlapped: Box<OVERLAPPED>, dst: &'a mut Vec<u8>,
state: State,
}
#[derive(PartialEq, Debug)]
enum State {
NotReading,
Reading,
Read(usize),
}
impl<'a> AsyncPipe<'a> {
fn new(pipe: Handle, dst: &'a mut Vec<u8>) -> io::Result<AsyncPipe<'a>> {
let event = new_event(true, true)?;
let mut overlapped: Box<OVERLAPPED> = unsafe { Box::new(mem::zeroed()) };
overlapped.hEvent = event.as_raw_handle();
Ok(AsyncPipe {
pipe,
overlapped,
event,
dst,
state: State::NotReading,
})
}
fn schedule_read(&mut self) -> io::Result<bool> {
assert_eq!(self.state, State::NotReading);
let amt = unsafe {
if self.dst.capacity() == self.dst.len() {
let additional = if self.dst.capacity() == 0 { 16 } else { 1 };
self.dst.reserve(additional);
}
read_overlapped(
&self.pipe,
self.dst.spare_capacity_mut(),
&mut *self.overlapped,
)?
};
self.state = match amt {
Some(0) => return Ok(false),
Some(amt) => State::Read(amt),
None => State::Reading,
};
Ok(true)
}
fn result(&mut self) -> io::Result<bool> {
let amt = match self.state {
State::NotReading => return Ok(true),
State::Reading => {
overlapped_result(&self.pipe, &mut *self.overlapped, true)?
}
State::Read(amt) => amt,
};
self.state = State::NotReading;
unsafe {
let len = self.dst.len();
self.dst.set_len(len + amt);
}
Ok(amt != 0)
}
fn finish(&mut self) -> io::Result<()> {
while self.result()? && self.schedule_read()? {
}
Ok(())
}
}
impl Drop for AsyncPipe<'_> {
fn drop(&mut self) {
match self.state {
State::Reading => {}
_ => return,
}
if cancel_io(&self.pipe).is_err() || self.result().is_err() {
let buf = mem::take(self.dst);
let overlapped = Box::new(unsafe { mem::zeroed() });
let overlapped = mem::replace(&mut self.overlapped, overlapped);
mem::forget((buf, overlapped));
}
}
}
pub fn cancel_io(handle: &Handle) -> io::Result<()> {
unsafe { cvt(CancelIo(handle.as_raw_handle())) }
}
pub fn overlapped_result(
handle: &Handle,
overlapped: *mut OVERLAPPED,
wait: bool,
) -> io::Result<usize> {
unsafe {
let mut bytes = 0;
let wait = if wait { TRUE } else { FALSE };
let res = cvt(GetOverlappedResult(
handle.as_raw_handle(),
overlapped,
&mut bytes,
wait,
));
match res {
Ok(_) => Ok(bytes as usize),
Err(e) => {
if e.raw_os_error() == Some(ERROR_HANDLE_EOF as i32)
|| e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32)
{
Ok(0)
} else {
Err(e)
}
}
}
}
}
pub unsafe fn read_overlapped(
handle: &Handle,
buf: &mut [mem::MaybeUninit<u8>],
overlapped: *mut OVERLAPPED,
) -> io::Result<Option<usize>> {
let (res, amt) = unsafe {
let len = cmp::min(buf.len(), u32::MAX as usize) as u32;
let mut amt = 0;
let res = cvt(ReadFile(
handle.as_raw_handle(),
buf.as_mut_ptr().cast::<u8>(),
len,
&mut amt,
overlapped,
));
(res, amt)
};
match res {
Ok(_) => Ok(Some(amt as usize)),
Err(e) => {
if e.raw_os_error() == Some(ERROR_IO_PENDING as i32) {
Ok(None)
} else if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
Ok(Some(0))
} else {
Err(e)
}
}
}
}
pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> {
unsafe {
let event =
CreateEventW(ptr::null_mut(), manual as BOOL, init as BOOL, ptr::null());
if event.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(Handle::from_raw_handle(event))
}
}
}