diff --git a/src/sys/windows/named_pipe.rs b/src/sys/windows/named_pipe.rs index 3cf627af1..f6968d3c7 100644 --- a/src/sys/windows/named_pipe.rs +++ b/src/sys/windows/named_pipe.rs @@ -11,7 +11,7 @@ use miow::iocp::{CompletionPort, CompletionStatus}; use miow::pipe; use winapi::shared::winerror::{ERROR_BROKEN_PIPE, ERROR_PIPE_LISTENING}; use winapi::um::ioapiset::CancelIoEx; -use winapi::um::minwinbase::OVERLAPPED_ENTRY; +use winapi::um::minwinbase::{OVERLAPPED, OVERLAPPED_ENTRY}; use crate::event::Source; use crate::sys::windows::{Event, Overlapped}; @@ -70,35 +70,64 @@ pub struct NamedPipe { /// constants depend on it, see the `offset_constants` test. #[repr(C)] struct Inner { + // NOTE: careful modifying the order of these three fields, the `ptr_from_*` + // methods depend on the layout! connect: Overlapped, read: Overlapped, write: Overlapped, + // END NOTE. handle: pipe::NamedPipe, connecting: AtomicBool, io: Mutex, pool: Mutex, } -/// Offsets from a pointer to `Inner` to the `connect`, `read` and `write` -/// fields. -const CONNECT_OFFSET: usize = 0; -const READ_OFFSET: usize = CONNECT_OFFSET + size_of::(); // `connect` fields. -const WRITE_OFFSET: usize = READ_OFFSET + size_of::(); // `read` fields. +impl Inner { + /// Converts a pointer to `Inner.connect` to a pointer to `Inner`. + /// + /// # Unsafety + /// + /// Caller must ensure `ptr` is pointing to `Inner.connect`. + unsafe fn ptr_from_conn_overlapped(ptr: *mut OVERLAPPED) -> *const Inner { + // `connect` is the first field, so the pointer are the same. + ptr.cast() + } + + /// Same as [`ptr_from_conn_overlapped`] but for `Inner.read`. + unsafe fn ptr_from_read_overlapped(ptr: *mut OVERLAPPED) -> *const Inner { + // `read` is after `connect: Overlapped`. + (ptr as usize + size_of::()) as *const Inner + } + + /// Same as [`ptr_from_conn_overlapped`] but for `Inner.write`. + unsafe fn ptr_from_write_overlapped(ptr: *mut OVERLAPPED) -> *const Inner { + // `read` is after `connect: Overlapped` and `read: Overlapped`. + (ptr as usize + (2 * size_of::())) as *const Inner + } +} #[test] -fn offset_constants() { +fn ptr_from() { use std::mem::ManuallyDrop; use std::ptr; let pipe = unsafe { ManuallyDrop::new(NamedPipe::from_raw_handle(ptr::null_mut())) }; let inner: &Inner = &pipe.inner; - let base = inner as *const Inner as usize; - let connect = &inner.connect as *const Overlapped as usize; - assert_eq!(base + CONNECT_OFFSET, connect); - let read = &inner.read as *const Overlapped as usize; - assert_eq!(base + READ_OFFSET, read); - let write = &inner.write as *const Overlapped as usize; - assert_eq!(base + WRITE_OFFSET, write); + assert_eq!( + inner as *const Inner, + unsafe { Inner::ptr_from_conn_overlapped(&inner.connect as *const _ as *mut OVERLAPPED) }, + "`ptr_from_conn_overlapped` incorrect" + ); + assert_eq!( + inner as *const Inner, + unsafe { Inner::ptr_from_read_overlapped(&inner.read as *const _ as *mut OVERLAPPED) }, + "`ptr_from_read_overlapped` incorrect" + ); + assert_eq!( + inner as *const Inner, + unsafe { Inner::ptr_from_write_overlapped(&inner.write as *const _ as *mut OVERLAPPED) }, + "`ptr_from_write_overlapped` incorrect" + ); } struct Io { @@ -608,8 +637,7 @@ fn connect_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { // Acquire the `Arc`. Note that we should be guaranteed that // the refcount is available to us due to the `mem::forget` in // `connect` above. - let me = - unsafe { Arc::from_raw((status.overlapped() as usize - CONNECT_OFFSET) as *mut Inner) }; + let me = unsafe { Arc::from_raw(Inner::ptr_from_conn_overlapped(status.overlapped())) }; // Flag ourselves as no longer using the `connect` overlapped instances. let prev = me.connecting.swap(false, SeqCst); @@ -635,7 +663,7 @@ fn read_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { // Acquire the `FromRawArc`. Note that we should be guaranteed that // the refcount is available to us due to the `mem::forget` in // `schedule_read` above. - let me = unsafe { Arc::from_raw((status.overlapped() as usize - READ_OFFSET) as *mut Inner) }; + let me = unsafe { Arc::from_raw(Inner::ptr_from_read_overlapped(status.overlapped())) }; // Move from the `Pending` to `Ok` state. let mut io = me.io.lock().unwrap(); @@ -667,7 +695,7 @@ fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec>) { // Acquire the `Arc`. Note that we should be guaranteed that // the refcount is available to us due to the `mem::forget` in // `schedule_write` above. - let me = unsafe { Arc::from_raw((status.overlapped() as usize - WRITE_OFFSET) as *mut Inner) }; + let me = unsafe { Arc::from_raw(Inner::ptr_from_write_overlapped(status.overlapped())) }; // Make the state change out of `Pending`. If we wrote the entire buffer // then we're writable again and otherwise we schedule another write.