use std::ffi::OsStr;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
use std::ptr::{self, NonNull};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::{Arc, Mutex};
use std::{cmp, fmt, iter, mem};
use windows_sys::Win32::Foundation::{
ERROR_BROKEN_PIPE, ERROR_IO_PENDING, ERROR_MORE_DATA, ERROR_NOT_FOUND, ERROR_NO_DATA,
ERROR_PIPE_CONNECTED, ERROR_PIPE_LISTENING, HANDLE, INVALID_HANDLE_VALUE,
};
use windows_sys::Win32::Storage::FileSystem::{
GetFileType, ReadFile, WriteFile, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED,
FILE_TYPE_PIPE, PIPE_ACCESS_DUPLEX,
};
use windows_sys::Win32::System::Pipes::{
ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
};
use windows_sys::Win32::System::IO::{
CancelIoEx, GetOverlappedResult, OVERLAPPED, OVERLAPPED_ENTRY,
};
use crate::event::Source;
use crate::sys::windows::iocp::{CompletionPort, CompletionStatus};
use crate::sys::windows::{Event, Handle, Overlapped};
use crate::Registry;
use crate::{Interest, Token};
pub struct NamedPipe {
inner: Arc<Inner>,
}
#[repr(C)]
struct Inner {
event: Overlapped,
handle: Handle,
io: Mutex<Io>,
max_read_size: usize,
}
unsafe impl Send for Inner {}
unsafe impl Sync for Inner {}
impl Inner {
unsafe fn connect_overlapped(&self, overlapped: *mut OVERLAPPED) -> io::Result<()> {
if ConnectNamedPipe(self.handle.raw(), overlapped) == 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(ERROR_IO_PENDING as i32) {
return Err(err);
}
}
Ok(())
}
fn disconnect(&self) -> io::Result<()> {
if unsafe { DisconnectNamedPipe(self.handle.raw()) } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
unsafe fn read_overlapped(
&self,
buf: *mut u8,
len: usize,
overlapped: *mut OVERLAPPED,
) -> io::Result<()> {
let len = cmp::min(len, u32::MAX as usize) as u32;
if ReadFile(
self.handle.raw(),
buf.cast(),
len,
ptr::null_mut(),
overlapped,
) == 0
{
let err = io::Error::last_os_error();
match err.raw_os_error().map(|e| e as u32) {
Some(ERROR_IO_PENDING | ERROR_MORE_DATA) => {}
_ => return Err(err),
}
}
Ok(())
}
unsafe fn write_overlapped(
&self,
buf: *const u8,
len: usize,
overlapped: *mut OVERLAPPED,
) -> io::Result<()> {
let len = cmp::min(len, u32::MAX as usize) as u32;
if WriteFile(
self.handle.raw(),
buf.cast(),
len,
ptr::null_mut(),
overlapped,
) == 0
{
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(ERROR_IO_PENDING as i32) {
return Err(err);
}
}
Ok(())
}
#[inline]
fn result(&self, op: &mut Op) -> io::Result<usize> {
let mut transferred = 0;
let r = unsafe {
GetOverlappedResult(self.handle.raw(), Op::overlapped(op), &mut transferred, 0)
};
if r == 0 {
Err(io::Error::last_os_error())
} else {
Ok(transferred as usize)
}
}
fn cancel(&self, op_id: OpId) {
let overlapped = op_id.0.as_ptr().cast::<OVERLAPPED>();
if unsafe { CancelIoEx(self.handle.raw(), overlapped) } == 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(ERROR_NOT_FOUND as i32) {
error!("error cancelling named pipe operation: {err}");
}
}
}
}
fn is_connected(err: &io::Error) -> bool {
matches!(
err.raw_os_error().map(|e| e as u32),
Some(ERROR_PIPE_CONNECTED | ERROR_NO_DATA)
)
}
#[repr(C)]
struct Op {
overlapped: Overlapped,
pipe: Option<Arc<Inner>>,
buf: Vec<u8>,
}
const DEFAULT_READ_SIZE: usize = 4 * 1024;
const MAX_READ_SIZE: usize = 64 * 1024;
impl Op {
fn new() -> Box<Op> {
Box::new(Op {
overlapped: Overlapped::new(read_done),
pipe: None,
buf: Vec::new(),
})
}
fn id(&self) -> OpId {
OpId(NonNull::from(self))
}
fn overlapped(op: *mut Op) -> *mut OVERLAPPED {
op.cast()
}
unsafe fn complete(overlapped: *mut OVERLAPPED) -> (Box<Op>, Arc<Inner>) {
let mut op = Box::from_raw(overlapped.cast::<Op>());
let pipe = op
.pipe
.take()
.expect("operation in flight without its pipe");
(op, pipe)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct OpId(NonNull<Op>);
unsafe impl Send for OpId {}
const SPARE_OPS: usize = 2;
const MAX_IDLE_BUF_SIZE: usize = 1024 * 1024;
struct Io {
cp: Option<Arc<CompletionPort>>,
token: Option<Token>,
connect: Option<OpId>,
connect_error: Option<io::Error>,
read: ReadState,
read_size: usize,
write: WriteState,
spare: [Option<Box<Op>>; SPARE_OPS],
}
enum ReadState {
Idle,
InFlight(OpId),
Ready(Box<Op>, usize),
Failed(io::Error),
}
enum WriteState {
Idle,
InFlight(OpId, usize),
Failed(io::Error),
}
static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(1);
fn would_block() -> io::Error {
io::ErrorKind::WouldBlock.into()
}
fn not_registered() -> io::Error {
io::Error::new(
io::ErrorKind::NotFound,
"I/O source not registered with `Registry`",
)
}
impl NamedPipe {
pub fn new<A: AsRef<OsStr>>(addr: A) -> io::Result<NamedPipe> {
use std::os::windows::ffi::OsStrExt;
let name: Vec<_> = addr.as_ref().encode_wide().chain(Some(0)).collect();
let h = unsafe {
CreateNamedPipeW(
name.as_ptr(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
65536,
65536,
0,
std::ptr::null_mut(),
)
};
if h == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(unsafe { Self::from_raw_handle(h as RawHandle) })
}
}
pub fn connect(&self) -> io::Result<()> {
let mut io = self.inner.io.lock().unwrap();
if io.connect.is_some() {
return Err(would_block());
}
if io.token.is_none() {
return Err(not_registered());
}
let op = io.op();
let res = unsafe {
io.submit(op, &self.inner, connect_done, |op| {
self.inner.connect_overlapped(Op::overlapped(op))
})
};
match res {
Ok(op_id) => {
io.connect = Some(op_id);
Err(would_block())
}
Err(ref e) if is_connected(e) => {
Inner::post_register(&self.inner, &mut io, None);
Ok(())
}
Err(e) => Err(e),
}
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
Ok(self.inner.io.lock().unwrap().connect_error.take())
}
pub fn disconnect(&self) -> io::Result<()> {
let mut io = self.inner.io.lock().unwrap();
self.inner.disconnect()?;
io.connect_error = None;
if let Some(op_id) = io.connect.take() {
self.inner.cancel(op_id);
}
match mem::replace(&mut io.read, ReadState::Idle) {
ReadState::InFlight(op_id) => self.inner.cancel(op_id),
ReadState::Ready(op, _) => io.recycle(op),
ReadState::Idle | ReadState::Failed(_) => {}
}
match mem::replace(&mut io.write, WriteState::Idle) {
WriteState::InFlight(op_id, _) => self.inner.cancel(op_id),
WriteState::Idle | WriteState::Failed(_) => {}
}
Ok(())
}
}
impl FromRawHandle for NamedPipe {
unsafe fn from_raw_handle(handle: RawHandle) -> NamedPipe {
let max_read_size = if GetFileType(handle as HANDLE) == FILE_TYPE_PIPE {
MAX_READ_SIZE
} else {
DEFAULT_READ_SIZE
};
NamedPipe {
inner: Arc::new(Inner {
event: Overlapped::new(event_done),
handle: Handle::new(handle as HANDLE),
io: Mutex::new(Io {
cp: None,
token: None,
connect: None,
connect_error: None,
read: ReadState::Idle,
read_size: DEFAULT_READ_SIZE,
write: WriteState::Idle,
spare: [None, None],
}),
max_read_size,
}),
}
}
}
impl Read for NamedPipe {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
<&NamedPipe as Read>::read(&mut &*self, buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
<&NamedPipe as Read>::read_vectored(&mut &*self, bufs)
}
}
impl Write for NamedPipe {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
<&NamedPipe as Write>::write(&mut &*self, buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
<&NamedPipe as Write>::write_vectored(&mut &*self, bufs)
}
fn flush(&mut self) -> io::Result<()> {
<&NamedPipe as Write>::flush(&mut &*self)
}
}
impl NamedPipe {
fn read_with(
&self,
len: usize,
f: impl FnOnce(&mut &[u8]) -> io::Result<usize>,
) -> io::Result<usize> {
let mut state = self.inner.io.lock().unwrap();
state.read_size = len.clamp(DEFAULT_READ_SIZE, self.inner.max_read_size);
match mem::replace(&mut state.read, ReadState::Idle) {
ReadState::Idle if state.token.is_none() => Err(not_registered()),
read @ (ReadState::Idle | ReadState::InFlight(_)) => {
state.read = read;
Err(would_block())
}
ReadState::Ready(op, cur) => {
let n = {
let mut remaining = &op.buf[cur..];
f(&mut remaining)?
};
let next = cur + n;
if next != op.buf.len() {
state.read = ReadState::Ready(op, next);
} else {
state.recycle(op);
Inner::schedule_read(&self.inner, &mut state, None);
}
Ok(n)
}
ReadState::Failed(e) => {
Inner::schedule_read(&self.inner, &mut state, None);
if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
Ok(0)
} else {
Err(e)
}
}
}
}
fn write_bufs<'b>(&self, bufs: impl Iterator<Item = &'b [u8]> + Clone) -> io::Result<usize> {
let mut io = self.inner.io.lock().unwrap();
match mem::replace(&mut io.write, WriteState::Idle) {
WriteState::Idle if io.token.is_none() => return Err(not_registered()),
WriteState::Idle => {}
WriteState::Failed(e) => return Err(e),
write @ WriteState::InFlight(..) => {
io.write = write;
return Err(would_block());
}
}
let mut op = io.op();
op.buf
.reserve(bufs.clone().map(<[u8]>::len).fold(0, usize::saturating_add));
for buf in bufs {
op.buf.extend_from_slice(buf);
}
let len = op.buf.len();
Inner::schedule_write(&self.inner, &mut io, op, 0)?;
Ok(len)
}
}
impl<'a> Read for &'a NamedPipe {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read_with(buf.len(), |data| data.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let len = bufs.iter().map(|buf| buf.len()).sum();
self.read_with(len, |data| data.read_vectored(bufs))
}
}
impl<'a> Write for &'a NamedPipe {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_bufs(iter::once(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.write_bufs(bufs.iter().map(|buf| &**buf))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Source for NamedPipe {
fn register(&mut self, registry: &Registry, token: Token, _: Interest) -> io::Result<()> {
let mut io = self.inner.io.lock().unwrap();
io.check_association(registry, false)?;
if io.token.is_some() {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"I/O source already registered with a `Registry`",
));
}
if io.cp.is_none() {
let selector = registry.selector();
let inner_token = NEXT_TOKEN.fetch_add(2, Relaxed) + 2;
selector.inner.cp.add_handle(inner_token, self)?;
io.cp = Some(selector.clone_port());
}
io.token = Some(token);
Inner::post_register(&self.inner, &mut io, None);
Ok(())
}
fn reregister(&mut self, registry: &Registry, token: Token, _: Interest) -> io::Result<()> {
let mut io = self.inner.io.lock().unwrap();
io.check_association(registry, true)?;
io.token = Some(token);
Inner::post_register(&self.inner, &mut io, None);
Ok(())
}
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
let mut io = self.inner.io.lock().unwrap();
io.check_association(registry, true)?;
if io.token.is_none() {
return Err(not_registered());
}
io.token = None;
Ok(())
}
}
impl AsRawHandle for NamedPipe {
fn as_raw_handle(&self) -> RawHandle {
self.inner.handle.raw() as RawHandle
}
}
impl fmt::Debug for NamedPipe {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.handle.fmt(f)
}
}
impl Drop for NamedPipe {
fn drop(&mut self) {
let mut io = self.inner.io.lock().unwrap();
io.token = None;
if let Some(op_id) = io.connect.take() {
self.inner.cancel(op_id);
}
match mem::replace(&mut io.read, ReadState::Idle) {
ReadState::InFlight(op_id) => self.inner.cancel(op_id),
ReadState::Ready(..) | ReadState::Idle | ReadState::Failed(_) => {}
}
}
}
impl Inner {
fn schedule_read(me: &Arc<Inner>, io: &mut Io, events: Option<&mut Vec<Event>>) -> bool {
if !matches!(io.read, ReadState::Idle) {
return true;
}
if io.token.is_none() {
return true;
}
let len = io.read_size;
let mut op = io.op();
op.buf.reserve_exact(len);
let res = unsafe {
io.submit(op, me, read_done, |op| {
me.read_overlapped((*op).buf.as_mut_ptr(), len, Op::overlapped(op))
})
};
match res {
Ok(op_id) => {
io.read = ReadState::InFlight(op_id);
true
}
Err(ref e) if e.raw_os_error() == Some(ERROR_PIPE_LISTENING as i32) => false,
Err(e) => {
io.read = ReadState::Failed(e);
io.notify_readable(me, events);
true
}
}
}
fn schedule_write(me: &Arc<Inner>, io: &mut Io, op: Box<Op>, pos: usize) -> io::Result<()> {
let op_id = unsafe {
io.submit(op, me, write_done, |op| {
let buf = &(*op).buf;
me.write_overlapped(buf.as_ptr().add(pos), buf.len() - pos, Op::overlapped(op))
})
}?;
io.write = WriteState::InFlight(op_id, pos);
Ok(())
}
fn post_register(me: &Arc<Inner>, io: &mut Io, mut events: Option<&mut Vec<Event>>) {
let readable = matches!(io.read, ReadState::Ready(..) | ReadState::Failed(_));
#[allow(clippy::needless_option_as_deref)]
if !Inner::schedule_read(me, io, events.as_deref_mut()) {
return;
}
if readable {
io.notify_readable(me, events.as_deref_mut());
}
if !matches!(io.write, WriteState::InFlight(..)) {
io.notify_writable(me, events);
}
}
}
fn connect_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let status = CompletionStatus::from_entry(status);
let (mut op, me) = unsafe { Op::complete(status.overlapped()) };
let mut io = me.io.lock().unwrap();
if io.connect != Some(op.id()) {
io.recycle(op);
return;
}
io.connect = None;
debug_assert_eq!(status.bytes_transferred(), 0);
if let Err(e) = me.result(&mut op) {
io.connect_error = Some(e);
}
io.recycle(op);
Inner::post_register(&me, &mut io, events);
}
fn read_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let status = CompletionStatus::from_entry(status);
let (mut op, me) = unsafe { Op::complete(status.overlapped()) };
let mut io = me.io.lock().unwrap();
if !matches!(io.read, ReadState::InFlight(id) if id == op.id()) {
io.recycle(op);
return;
}
io.read = match me.result(&mut op) {
Ok(n) => {
debug_assert_eq!(status.bytes_transferred() as usize, n);
unsafe { op.buf.set_len(n) };
ReadState::Ready(op, 0)
}
Err(e) if e.raw_os_error() == Some(ERROR_MORE_DATA as i32) => {
unsafe { op.buf.set_len(status.bytes_transferred() as usize) };
ReadState::Ready(op, 0)
}
Err(e) => {
debug_assert_eq!(status.bytes_transferred(), 0);
io.recycle(op);
ReadState::Failed(e)
}
};
io.notify_readable(&me, events);
}
fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let status = CompletionStatus::from_entry(status);
let (mut op, me) = unsafe { Op::complete(status.overlapped()) };
let mut io = me.io.lock().unwrap();
let pos = match io.write {
WriteState::InFlight(id, pos) if id == op.id() => pos,
_ => {
io.recycle(op);
return;
}
};
io.write = WriteState::Idle;
match me.result(&mut op) {
Ok(n) => {
debug_assert_eq!(status.bytes_transferred() as usize, n);
let pos = pos + n;
if pos == op.buf.len() {
io.recycle(op);
io.notify_writable(&me, events);
} else if n == 0 {
io.recycle(op);
io.write = WriteState::Failed(io::ErrorKind::WriteZero.into());
io.notify_writable(&me, events);
} else {
let res = Inner::schedule_write(&me, &mut io, op, pos);
if let Err(e) = res {
io.write = WriteState::Failed(e);
io.notify_writable(&me, events);
}
}
}
Err(e) => {
debug_assert_eq!(status.bytes_transferred(), 0);
io.recycle(op);
io.write = WriteState::Failed(e);
io.notify_writable(&me, events);
}
}
}
fn event_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let status = CompletionStatus::from_entry(status);
let me = unsafe { Arc::from_raw(status.overlapped() as *const Inner) };
let io = me.io.lock().unwrap();
if io.token.is_some() {
if let Some(events) = events {
let mut ev = Event::from_completion_status(status);
ev.data >>= 1;
events.push(ev);
}
}
}
impl Io {
fn op(&mut self) -> Box<Op> {
self.spare
.iter_mut()
.rev()
.find_map(Option::take)
.unwrap_or_else(Op::new)
}
fn recycle(&mut self, mut op: Box<Op>) {
if let Some(spare) = self.spare.iter_mut().find(|spare| spare.is_none()) {
if op.buf.capacity() > MAX_IDLE_BUF_SIZE {
op.buf = Vec::new();
} else {
op.buf.clear();
}
*spare = Some(op);
}
}
unsafe fn submit(
&mut self,
mut op: Box<Op>,
pipe: &Arc<Inner>,
done: fn(&OVERLAPPED_ENTRY, Option<&mut Vec<Event>>),
start: impl FnOnce(*mut Op) -> io::Result<()>,
) -> io::Result<OpId> {
debug_assert!(op.pipe.is_none(), "submitting an `Op` in flight");
op.overlapped.callback = done;
op.pipe = Some(pipe.clone());
let op = NonNull::from(Box::leak(op));
match start(op.as_ptr()) {
Ok(()) => Ok(OpId(op)),
Err(e) => {
let mut op = Box::from_raw(op.as_ptr());
op.pipe = None;
self.recycle(op);
Err(e)
}
}
}
fn check_association(&self, registry: &Registry, required: bool) -> io::Result<()> {
match self.cp {
Some(ref cp) if !registry.selector().same_port(cp) => Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"I/O source already registered with a different `Registry`",
)),
None if required => Err(not_registered()),
_ => Ok(()),
}
}
fn notify_readable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
if let Some(token) = self.token {
let mut ev = Event::new(token);
ev.set_readable();
if let Some(events) = events {
events.push(ev);
} else {
self.schedule_event(me, ev);
}
}
}
fn notify_writable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
if let Some(token) = self.token {
let mut ev = Event::new(token);
ev.set_writable();
if let Some(events) = events {
events.push(ev);
} else {
self.schedule_event(me, ev);
}
}
}
fn schedule_event(&self, me: &Arc<Inner>, mut event: Event) {
event.data <<= 1;
event.data += 1;
let overlapped = Arc::into_raw(me.clone()) as *mut Overlapped;
let completion_status = event.to_completion_status_with_overlapped(overlapped);
if self.cp.as_ref().unwrap().post(completion_status).is_err() {
drop(unsafe { Arc::from_raw(overlapped as *const Inner) });
}
}
}