diff options
Diffstat (limited to 'third_party/rust/mio/src/sys')
33 files changed, 5354 insertions, 0 deletions
diff --git a/third_party/rust/mio/src/sys/mod.rs b/third_party/rust/mio/src/sys/mod.rs new file mode 100644 index 0000000000..81ae6d2e61 --- /dev/null +++ b/third_party/rust/mio/src/sys/mod.rs @@ -0,0 +1,80 @@ +//! Module with system specific types. +//! +//! Required types: +//! +//! * `Event`: a type alias for the system specific event, e.g. `kevent` or +//! `epoll_event`. +//! * `event`: a module with various helper functions for `Event`, see +//! [`crate::event::Event`] for the required functions. +//! * `Events`: collection of `Event`s, see [`crate::Events`]. +//! * `IoSourceState`: state for the `IoSource` type. +//! * `Selector`: selector used to register event sources and poll for events, +//! see [`crate::Poll`] and [`crate::Registry`] for required +//! methods. +//! * `tcp` and `udp` modules: see the [`crate::net`] module. +//! * `Waker`: see [`crate::Waker`]. + +cfg_os_poll! { + macro_rules! debug_detail { + ( + $type: ident ($event_type: ty), $test: path, + $($(#[$target: meta])* $libc: ident :: $flag: ident),+ $(,)* + ) => { + struct $type($event_type); + + impl fmt::Debug for $type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut written_one = false; + $( + $(#[$target])* + #[allow(clippy::bad_bit_mask)] // Apparently some flags are zero. + { + // Windows doesn't use `libc` but the `afd` module. + if $test(&self.0, &$libc :: $flag) { + if !written_one { + write!(f, "{}", stringify!($flag))?; + written_one = true; + } else { + write!(f, "|{}", stringify!($flag))?; + } + } + } + )+ + if !written_one { + write!(f, "(empty)") + } else { + Ok(()) + } + } + } + }; + } +} + +#[cfg(unix)] +cfg_os_poll! { + mod unix; + pub use self::unix::*; +} + +#[cfg(windows)] +cfg_os_poll! { + mod windows; + pub use self::windows::*; +} + +cfg_not_os_poll! { + mod shell; + pub(crate) use self::shell::*; + + #[cfg(unix)] + cfg_any_os_ext! { + mod unix; + pub use self::unix::SourceFd; + } + + #[cfg(unix)] + cfg_net! { + pub use self::unix::SocketAddr; + } +} diff --git a/third_party/rust/mio/src/sys/shell/mod.rs b/third_party/rust/mio/src/sys/shell/mod.rs new file mode 100644 index 0000000000..7e1533f452 --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/mod.rs @@ -0,0 +1,70 @@ +macro_rules! os_required { + () => { + panic!("mio must be compiled with `os-poll` to run.") + }; +} + +mod selector; +pub(crate) use self::selector::{event, Event, Events, Selector}; + +mod waker; +pub(crate) use self::waker::Waker; + +cfg_net! { + pub(crate) mod tcp; + pub(crate) mod udp; + #[cfg(unix)] + pub(crate) mod uds; +} + +cfg_io_source! { + use std::io; + #[cfg(windows)] + use std::os::windows::io::RawSocket; + + #[cfg(windows)] + use crate::{Registry, Token, Interest}; + + pub(crate) struct IoSourceState; + + impl IoSourceState { + pub fn new() -> IoSourceState { + IoSourceState + } + + pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R> + where + F: FnOnce(&T) -> io::Result<R>, + { + // We don't hold state, so we can just call the function and + // return. + f(io) + } + } + + #[cfg(windows)] + impl IoSourceState { + pub fn register( + &mut self, + _: &Registry, + _: Token, + _: Interest, + _: RawSocket, + ) -> io::Result<()> { + os_required!() + } + + pub fn reregister( + &mut self, + _: &Registry, + _: Token, + _: Interest, + ) -> io::Result<()> { + os_required!() + } + + pub fn deregister(&mut self) -> io::Result<()> { + os_required!() + } + } +} diff --git a/third_party/rust/mio/src/sys/shell/selector.rs b/third_party/rust/mio/src/sys/shell/selector.rs new file mode 100644 index 0000000000..91fc0bf47c --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/selector.rs @@ -0,0 +1,108 @@ +use std::io; +#[cfg(unix)] +use std::os::unix::io::{AsRawFd, RawFd}; +use std::time::Duration; + +pub type Event = usize; + +pub type Events = Vec<Event>; + +#[derive(Debug)] +pub struct Selector {} + +impl Selector { + pub fn try_clone(&self) -> io::Result<Selector> { + os_required!(); + } + + pub fn select(&self, _: &mut Events, _: Option<Duration>) -> io::Result<()> { + os_required!(); + } + + #[cfg(debug_assertions)] + pub fn register_waker(&self) -> bool { + os_required!(); + } +} + +#[cfg(unix)] +cfg_any_os_ext! { + use crate::{Interest, Token}; + + impl Selector { + pub fn register(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> { + os_required!(); + } + + pub fn reregister(&self, _: RawFd, _: Token, _: Interest) -> io::Result<()> { + os_required!(); + } + + pub fn deregister(&self, _: RawFd) -> io::Result<()> { + os_required!(); + } + } +} + +cfg_io_source! { + #[cfg(debug_assertions)] + impl Selector { + pub fn id(&self) -> usize { + os_required!(); + } + } +} + +#[cfg(unix)] +impl AsRawFd for Selector { + fn as_raw_fd(&self) -> RawFd { + os_required!() + } +} + +#[allow(clippy::trivially_copy_pass_by_ref)] +pub mod event { + use crate::sys::Event; + use crate::Token; + use std::fmt; + + pub fn token(_: &Event) -> Token { + os_required!(); + } + + pub fn is_readable(_: &Event) -> bool { + os_required!(); + } + + pub fn is_writable(_: &Event) -> bool { + os_required!(); + } + + pub fn is_error(_: &Event) -> bool { + os_required!(); + } + + pub fn is_read_closed(_: &Event) -> bool { + os_required!(); + } + + pub fn is_write_closed(_: &Event) -> bool { + os_required!(); + } + + pub fn is_priority(_: &Event) -> bool { + os_required!(); + } + + pub fn is_aio(_: &Event) -> bool { + os_required!(); + } + + pub fn is_lio(_: &Event) -> bool { + os_required!(); + } + + pub fn debug_details(_: &mut fmt::Formatter<'_>, _: &Event) -> fmt::Result { + os_required!(); + } +} diff --git a/third_party/rust/mio/src/sys/shell/tcp.rs b/third_party/rust/mio/src/sys/shell/tcp.rs new file mode 100644 index 0000000000..60dfe70f67 --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/tcp.rs @@ -0,0 +1,27 @@ +use std::io; +use std::net::{self, SocketAddr}; + +pub(crate) fn new_for_addr(_: SocketAddr) -> io::Result<i32> { + os_required!(); +} + +pub(crate) fn bind(_: &net::TcpListener, _: SocketAddr) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn connect(_: &net::TcpStream, _: SocketAddr) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn listen(_: &net::TcpListener, _: u32) -> io::Result<()> { + os_required!(); +} + +#[cfg(unix)] +pub(crate) fn set_reuseaddr(_: &net::TcpListener, _: bool) -> io::Result<()> { + os_required!(); +} + +pub(crate) fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { + os_required!(); +} diff --git a/third_party/rust/mio/src/sys/shell/udp.rs b/third_party/rust/mio/src/sys/shell/udp.rs new file mode 100644 index 0000000000..48ccac740f --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/udp.rs @@ -0,0 +1,10 @@ +use std::io; +use std::net::{self, SocketAddr}; + +pub fn bind(_: SocketAddr) -> io::Result<net::UdpSocket> { + os_required!() +} + +pub(crate) fn only_v6(_: &net::UdpSocket) -> io::Result<bool> { + os_required!() +} diff --git a/third_party/rust/mio/src/sys/shell/uds.rs b/third_party/rust/mio/src/sys/shell/uds.rs new file mode 100644 index 0000000000..c18aca042f --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/uds.rs @@ -0,0 +1,75 @@ +pub(crate) mod datagram { + use crate::net::SocketAddr; + use std::io; + use std::os::unix::net; + use std::path::Path; + + pub(crate) fn bind(_: &Path) -> io::Result<net::UnixDatagram> { + os_required!() + } + + pub(crate) fn unbound() -> io::Result<net::UnixDatagram> { + os_required!() + } + + pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> { + os_required!() + } + + pub(crate) fn local_addr(_: &net::UnixDatagram) -> io::Result<SocketAddr> { + os_required!() + } + + pub(crate) fn peer_addr(_: &net::UnixDatagram) -> io::Result<SocketAddr> { + os_required!() + } + + pub(crate) fn recv_from( + _: &net::UnixDatagram, + _: &mut [u8], + ) -> io::Result<(usize, SocketAddr)> { + os_required!() + } +} + +pub(crate) mod listener { + use crate::net::{SocketAddr, UnixStream}; + use std::io; + use std::os::unix::net; + use std::path::Path; + + pub(crate) fn bind(_: &Path) -> io::Result<net::UnixListener> { + os_required!() + } + + pub(crate) fn accept(_: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> { + os_required!() + } + + pub(crate) fn local_addr(_: &net::UnixListener) -> io::Result<SocketAddr> { + os_required!() + } +} + +pub(crate) mod stream { + use crate::net::SocketAddr; + use std::io; + use std::os::unix::net; + use std::path::Path; + + pub(crate) fn connect(_: &Path) -> io::Result<net::UnixStream> { + os_required!() + } + + pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> { + os_required!() + } + + pub(crate) fn local_addr(_: &net::UnixStream) -> io::Result<SocketAddr> { + os_required!() + } + + pub(crate) fn peer_addr(_: &net::UnixStream) -> io::Result<SocketAddr> { + os_required!() + } +} diff --git a/third_party/rust/mio/src/sys/shell/waker.rs b/third_party/rust/mio/src/sys/shell/waker.rs new file mode 100644 index 0000000000..bbdd7c33af --- /dev/null +++ b/third_party/rust/mio/src/sys/shell/waker.rs @@ -0,0 +1,16 @@ +use crate::sys::Selector; +use crate::Token; +use std::io; + +#[derive(Debug)] +pub struct Waker {} + +impl Waker { + pub fn new(_: &Selector, _: Token) -> io::Result<Waker> { + os_required!(); + } + + pub fn wake(&self) -> io::Result<()> { + os_required!(); + } +} diff --git a/third_party/rust/mio/src/sys/unix/mod.rs b/third_party/rust/mio/src/sys/unix/mod.rs new file mode 100644 index 0000000000..231480a5da --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/mod.rs @@ -0,0 +1,72 @@ +/// Helper macro to execute a system call that returns an `io::Result`. +// +// Macro must be defined before any modules that uses them. +#[allow(unused_macros)] +macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ + let res = unsafe { libc::$fn($($arg, )*) }; + if res == -1 { + Err(std::io::Error::last_os_error()) + } else { + Ok(res) + } + }}; +} + +cfg_os_poll! { + mod selector; + pub(crate) use self::selector::{event, Event, Events, Selector}; + + mod sourcefd; + pub use self::sourcefd::SourceFd; + + mod waker; + pub(crate) use self::waker::Waker; + + cfg_net! { + mod net; + + pub(crate) mod tcp; + pub(crate) mod udp; + pub(crate) mod uds; + pub use self::uds::SocketAddr; + } + + cfg_io_source! { + use std::io; + + // Both `kqueue` and `epoll` don't need to hold any user space state. + pub(crate) struct IoSourceState; + + impl IoSourceState { + pub fn new() -> IoSourceState { + IoSourceState + } + + pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R> + where + F: FnOnce(&T) -> io::Result<R>, + { + // We don't hold state, so we can just call the function and + // return. + f(io) + } + } + } + + cfg_os_ext! { + pub(crate) mod pipe; + } +} + +cfg_not_os_poll! { + cfg_net! { + mod uds; + pub use self::uds::SocketAddr; + } + + cfg_any_os_ext! { + mod sourcefd; + pub use self::sourcefd::SourceFd; + } +} diff --git a/third_party/rust/mio/src/sys/unix/net.rs b/third_party/rust/mio/src/sys/unix/net.rs new file mode 100644 index 0000000000..78f1387b1f --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/net.rs @@ -0,0 +1,168 @@ +use std::io; +use std::mem::size_of; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + +pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> { + let domain = match addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + + new_socket(domain, socket_type) +} + +/// Create a new non-blocking socket. +pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> { + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; + + // Gives a warning for platforms without SOCK_NONBLOCK. + #[allow(clippy::let_and_return)] + let socket = syscall!(socket(domain, socket_type, 0)); + + // Mimick `libstd` and set `SO_NOSIGPIPE` on apple systems. + #[cfg(target_vendor = "apple")] + let socket = socket.and_then(|socket| { + syscall!(setsockopt( + socket, + libc::SOL_SOCKET, + libc::SO_NOSIGPIPE, + &1 as *const libc::c_int as *const libc::c_void, + size_of::<libc::c_int>() as libc::socklen_t + )) + .map(|_| socket) + }); + + // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. + #[cfg(any(target_os = "ios", target_os = "macos"))] + let socket = socket.and_then(|socket| { + // For platforms that don't support flags in socket, we need to + // set the flags ourselves. + syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) + .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| socket)) + .map_err(|e| { + // If either of the `fcntl` calls failed, ensure the socket is + // closed and return the error. + let _ = syscall!(close(socket)); + e + }) + }); + + socket +} + +/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: libc::sockaddr_in, + v6: libc::sockaddr_in6, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const libc::sockaddr { + self as *const _ as *const libc::sockaddr + } +} + +/// Converts a Rust `SocketAddr` into the system representation. +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) { + match addr { + SocketAddr::V4(ref addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + let sin_addr = libc::in_addr { + s_addr: u32::from_ne_bytes(addr.ip().octets()), + }; + + let sockaddr_in = libc::sockaddr_in { + sin_family: libc::AF_INET as libc::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin_len: 0, + }; + + let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t; + (sockaddr, socklen) + } + SocketAddr::V6(ref addr) => { + let sockaddr_in6 = libc::sockaddr_in6 { + sin6_family: libc::AF_INET6 as libc::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr: libc::in6_addr { + s6_addr: addr.ip().octets(), + }, + sin6_flowinfo: addr.flowinfo(), + sin6_scope_id: addr.scope_id(), + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin6_len: 0, + #[cfg(target_os = "illumos")] + __sin6_src_id: 0, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t; + (sockaddr, socklen) + } + } +} + +/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`. +/// +/// # Safety +/// +/// `storage` must have the `ss_family` field correctly initialized. +/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`. +pub(crate) unsafe fn to_socket_addr( + storage: *const libc::sockaddr_storage, +) -> io::Result<SocketAddr> { + match (*storage).ss_family as libc::c_int { + libc::AF_INET => { + // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. + let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in); + let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + let port = u16::from_be(addr.sin_port); + Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } + libc::AF_INET6 => { + // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. + let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6); + let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr); + let port = u16::from_be(addr.sin6_port); + Ok(SocketAddr::V6(SocketAddrV6::new( + ip, + port, + addr.sin6_flowinfo, + addr.sin6_scope_id, + ))) + } + _ => Err(io::ErrorKind::InvalidInput.into()), + } +} diff --git a/third_party/rust/mio/src/sys/unix/pipe.rs b/third_party/rust/mio/src/sys/unix/pipe.rs new file mode 100644 index 0000000000..c899dfb2da --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/pipe.rs @@ -0,0 +1,431 @@ +//! Unix pipe. +//! +//! See the [`new`] function for documentation. + +use std::fs::File; +use std::io::{self, IoSlice, IoSliceMut, Read, Write}; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use std::process::{ChildStderr, ChildStdin, ChildStdout}; + +use crate::io_source::IoSource; +use crate::{event, Interest, Registry, Token}; + +/// Create a new non-blocking Unix pipe. +/// +/// This is a wrapper around Unix's [`pipe(2)`] system call and can be used as +/// inter-process or thread communication channel. +/// +/// This channel may be created before forking the process and then one end used +/// in each process, e.g. the parent process has the sending end to send command +/// to the child process. +/// +/// [`pipe(2)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html +/// +/// # Events +/// +/// The [`Sender`] can be registered with [`WRITABLE`] interest to receive +/// [writable events], the [`Receiver`] with [`READABLE`] interest. Once data is +/// written to the `Sender` the `Receiver` will receive an [readable event]. +/// +/// In addition to those events, events will also be generated if the other side +/// is dropped. To check if the `Sender` is dropped you'll need to check +/// [`is_read_closed`] on events for the `Receiver`, if it returns true the +/// `Sender` is dropped. On the `Sender` end check [`is_write_closed`], if it +/// returns true the `Receiver` was dropped. Also see the second example below. +/// +/// [`WRITABLE`]: Interest::WRITABLE +/// [writable events]: event::Event::is_writable +/// [`READABLE`]: Interest::READABLE +/// [readable event]: event::Event::is_readable +/// [`is_read_closed`]: event::Event::is_read_closed +/// [`is_write_closed`]: event::Event::is_write_closed +/// +/// # Deregistering +/// +/// Both `Sender` and `Receiver` will deregister themselves when dropped, +/// **iff** the file descriptors are not duplicated (via [`dup(2)`]). +/// +/// [`dup(2)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html +/// +/// # Examples +/// +/// Simple example that writes data into the sending end and read it from the +/// receiving end. +/// +/// ``` +/// use std::io::{self, Read, Write}; +/// +/// use mio::{Poll, Events, Interest, Token}; +/// use mio::unix::pipe; +/// +/// // Unique tokens for the two ends of the channel. +/// const PIPE_RECV: Token = Token(0); +/// const PIPE_SEND: Token = Token(1); +/// +/// # fn main() -> io::Result<()> { +/// // Create our `Poll` instance and the `Events` container. +/// let mut poll = Poll::new()?; +/// let mut events = Events::with_capacity(8); +/// +/// // Create a new pipe. +/// let (mut sender, mut receiver) = pipe::new()?; +/// +/// // Register both ends of the channel. +/// poll.registry().register(&mut receiver, PIPE_RECV, Interest::READABLE)?; +/// poll.registry().register(&mut sender, PIPE_SEND, Interest::WRITABLE)?; +/// +/// const MSG: &[u8; 11] = b"Hello world"; +/// +/// loop { +/// poll.poll(&mut events, None)?; +/// +/// for event in events.iter() { +/// match event.token() { +/// PIPE_SEND => sender.write(MSG) +/// .and_then(|n| if n != MSG.len() { +/// // We'll consider a short write an error in this +/// // example. NOTE: we can't use `write_all` with +/// // non-blocking I/O. +/// Err(io::ErrorKind::WriteZero.into()) +/// } else { +/// Ok(()) +/// })?, +/// PIPE_RECV => { +/// let mut buf = [0; 11]; +/// let n = receiver.read(&mut buf)?; +/// println!("received: {:?}", &buf[0..n]); +/// assert_eq!(n, MSG.len()); +/// assert_eq!(&buf, &*MSG); +/// return Ok(()); +/// }, +/// _ => unreachable!(), +/// } +/// } +/// } +/// # } +/// ``` +/// +/// Example that receives an event once the `Sender` is dropped. +/// +/// ``` +/// # use std::io; +/// # +/// # use mio::{Poll, Events, Interest, Token}; +/// # use mio::unix::pipe; +/// # +/// # const PIPE_RECV: Token = Token(0); +/// # const PIPE_SEND: Token = Token(1); +/// # +/// # fn main() -> io::Result<()> { +/// // Same setup as in the example above. +/// let mut poll = Poll::new()?; +/// let mut events = Events::with_capacity(8); +/// +/// let (mut sender, mut receiver) = pipe::new()?; +/// +/// poll.registry().register(&mut receiver, PIPE_RECV, Interest::READABLE)?; +/// poll.registry().register(&mut sender, PIPE_SEND, Interest::WRITABLE)?; +/// +/// // Drop the sender. +/// drop(sender); +/// +/// poll.poll(&mut events, None)?; +/// +/// for event in events.iter() { +/// match event.token() { +/// PIPE_RECV if event.is_read_closed() => { +/// // Detected that the sender was dropped. +/// println!("Sender dropped!"); +/// return Ok(()); +/// }, +/// _ => unreachable!(), +/// } +/// } +/// # unreachable!(); +/// # } +/// ``` +pub fn new() -> io::Result<(Sender, Receiver)> { + let mut fds: [RawFd; 2] = [-1, -1]; + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "illumos", + ))] + unsafe { + if libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) != 0 { + return Err(io::Error::last_os_error()); + } + } + + #[cfg(any(target_os = "ios", target_os = "macos"))] + unsafe { + // For platforms that don't have `pipe2(2)` we need to manually set the + // correct flags on the file descriptor. + if libc::pipe(fds.as_mut_ptr()) != 0 { + return Err(io::Error::last_os_error()); + } + + for fd in &fds { + if libc::fcntl(*fd, libc::F_SETFL, libc::O_NONBLOCK) != 0 + || libc::fcntl(*fd, libc::F_SETFD, libc::FD_CLOEXEC) != 0 + { + let err = io::Error::last_os_error(); + // Don't leak file descriptors. Can't handle error though. + let _ = libc::close(fds[0]); + let _ = libc::close(fds[1]); + return Err(err); + } + } + } + + #[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + )))] + compile_error!("unsupported target for `mio::unix::pipe`"); + + // Safety: we just initialised the `fds` above. + let r = unsafe { Receiver::from_raw_fd(fds[0]) }; + let w = unsafe { Sender::from_raw_fd(fds[1]) }; + Ok((w, r)) +} + +/// Sending end of an Unix pipe. +/// +/// See [`new`] for documentation, including examples. +#[derive(Debug)] +pub struct Sender { + inner: IoSource<File>, +} + +impl Sender { + /// Set the `Sender` into or out of non-blocking mode. + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + set_nonblocking(self.inner.as_raw_fd(), nonblocking) + } +} + +impl event::Source for Sender { + fn register( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + self.inner.register(registry, token, interests) + } + + fn reregister( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + self.inner.reregister(registry, token, interests) + } + + fn deregister(&mut self, registry: &Registry) -> io::Result<()> { + self.inner.deregister(registry) + } +} + +impl Write for Sender { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).write(buf)) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).write_vectored(bufs)) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.do_io(|sender| (&*sender).flush()) + } +} + +impl Write for &Sender { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).write(buf)) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).write_vectored(bufs)) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.do_io(|sender| (&*sender).flush()) + } +} + +/// # Notes +/// +/// The underlying pipe is **not** set to non-blocking. +impl From<ChildStdin> for Sender { + fn from(stdin: ChildStdin) -> Sender { + // Safety: `ChildStdin` is guaranteed to be a valid file descriptor. + unsafe { Sender::from_raw_fd(stdin.into_raw_fd()) } + } +} + +impl FromRawFd for Sender { + unsafe fn from_raw_fd(fd: RawFd) -> Sender { + Sender { + inner: IoSource::new(File::from_raw_fd(fd)), + } + } +} + +impl AsRawFd for Sender { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +impl IntoRawFd for Sender { + fn into_raw_fd(self) -> RawFd { + self.inner.into_inner().into_raw_fd() + } +} + +/// Receiving end of an Unix pipe. +/// +/// See [`new`] for documentation, including examples. +#[derive(Debug)] +pub struct Receiver { + inner: IoSource<File>, +} + +impl Receiver { + /// Set the `Receiver` into or out of non-blocking mode. + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + set_nonblocking(self.inner.as_raw_fd(), nonblocking) + } +} + +impl event::Source for Receiver { + fn register( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + self.inner.register(registry, token, interests) + } + + fn reregister( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + self.inner.reregister(registry, token, interests) + } + + fn deregister(&mut self, registry: &Registry) -> io::Result<()> { + self.inner.deregister(registry) + } +} + +impl Read for Receiver { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).read(buf)) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).read_vectored(bufs)) + } +} + +impl Read for &Receiver { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).read(buf)) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.do_io(|sender| (&*sender).read_vectored(bufs)) + } +} + +/// # Notes +/// +/// The underlying pipe is **not** set to non-blocking. +impl From<ChildStdout> for Receiver { + fn from(stdout: ChildStdout) -> Receiver { + // Safety: `ChildStdout` is guaranteed to be a valid file descriptor. + unsafe { Receiver::from_raw_fd(stdout.into_raw_fd()) } + } +} + +/// # Notes +/// +/// The underlying pipe is **not** set to non-blocking. +impl From<ChildStderr> for Receiver { + fn from(stderr: ChildStderr) -> Receiver { + // Safety: `ChildStderr` is guaranteed to be a valid file descriptor. + unsafe { Receiver::from_raw_fd(stderr.into_raw_fd()) } + } +} + +impl FromRawFd for Receiver { + unsafe fn from_raw_fd(fd: RawFd) -> Receiver { + Receiver { + inner: IoSource::new(File::from_raw_fd(fd)), + } + } +} + +impl AsRawFd for Receiver { + fn as_raw_fd(&self) -> RawFd { + self.inner.as_raw_fd() + } +} + +impl IntoRawFd for Receiver { + fn into_raw_fd(self) -> RawFd { + self.inner.into_inner().into_raw_fd() + } +} + +#[cfg(not(target_os = "illumos"))] +fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> { + let value = nonblocking as libc::c_int; + if unsafe { libc::ioctl(fd, libc::FIONBIO, &value) } == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +#[cfg(target_os = "illumos")] +fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> { + let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if flags < 0 { + return Err(io::Error::last_os_error()); + } + + let nflags = if nonblocking { + flags | libc::O_NONBLOCK + } else { + flags & !libc::O_NONBLOCK + }; + + if flags != nflags { + if unsafe { libc::fcntl(fd, libc::F_SETFL, nflags) } < 0 { + return Err(io::Error::last_os_error()); + } + } + + Ok(()) +} diff --git a/third_party/rust/mio/src/sys/unix/selector/epoll.rs b/third_party/rust/mio/src/sys/unix/selector/epoll.rs new file mode 100644 index 0000000000..f4430909b0 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/selector/epoll.rs @@ -0,0 +1,246 @@ +use crate::{Interest, Token}; + +use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLRDHUP}; +use log::error; +use std::os::unix::io::{AsRawFd, RawFd}; +#[cfg(debug_assertions)] +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::time::Duration; +use std::{cmp, i32, io, ptr}; + +/// Unique id for use as `SelectorId`. +#[cfg(debug_assertions)] +static NEXT_ID: AtomicUsize = AtomicUsize::new(1); + +#[derive(Debug)] +pub struct Selector { + #[cfg(debug_assertions)] + id: usize, + ep: RawFd, + #[cfg(debug_assertions)] + has_waker: AtomicBool, +} + +impl Selector { + pub fn new() -> io::Result<Selector> { + // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < + // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, + // so we use it instead. + #[cfg(target_os = "android")] + let flag = libc::O_CLOEXEC; + #[cfg(not(target_os = "android"))] + let flag = libc::EPOLL_CLOEXEC; + + syscall!(epoll_create1(flag)).map(|ep| Selector { + #[cfg(debug_assertions)] + id: NEXT_ID.fetch_add(1, Ordering::Relaxed), + ep, + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(false), + }) + } + + pub fn try_clone(&self) -> io::Result<Selector> { + syscall!(fcntl(self.ep, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|ep| Selector { + // It's the same selector, so we use the same id. + #[cfg(debug_assertions)] + id: self.id, + ep, + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)), + }) + } + + pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { + // A bug in kernels < 2.6.37 makes timeouts larger than LONG_MAX / CONFIG_HZ + // (approx. 30 minutes with CONFIG_HZ=1200) effectively infinite on 32 bits + // architectures. The magic number is the same constant used by libuv. + #[cfg(target_pointer_width = "32")] + const MAX_SAFE_TIMEOUT: u128 = 1789569; + #[cfg(not(target_pointer_width = "32"))] + const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128; + + let timeout = timeout + .map(|to| cmp::min(to.as_millis(), MAX_SAFE_TIMEOUT) as libc::c_int) + .unwrap_or(-1); + + events.clear(); + syscall!(epoll_wait( + self.ep, + events.as_mut_ptr(), + events.capacity() as i32, + timeout, + )) + .map(|n_events| { + // This is safe because `epoll_wait` ensures that `n_events` are + // assigned. + unsafe { events.set_len(n_events as usize) }; + }) + } + + pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { + let mut event = libc::epoll_event { + events: interests_to_epoll(interests), + u64: usize::from(token) as u64, + }; + + syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ()) + } + + pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { + let mut event = libc::epoll_event { + events: interests_to_epoll(interests), + u64: usize::from(token) as u64, + }; + + syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ()) + } + + pub fn deregister(&self, fd: RawFd) -> io::Result<()> { + syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ()) + } + + #[cfg(debug_assertions)] + pub fn register_waker(&self) -> bool { + self.has_waker.swap(true, Ordering::AcqRel) + } +} + +cfg_io_source! { + impl Selector { + #[cfg(debug_assertions)] + pub fn id(&self) -> usize { + self.id + } + } +} + +impl AsRawFd for Selector { + fn as_raw_fd(&self) -> RawFd { + self.ep + } +} + +impl Drop for Selector { + fn drop(&mut self) { + if let Err(err) = syscall!(close(self.ep)) { + error!("error closing epoll: {}", err); + } + } +} + +fn interests_to_epoll(interests: Interest) -> u32 { + let mut kind = EPOLLET; + + if interests.is_readable() { + kind = kind | EPOLLIN | EPOLLRDHUP; + } + + if interests.is_writable() { + kind |= EPOLLOUT; + } + + kind as u32 +} + +pub type Event = libc::epoll_event; +pub type Events = Vec<Event>; + +pub mod event { + use std::fmt; + + use crate::sys::Event; + use crate::Token; + + pub fn token(event: &Event) -> Token { + Token(event.u64 as usize) + } + + pub fn is_readable(event: &Event) -> bool { + (event.events as libc::c_int & libc::EPOLLIN) != 0 + || (event.events as libc::c_int & libc::EPOLLPRI) != 0 + } + + pub fn is_writable(event: &Event) -> bool { + (event.events as libc::c_int & libc::EPOLLOUT) != 0 + } + + pub fn is_error(event: &Event) -> bool { + (event.events as libc::c_int & libc::EPOLLERR) != 0 + } + + pub fn is_read_closed(event: &Event) -> bool { + // Both halves of the socket have closed + event.events as libc::c_int & libc::EPOLLHUP != 0 + // Socket has received FIN or called shutdown(SHUT_RD) + || (event.events as libc::c_int & libc::EPOLLIN != 0 + && event.events as libc::c_int & libc::EPOLLRDHUP != 0) + } + + pub fn is_write_closed(event: &Event) -> bool { + // Both halves of the socket have closed + event.events as libc::c_int & libc::EPOLLHUP != 0 + // Unix pipe write end has closed + || (event.events as libc::c_int & libc::EPOLLOUT != 0 + && event.events as libc::c_int & libc::EPOLLERR != 0) + // The other side (read end) of a Unix pipe has closed. + || event.events as libc::c_int == libc::EPOLLERR + } + + pub fn is_priority(event: &Event) -> bool { + (event.events as libc::c_int & libc::EPOLLPRI) != 0 + } + + pub fn is_aio(_: &Event) -> bool { + // Not supported in the kernel, only in libc. + false + } + + pub fn is_lio(_: &Event) -> bool { + // Not supported. + false + } + + pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn check_events(got: &u32, want: &libc::c_int) -> bool { + (*got as libc::c_int & want) != 0 + } + debug_detail!( + EventsDetails(u32), + check_events, + libc::EPOLLIN, + libc::EPOLLPRI, + libc::EPOLLOUT, + libc::EPOLLRDNORM, + libc::EPOLLRDBAND, + libc::EPOLLWRNORM, + libc::EPOLLWRBAND, + libc::EPOLLMSG, + libc::EPOLLERR, + libc::EPOLLHUP, + libc::EPOLLET, + libc::EPOLLRDHUP, + libc::EPOLLONESHOT, + #[cfg(target_os = "linux")] + libc::EPOLLEXCLUSIVE, + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::EPOLLWAKEUP, + libc::EPOLL_CLOEXEC, + ); + + // Can't reference fields in packed structures. + let e_u64 = event.u64; + f.debug_struct("epoll_event") + .field("events", &EventsDetails(event.events)) + .field("u64", &e_u64) + .finish() + } +} + +#[cfg(target_os = "android")] +#[test] +fn assert_close_on_exec_flag() { + // This assertion need to be true for Selector::new. + assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC); +} diff --git a/third_party/rust/mio/src/sys/unix/selector/kqueue.rs b/third_party/rust/mio/src/sys/unix/selector/kqueue.rs new file mode 100644 index 0000000000..b7a01a51c6 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/selector/kqueue.rs @@ -0,0 +1,688 @@ +use crate::{Interest, Token}; +use log::error; +use std::mem::MaybeUninit; +use std::ops::{Deref, DerefMut}; +use std::os::unix::io::{AsRawFd, RawFd}; +#[cfg(debug_assertions)] +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::time::Duration; +use std::{cmp, io, ptr, slice}; + +/// Unique id for use as `SelectorId`. +#[cfg(debug_assertions)] +static NEXT_ID: AtomicUsize = AtomicUsize::new(1); + +// Type of the `nchanges` and `nevents` parameters in the `kevent` function. +#[cfg(not(target_os = "netbsd"))] +type Count = libc::c_int; +#[cfg(target_os = "netbsd")] +type Count = libc::size_t; + +// Type of the `filter` field in the `kevent` structure. +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] +type Filter = libc::c_short; +#[cfg(any(target_os = "macos", target_os = "ios"))] +type Filter = i16; +#[cfg(target_os = "netbsd")] +type Filter = u32; + +// Type of the `flags` field in the `kevent` structure. +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] +type Flags = libc::c_ushort; +#[cfg(any(target_os = "macos", target_os = "ios"))] +type Flags = u16; +#[cfg(target_os = "netbsd")] +type Flags = u32; + +// Type of the `data` field in the `kevent` structure. +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" +))] +type Data = libc::intptr_t; +#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +type Data = i64; + +// Type of the `udata` field in the `kevent` structure. +#[cfg(not(target_os = "netbsd"))] +type UData = *mut libc::c_void; +#[cfg(target_os = "netbsd")] +type UData = libc::intptr_t; + +macro_rules! kevent { + ($id: expr, $filter: expr, $flags: expr, $data: expr) => { + libc::kevent { + ident: $id as libc::uintptr_t, + filter: $filter as Filter, + flags: $flags, + fflags: 0, + data: 0, + udata: $data as UData, + } + }; +} + +#[derive(Debug)] +pub struct Selector { + #[cfg(debug_assertions)] + id: usize, + kq: RawFd, + #[cfg(debug_assertions)] + has_waker: AtomicBool, +} + +impl Selector { + pub fn new() -> io::Result<Selector> { + syscall!(kqueue()) + .and_then(|kq| syscall!(fcntl(kq, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| kq)) + .map(|kq| Selector { + #[cfg(debug_assertions)] + id: NEXT_ID.fetch_add(1, Ordering::Relaxed), + kq, + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(false), + }) + } + + pub fn try_clone(&self) -> io::Result<Selector> { + syscall!(fcntl(self.kq, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|kq| Selector { + // It's the same selector, so we use the same id. + #[cfg(debug_assertions)] + id: self.id, + kq, + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)), + }) + } + + pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { + let timeout = timeout.map(|to| libc::timespec { + tv_sec: cmp::min(to.as_secs(), libc::time_t::max_value() as u64) as libc::time_t, + // `Duration::subsec_nanos` is guaranteed to be less than one + // billion (the number of nanoseconds in a second), making the + // cast to i32 safe. The cast itself is needed for platforms + // where C's long is only 32 bits. + tv_nsec: libc::c_long::from(to.subsec_nanos() as i32), + }); + let timeout = timeout + .as_ref() + .map(|s| s as *const _) + .unwrap_or(ptr::null_mut()); + + events.clear(); + syscall!(kevent( + self.kq, + ptr::null(), + 0, + events.as_mut_ptr(), + events.capacity() as Count, + timeout, + )) + .map(|n_events| { + // This is safe because `kevent` ensures that `n_events` are + // assigned. + unsafe { events.set_len(n_events as usize) }; + }) + } + + pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { + let flags = libc::EV_CLEAR | libc::EV_RECEIPT | libc::EV_ADD; + // At most we need two changes, but maybe we only need 1. + let mut changes: [MaybeUninit<libc::kevent>; 2] = + [MaybeUninit::uninit(), MaybeUninit::uninit()]; + let mut n_changes = 0; + + if interests.is_writable() { + let kevent = kevent!(fd, libc::EVFILT_WRITE, flags, token.0); + changes[n_changes] = MaybeUninit::new(kevent); + n_changes += 1; + } + + if interests.is_readable() { + let kevent = kevent!(fd, libc::EVFILT_READ, flags, token.0); + changes[n_changes] = MaybeUninit::new(kevent); + n_changes += 1; + } + + // Older versions of macOS (OS X 10.11 and 10.10 have been witnessed) + // can return EPIPE when registering a pipe file descriptor where the + // other end has already disappeared. For example code that creates a + // pipe, closes a file descriptor, and then registers the other end will + // see an EPIPE returned from `register`. + // + // It also turns out that kevent will still report events on the file + // descriptor, telling us that it's readable/hup at least after we've + // done this registration. As a result we just ignore `EPIPE` here + // instead of propagating it. + // + // More info can be found at tokio-rs/mio#582. + let changes = unsafe { + // This is safe because we ensure that at least `n_changes` are in + // the array. + slice::from_raw_parts_mut(changes[0].as_mut_ptr(), n_changes) + }; + kevent_register(self.kq, changes, &[libc::EPIPE as Data]) + } + + pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> { + let flags = libc::EV_CLEAR | libc::EV_RECEIPT; + let write_flags = if interests.is_writable() { + flags | libc::EV_ADD + } else { + flags | libc::EV_DELETE + }; + let read_flags = if interests.is_readable() { + flags | libc::EV_ADD + } else { + flags | libc::EV_DELETE + }; + + let mut changes: [libc::kevent; 2] = [ + kevent!(fd, libc::EVFILT_WRITE, write_flags, token.0), + kevent!(fd, libc::EVFILT_READ, read_flags, token.0), + ]; + + // Since there is no way to check with which interests the fd was + // registered we modify both readable and write, adding it when required + // and removing it otherwise, ignoring the ENOENT error when it comes + // up. The ENOENT error informs us that a filter we're trying to remove + // wasn't there in first place, but we don't really care since our goal + // is accomplished. + // + // For the explanation of ignoring `EPIPE` see `register`. + kevent_register( + self.kq, + &mut changes, + &[libc::ENOENT as Data, libc::EPIPE as Data], + ) + } + + pub fn deregister(&self, fd: RawFd) -> io::Result<()> { + let flags = libc::EV_DELETE | libc::EV_RECEIPT; + let mut changes: [libc::kevent; 2] = [ + kevent!(fd, libc::EVFILT_WRITE, flags, 0), + kevent!(fd, libc::EVFILT_READ, flags, 0), + ]; + + // Since there is no way to check with which interests the fd was + // registered we remove both filters (readable and writeable) and ignore + // the ENOENT error when it comes up. The ENOENT error informs us that + // the filter wasn't there in first place, but we don't really care + // about that since our goal is to remove it. + kevent_register(self.kq, &mut changes, &[libc::ENOENT as Data]) + } + + #[cfg(debug_assertions)] + pub fn register_waker(&self) -> bool { + self.has_waker.swap(true, Ordering::AcqRel) + } + + // Used by `Waker`. + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + pub fn setup_waker(&self, token: Token) -> io::Result<()> { + // First attempt to accept user space notifications. + let mut kevent = kevent!( + 0, + libc::EVFILT_USER, + libc::EV_ADD | libc::EV_CLEAR | libc::EV_RECEIPT, + token.0 + ); + + syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| { + if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 { + Err(io::Error::from_raw_os_error(kevent.data as i32)) + } else { + Ok(()) + } + }) + } + + // Used by `Waker`. + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + pub fn wake(&self, token: Token) -> io::Result<()> { + let mut kevent = kevent!( + 0, + libc::EVFILT_USER, + libc::EV_ADD | libc::EV_RECEIPT, + token.0 + ); + kevent.fflags = libc::NOTE_TRIGGER; + + syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| { + if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 { + Err(io::Error::from_raw_os_error(kevent.data as i32)) + } else { + Ok(()) + } + }) + } +} + +/// Register `changes` with `kq`ueue. +fn kevent_register( + kq: RawFd, + changes: &mut [libc::kevent], + ignored_errors: &[Data], +) -> io::Result<()> { + syscall!(kevent( + kq, + changes.as_ptr(), + changes.len() as Count, + changes.as_mut_ptr(), + changes.len() as Count, + ptr::null(), + )) + .map(|_| ()) + .or_else(|err| { + // According to the manual page of FreeBSD: "When kevent() call fails + // with EINTR error, all changes in the changelist have been applied", + // so we can safely ignore it. + if err.raw_os_error() == Some(libc::EINTR) { + Ok(()) + } else { + Err(err) + } + }) + .and_then(|()| check_errors(changes, ignored_errors)) +} + +/// Check all events for possible errors, it returns the first error found. +fn check_errors(events: &[libc::kevent], ignored_errors: &[Data]) -> io::Result<()> { + for event in events { + // We can't use references to packed structures (in checking the ignored + // errors), so we need copy the data out before use. + let data = event.data; + // Check for the error flag, the actual error will be in the `data` + // field. + if (event.flags & libc::EV_ERROR != 0) && data != 0 && !ignored_errors.contains(&data) { + return Err(io::Error::from_raw_os_error(data as i32)); + } + } + Ok(()) +} + +cfg_io_source! { + #[cfg(debug_assertions)] + impl Selector { + pub fn id(&self) -> usize { + self.id + } + } +} + +impl AsRawFd for Selector { + fn as_raw_fd(&self) -> RawFd { + self.kq + } +} + +impl Drop for Selector { + fn drop(&mut self) { + if let Err(err) = syscall!(close(self.kq)) { + error!("error closing kqueue: {}", err); + } + } +} + +pub type Event = libc::kevent; +pub struct Events(Vec<libc::kevent>); + +impl Events { + pub fn with_capacity(capacity: usize) -> Events { + Events(Vec::with_capacity(capacity)) + } +} + +impl Deref for Events { + type Target = Vec<libc::kevent>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Events { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +// `Events` cannot derive `Send` or `Sync` because of the +// `udata: *mut ::c_void` field in `libc::kevent`. However, `Events`'s public +// API treats the `udata` field as a `uintptr_t` which is `Send`. `Sync` is +// safe because with a `events: &Events` value, the only access to the `udata` +// field is through `fn token(event: &Event)` which cannot mutate the field. +unsafe impl Send for Events {} +unsafe impl Sync for Events {} + +pub mod event { + use std::fmt; + + use crate::sys::Event; + use crate::Token; + + use super::{Filter, Flags}; + + pub fn token(event: &Event) -> Token { + Token(event.udata as usize) + } + + pub fn is_readable(event: &Event) -> bool { + event.filter == libc::EVFILT_READ || { + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + // Used by the `Awakener`. On platforms that use `eventfd` or a unix + // pipe it will emit a readable event so we'll fake that here as + // well. + { + event.filter == libc::EVFILT_USER + } + #[cfg(not(any(target_os = "freebsd", target_os = "ios", target_os = "macos")))] + { + false + } + } + } + + pub fn is_writable(event: &Event) -> bool { + event.filter == libc::EVFILT_WRITE + } + + pub fn is_error(event: &Event) -> bool { + (event.flags & libc::EV_ERROR) != 0 || + // When the read end of the socket is closed, EV_EOF is set on + // flags, and fflags contains the error if there is one. + (event.flags & libc::EV_EOF) != 0 && event.fflags != 0 + } + + pub fn is_read_closed(event: &Event) -> bool { + event.filter == libc::EVFILT_READ && event.flags & libc::EV_EOF != 0 + } + + pub fn is_write_closed(event: &Event) -> bool { + event.filter == libc::EVFILT_WRITE && event.flags & libc::EV_EOF != 0 + } + + pub fn is_priority(_: &Event) -> bool { + // kqueue doesn't have priority indicators. + false + } + + #[allow(unused_variables)] // `event` is not used on some platforms. + pub fn is_aio(event: &Event) -> bool { + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + { + event.filter == libc::EVFILT_AIO + } + #[cfg(not(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + )))] + { + false + } + } + + #[allow(unused_variables)] // `event` is only used on FreeBSD. + pub fn is_lio(event: &Event) -> bool { + #[cfg(target_os = "freebsd")] + { + event.filter == libc::EVFILT_LIO + } + #[cfg(not(target_os = "freebsd"))] + { + false + } + } + + pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result { + debug_detail!( + FilterDetails(Filter), + PartialEq::eq, + libc::EVFILT_READ, + libc::EVFILT_WRITE, + libc::EVFILT_AIO, + libc::EVFILT_VNODE, + libc::EVFILT_PROC, + libc::EVFILT_SIGNAL, + libc::EVFILT_TIMER, + #[cfg(target_os = "freebsd")] + libc::EVFILT_PROCDESC, + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos" + ))] + libc::EVFILT_FS, + #[cfg(target_os = "freebsd")] + libc::EVFILT_LIO, + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos" + ))] + libc::EVFILT_USER, + #[cfg(target_os = "freebsd")] + libc::EVFILT_SENDFILE, + #[cfg(target_os = "freebsd")] + libc::EVFILT_EMPTY, + #[cfg(target_os = "dragonfly")] + libc::EVFILT_EXCEPT, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::EVFILT_MACHPORT, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::EVFILT_VM, + ); + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn check_flag(got: &Flags, want: &Flags) -> bool { + (got & want) != 0 + } + debug_detail!( + FlagsDetails(Flags), + check_flag, + libc::EV_ADD, + libc::EV_DELETE, + libc::EV_ENABLE, + libc::EV_DISABLE, + libc::EV_ONESHOT, + libc::EV_CLEAR, + libc::EV_RECEIPT, + libc::EV_DISPATCH, + #[cfg(target_os = "freebsd")] + libc::EV_DROP, + libc::EV_FLAG1, + libc::EV_ERROR, + libc::EV_EOF, + libc::EV_SYSFLAGS, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::EV_FLAG0, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::EV_POLL, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::EV_OOBAND, + #[cfg(target_os = "dragonfly")] + libc::EV_NODATA, + ); + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn check_fflag(got: &u32, want: &u32) -> bool { + (got & want) != 0 + } + debug_detail!( + FflagsDetails(u32), + check_fflag, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_TRIGGER, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFNOP, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFAND, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFOR, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFCOPY, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFCTRLMASK, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos" + ))] + libc::NOTE_FFLAGSMASK, + libc::NOTE_LOWAT, + libc::NOTE_DELETE, + libc::NOTE_WRITE, + #[cfg(target_os = "dragonfly")] + libc::NOTE_OOB, + #[cfg(target_os = "openbsd")] + libc::NOTE_EOF, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXTEND, + libc::NOTE_ATTRIB, + libc::NOTE_LINK, + libc::NOTE_RENAME, + libc::NOTE_REVOKE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_NONE, + #[cfg(any(target_os = "openbsd"))] + libc::NOTE_TRUNCATE, + libc::NOTE_EXIT, + libc::NOTE_FORK, + libc::NOTE_EXEC, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_SIGNAL, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXITSTATUS, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXIT_DETAIL, + libc::NOTE_PDATAMASK, + libc::NOTE_PCTRLMASK, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + libc::NOTE_TRACK, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + libc::NOTE_TRACKERR, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + libc::NOTE_CHILD, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXIT_DETAIL_MASK, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXIT_DECRYPTFAIL, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXIT_MEMORY, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_EXIT_CSERROR, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_VM_PRESSURE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_VM_PRESSURE_TERMINATE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_VM_ERROR, + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + libc::NOTE_SECONDS, + #[cfg(any(target_os = "freebsd"))] + libc::NOTE_MSECONDS, + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + libc::NOTE_USECONDS, + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + libc::NOTE_NSECONDS, + #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + libc::NOTE_ABSOLUTE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_LEEWAY, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_CRITICAL, + #[cfg(any(target_os = "ios", target_os = "macos"))] + libc::NOTE_BACKGROUND, + ); + + // Can't reference fields in packed structures. + let ident = event.ident; + let data = event.data; + let udata = event.udata; + f.debug_struct("kevent") + .field("ident", &ident) + .field("filter", &FilterDetails(event.filter)) + .field("flags", &FlagsDetails(event.flags)) + .field("fflags", &FflagsDetails(event.fflags)) + .field("data", &data) + .field("udata", &udata) + .finish() + } +} + +#[test] +#[cfg(feature = "os-ext")] +fn does_not_register_rw() { + use crate::unix::SourceFd; + use crate::{Poll, Token}; + + let kq = unsafe { libc::kqueue() }; + let mut kqf = SourceFd(&kq); + let poll = Poll::new().unwrap(); + + // Registering kqueue fd will fail if write is requested (On anything but + // some versions of macOS). + poll.registry() + .register(&mut kqf, Token(1234), Interest::READABLE) + .unwrap(); +} diff --git a/third_party/rust/mio/src/sys/unix/selector/mod.rs b/third_party/rust/mio/src/sys/unix/selector/mod.rs new file mode 100644 index 0000000000..da61e14d7e --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/selector/mod.rs @@ -0,0 +1,35 @@ +#[cfg(any(target_os = "android", target_os = "illumos", target_os = "linux"))] +mod epoll; + +#[cfg(any(target_os = "android", target_os = "illumos", target_os = "linux"))] +pub(crate) use self::epoll::{event, Event, Events, Selector}; + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" +))] +mod kqueue; + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" +))] +pub(crate) use self::kqueue::{event, Event, Events, Selector}; + +/// Lowest file descriptor used in `Selector::try_clone`. +/// +/// # Notes +/// +/// Usually fds 0, 1 and 2 are standard in, out and error. Some application +/// blindly assume this to be true, which means using any one of those a select +/// could result in some interesting and unexpected errors. Avoid that by using +/// an fd that doesn't have a pre-determined usage. +const LOWEST_FD: libc::c_int = 3; diff --git a/third_party/rust/mio/src/sys/unix/sourcefd.rs b/third_party/rust/mio/src/sys/unix/sourcefd.rs new file mode 100644 index 0000000000..84e776d21d --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/sourcefd.rs @@ -0,0 +1,116 @@ +use crate::{event, Interest, Registry, Token}; + +use std::io; +use std::os::unix::io::RawFd; + +/// Adapter for [`RawFd`] providing an [`event::Source`] implementation. +/// +/// `SourceFd` enables registering any type with an FD with [`Poll`]. +/// +/// While only implementations for TCP and UDP are provided, Mio supports +/// registering any FD that can be registered with the underlying OS selector. +/// `SourceFd` provides the necessary bridge. +/// +/// Note that `SourceFd` takes a `&RawFd`. This is because `SourceFd` **does +/// not** take ownership of the FD. Specifically, it will not manage any +/// lifecycle related operations, such as closing the FD on drop. It is expected +/// that the `SourceFd` is constructed right before a call to +/// [`Registry::register`]. See the examples for more detail. +/// +/// [`event::Source`]: ../event/trait.Source.html +/// [`Poll`]: ../struct.Poll.html +/// [`Registry::register`]: ../struct.Registry.html#method.register +/// +/// # Examples +/// +/// Basic usage. +/// +#[cfg_attr( + all(feature = "os-poll", feature = "net", feature = "os-ext"), + doc = "```" +)] +#[cfg_attr( + not(all(feature = "os-poll", feature = "net", feature = "os-ext")), + doc = "```ignore" +)] +/// # use std::error::Error; +/// # fn main() -> Result<(), Box<dyn Error>> { +/// use mio::{Interest, Poll, Token}; +/// use mio::unix::SourceFd; +/// +/// use std::os::unix::io::AsRawFd; +/// use std::net::TcpListener; +/// +/// // Bind a std listener +/// let listener = TcpListener::bind("127.0.0.1:0")?; +/// +/// let poll = Poll::new()?; +/// +/// // Register the listener +/// poll.registry().register( +/// &mut SourceFd(&listener.as_raw_fd()), +/// Token(0), +/// Interest::READABLE)?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Implementing [`event::Source`] for a custom type backed by a [`RawFd`]. +/// +#[cfg_attr(all(feature = "os-poll", feature = "os-ext"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "os-ext")), doc = "```ignore")] +/// use mio::{event, Interest, Registry, Token}; +/// use mio::unix::SourceFd; +/// +/// use std::os::unix::io::RawFd; +/// use std::io; +/// +/// # #[allow(dead_code)] +/// pub struct MyIo { +/// fd: RawFd, +/// } +/// +/// impl event::Source for MyIo { +/// fn register(&mut self, registry: &Registry, token: Token, interests: Interest) +/// -> io::Result<()> +/// { +/// SourceFd(&self.fd).register(registry, token, interests) +/// } +/// +/// fn reregister(&mut self, registry: &Registry, token: Token, interests: Interest) +/// -> io::Result<()> +/// { +/// SourceFd(&self.fd).reregister(registry, token, interests) +/// } +/// +/// fn deregister(&mut self, registry: &Registry) -> io::Result<()> { +/// SourceFd(&self.fd).deregister(registry) +/// } +/// } +/// ``` +#[derive(Debug)] +pub struct SourceFd<'a>(pub &'a RawFd); + +impl<'a> event::Source for SourceFd<'a> { + fn register( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + registry.selector().register(*self.0, token, interests) + } + + fn reregister( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + registry.selector().reregister(*self.0, token, interests) + } + + fn deregister(&mut self, registry: &Registry) -> io::Result<()> { + registry.selector().deregister(*self.0) + } +} diff --git a/third_party/rust/mio/src/sys/unix/tcp.rs b/third_party/rust/mio/src/sys/unix/tcp.rs new file mode 100644 index 0000000000..5b02cfcb52 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/tcp.rs @@ -0,0 +1,113 @@ +use std::convert::TryInto; +use std::io; +use std::mem::{size_of, MaybeUninit}; +use std::net::{self, SocketAddr}; +use std::os::unix::io::{AsRawFd, FromRawFd}; + +use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr}; + +pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result<libc::c_int> { + let domain = match address { + SocketAddr::V4(_) => libc::AF_INET, + SocketAddr::V6(_) => libc::AF_INET6, + }; + new_socket(domain, libc::SOCK_STREAM) +} + +pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> { + let (raw_addr, raw_addr_length) = socket_addr(&addr); + syscall!(bind(socket.as_raw_fd(), raw_addr.as_ptr(), raw_addr_length))?; + Ok(()) +} + +pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> { + let (raw_addr, raw_addr_length) = socket_addr(&addr); + + match syscall!(connect( + socket.as_raw_fd(), + raw_addr.as_ptr(), + raw_addr_length + )) { + Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => Err(err), + _ => Ok(()), + } +} + +pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { + let backlog = backlog.try_into().unwrap_or(i32::max_value()); + syscall!(listen(socket.as_raw_fd(), backlog))?; + Ok(()) +} + +pub(crate) fn set_reuseaddr(socket: &net::TcpListener, reuseaddr: bool) -> io::Result<()> { + let val: libc::c_int = if reuseaddr { 1 } else { 0 }; + syscall!(setsockopt( + socket.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_REUSEADDR, + &val as *const libc::c_int as *const libc::c_void, + size_of::<libc::c_int>() as libc::socklen_t, + ))?; + Ok(()) +} + +pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { + let mut addr: MaybeUninit<libc::sockaddr_storage> = MaybeUninit::uninit(); + let mut length = size_of::<libc::sockaddr_storage>() as libc::socklen_t; + + // On platforms that support it we can use `accept4(2)` to set `NONBLOCK` + // and `CLOEXEC` in the call to accept the connection. + #[cfg(any( + // Android x86's seccomp profile forbids calls to `accept4(2)` + // See https://github.com/tokio-rs/mio/issues/1445 for details + all( + not(target_arch="x86"), + target_os = "android" + ), + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" + ))] + let stream = { + syscall!(accept4( + listener.as_raw_fd(), + addr.as_mut_ptr() as *mut _, + &mut length, + libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK, + )) + .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) }) + }?; + + // But not all platforms have the `accept4(2)` call. Luckily BSD (derived) + // OSes inherit the non-blocking flag from the listener, so we just have to + // set `CLOEXEC`. + #[cfg(any( + all(target_arch = "x86", target_os = "android"), + target_os = "ios", + target_os = "macos", + ))] + let stream = { + syscall!(accept( + listener.as_raw_fd(), + addr.as_mut_ptr() as *mut _, + &mut length + )) + .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) }) + .and_then(|s| { + syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?; + + // See https://github.com/tokio-rs/mio/issues/1450 + #[cfg(all(target_arch = "x86", target_os = "android"))] + syscall!(fcntl(s.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK))?; + + Ok(s) + }) + }?; + + // This is safe because `accept` calls above ensures the address + // initialised. + unsafe { to_socket_addr(addr.as_ptr()) }.map(|addr| (stream, addr)) +} diff --git a/third_party/rust/mio/src/sys/unix/udp.rs b/third_party/rust/mio/src/sys/unix/udp.rs new file mode 100644 index 0000000000..5a97cbd897 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/udp.rs @@ -0,0 +1,39 @@ +use crate::sys::unix::net::{new_ip_socket, socket_addr}; + +use std::io; +use std::mem; +use std::net::{self, SocketAddr}; +use std::os::unix::io::{AsRawFd, FromRawFd}; + +pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> { + // Gives a warning for non Apple platforms. + #[allow(clippy::let_and_return)] + let socket = new_ip_socket(addr, libc::SOCK_DGRAM); + + socket.and_then(|socket| { + let (raw_addr, raw_addr_length) = socket_addr(&addr); + syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length)) + .map_err(|err| { + // Close the socket if we hit an error, ignoring the error + // from closing since we can't pass back two errors. + let _ = unsafe { libc::close(socket) }; + err + }) + .map(|_| unsafe { net::UdpSocket::from_raw_fd(socket) }) + }) +} + +pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result<bool> { + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::<libc::c_int>() as libc::socklen_t; + + syscall!(getsockopt( + socket.as_raw_fd(), + libc::IPPROTO_IPV6, + libc::IPV6_V6ONLY, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval != 0) +} diff --git a/third_party/rust/mio/src/sys/unix/uds/datagram.rs b/third_party/rust/mio/src/sys/unix/uds/datagram.rs new file mode 100644 index 0000000000..d3e5314fe3 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/datagram.rs @@ -0,0 +1,56 @@ +use super::{socket_addr, SocketAddr}; +use crate::sys::unix::net::new_socket; + +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; + +pub(crate) fn bind(path: &Path) -> io::Result<net::UnixDatagram> { + let fd = new_socket(libc::AF_UNIX, libc::SOCK_DGRAM)?; + // Ensure the fd is closed. + let socket = unsafe { net::UnixDatagram::from_raw_fd(fd) }; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const _; + syscall!(bind(fd, sockaddr, socklen))?; + Ok(socket) +} + +pub(crate) fn unbound() -> io::Result<net::UnixDatagram> { + new_socket(libc::AF_UNIX, libc::SOCK_DGRAM) + .map(|socket| unsafe { net::UnixDatagram::from_raw_fd(socket) }) +} + +pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> { + super::pair(libc::SOCK_DGRAM) +} + +pub(crate) fn local_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> { + super::local_addr(socket.as_raw_fd()) +} + +pub(crate) fn peer_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> { + super::peer_addr(socket.as_raw_fd()) +} + +pub(crate) fn recv_from( + socket: &net::UnixDatagram, + dst: &mut [u8], +) -> io::Result<(usize, SocketAddr)> { + let mut count = 0; + let socketaddr = SocketAddr::new(|sockaddr, socklen| { + syscall!(recvfrom( + socket.as_raw_fd(), + dst.as_mut_ptr() as *mut _, + dst.len(), + 0, + sockaddr, + socklen, + )) + .map(|c| { + count = c; + c as libc::c_int + }) + })?; + Ok((count as usize, socketaddr)) +} diff --git a/third_party/rust/mio/src/sys/unix/uds/listener.rs b/third_party/rust/mio/src/sys/unix/uds/listener.rs new file mode 100644 index 0000000000..b6218427f2 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/listener.rs @@ -0,0 +1,94 @@ +use super::socket_addr; +use crate::net::{SocketAddr, UnixStream}; +use crate::sys::unix::net::new_socket; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; +use std::{io, mem}; + +pub(crate) fn bind(path: &Path) -> io::Result<net::UnixListener> { + let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr; + + syscall!(bind(socket, sockaddr, socklen)) + .and_then(|_| syscall!(listen(socket, 1024))) + .map_err(|err| { + // Close the socket if we hit an error, ignoring the error from + // closing since we can't pass back two errors. + let _ = unsafe { libc::close(socket) }; + err + }) + .map(|_| unsafe { net::UnixListener::from_raw_fd(socket) }) +} + +pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + + // This is safe to assume because a `libc::sockaddr_un` filled with `0` + // bytes is properly initialized. + // + // `0` is a valid value for `sockaddr_un::sun_family`; it is + // `libc::AF_UNSPEC`. + // + // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an + // abstract path. + let mut sockaddr = unsafe { sockaddr.assume_init() }; + + sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; + let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t; + + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + // Android x86's seccomp profile forbids calls to `accept4(2)` + // See https://github.com/tokio-rs/mio/issues/1445 for details + all( + target_arch = "x86", + target_os = "android" + ) + )))] + let socket = { + let flags = libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; + syscall!(accept4( + listener.as_raw_fd(), + &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr, + &mut socklen, + flags + )) + .map(|socket| unsafe { net::UnixStream::from_raw_fd(socket) }) + }; + + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + all(target_arch = "x86", target_os = "android") + ))] + let socket = syscall!(accept( + listener.as_raw_fd(), + &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr, + &mut socklen, + )) + .and_then(|socket| { + // Ensure the socket is closed if either of the `fcntl` calls + // error below. + let s = unsafe { net::UnixStream::from_raw_fd(socket) }; + syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?; + + // See https://github.com/tokio-rs/mio/issues/1450 + #[cfg(all(target_arch = "x86", target_os = "android"))] + syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?; + + Ok(s) + }); + + socket + .map(UnixStream::from_std) + .map(|stream| (stream, SocketAddr::from_parts(sockaddr, socklen))) +} + +pub(crate) fn local_addr(listener: &net::UnixListener) -> io::Result<SocketAddr> { + super::local_addr(listener.as_raw_fd()) +} diff --git a/third_party/rust/mio/src/sys/unix/uds/mod.rs b/third_party/rust/mio/src/sys/unix/uds/mod.rs new file mode 100644 index 0000000000..8e28a9573a --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/mod.rs @@ -0,0 +1,149 @@ +mod socketaddr; +pub use self::socketaddr::SocketAddr; + +/// Get the `sun_path` field offset of `sockaddr_un` for the target OS. +/// +/// On Linux, this funtion equates to the same value as +/// `size_of::<sa_family_t>()`, but some other implementations include +/// other fields before `sun_path`, so the expression more portably +/// describes the size of the address structure. +pub(in crate::sys) fn path_offset(sockaddr: &libc::sockaddr_un) -> usize { + let base = sockaddr as *const _ as usize; + let path = &sockaddr.sun_path as *const _ as usize; + path - base +} + +cfg_os_poll! { + use std::cmp::Ordering; + use std::os::unix::ffi::OsStrExt; + use std::os::unix::io::{RawFd, FromRawFd}; + use std::path::Path; + use std::{io, mem}; + + pub(crate) mod datagram; + pub(crate) mod listener; + pub(crate) mod stream; + + pub(in crate::sys) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + + // This is safe to assume because a `libc::sockaddr_un` filled with `0` + // bytes is properly initialized. + // + // `0` is a valid value for `sockaddr_un::sun_family`; it is + // `libc::AF_UNSPEC`. + // + // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an + // abstract path. + let mut sockaddr = unsafe { sockaddr.assume_init() }; + + sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + let bytes = path.as_os_str().as_bytes(); + match (bytes.get(0), bytes.len().cmp(&sockaddr.sun_path.len())) { + // Abstract paths don't need a null terminator + (Some(&0), Ordering::Greater) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be no longer than libc::sockaddr_un.sun_path", + )); + } + (_, Ordering::Greater) | (_, Ordering::Equal) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be shorter than libc::sockaddr_un.sun_path", + )); + } + _ => {} + } + + for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) { + *dst = *src as libc::c_char; + } + + let offset = path_offset(&sockaddr); + let mut socklen = offset + bytes.len(); + + match bytes.get(0) { + // The struct has already been zeroes so the null byte for pathname + // addresses is already there. + Some(&0) | None => {} + Some(_) => socklen += 1, + } + + Ok((sockaddr, socklen as libc::socklen_t)) + } + + fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)> + where T: FromRawFd, + { + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; + + let mut fds = [-1; 2]; + syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?; + let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) }; + + // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. + // + // In order to set those flags, additional `fcntl` sys calls must be + // performed. If a `fnctl` fails after the sockets have been created, + // the file descriptors will leak. Creating `pair` above ensures that if + // there is an error, the file descriptors are closed. + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; + syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; + syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?; + syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?; + } + Ok(pair) + } + + // The following functions can't simply be replaced with a call to + // `net::UnixDatagram` because of our `SocketAddr` type. + + fn local_addr(socket: RawFd) -> io::Result<SocketAddr> { + SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen))) + } + + fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> { + SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen))) + } + + #[cfg(test)] + mod tests { + use super::{path_offset, socket_addr}; + use std::path::Path; + use std::str; + + #[test] + fn pathname_address() { + const PATH: &str = "./foo/bar.txt"; + const PATH_LEN: usize = 13; + + // Pathname addresses do have a null terminator, so `socklen` is + // expected to be `PATH_LEN` + `offset` + 1. + let path = Path::new(PATH); + let (sockaddr, actual) = socket_addr(path).unwrap(); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset + 1; + assert_eq!(expected as libc::socklen_t, actual) + } + + #[test] + fn abstract_address() { + const PATH: &[u8] = &[0, 116, 111, 107, 105, 111]; + const PATH_LEN: usize = 6; + + // Abstract addresses do not have a null terminator, so `socklen` is + // expected to be `PATH_LEN` + `offset`. + let abstract_path = str::from_utf8(PATH).unwrap(); + let path = Path::new(abstract_path); + let (sockaddr, actual) = socket_addr(path).unwrap(); + let offset = path_offset(&sockaddr); + let expected = PATH_LEN + offset; + assert_eq!(expected as libc::socklen_t, actual) + } + } +} diff --git a/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs b/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs new file mode 100644 index 0000000000..4c7c411618 --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/socketaddr.rs @@ -0,0 +1,130 @@ +use super::path_offset; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; +use std::path::Path; +use std::{ascii, fmt}; + +/// An address associated with a `mio` specific Unix socket. +/// +/// This is implemented instead of imported from [`net::SocketAddr`] because +/// there is no way to create a [`net::SocketAddr`]. One must be returned by +/// [`accept`], so this is returned instead. +/// +/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr +/// [`accept`]: #method.accept +pub struct SocketAddr { + sockaddr: libc::sockaddr_un, + socklen: libc::socklen_t, +} + +struct AsciiEscaped<'a>(&'a [u8]); + +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a [u8]), +} + +impl SocketAddr { + fn address(&self) -> AddressKind<'_> { + let offset = path_offset(&self.sockaddr); + // Don't underflow in `len` below. + if (self.socklen as usize) < offset { + return AddressKind::Unnamed; + } + let len = self.socklen as usize - offset; + let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) }; + + // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses + if len == 0 + || (cfg!(not(any(target_os = "linux", target_os = "android"))) + && self.sockaddr.sun_path[0] == 0) + { + AddressKind::Unnamed + } else if self.sockaddr.sun_path[0] == 0 { + AddressKind::Abstract(&path[1..len]) + } else { + AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) + } + } +} + +cfg_os_poll! { + use std::{io, mem}; + + impl SocketAddr { + pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr> + where + F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>, + { + let mut sockaddr = { + let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed(); + unsafe { sockaddr.assume_init() } + }; + + let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr; + let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t; + + f(raw_sockaddr, &mut socklen)?; + Ok(SocketAddr::from_parts(sockaddr, socklen)) + } + + pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr { + SocketAddr { sockaddr, socklen } + } + + /// Returns `true` if the address is unnamed. + /// + /// Documentation reflected in [`SocketAddr`] + /// + /// [`SocketAddr`]: std::os::unix::net::SocketAddr + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// Documentation reflected in [`SocketAddr`] + /// + /// [`SocketAddr`]: std::os::unix::net::SocketAddr + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { + Some(path) + } else { + None + } + } + + /// Returns the contents of this address if it is an abstract namespace + /// without the leading null byte. + // Link to std::os::unix::net::SocketAddr pending + // https://github.com/rust-lang/rust/issues/85410. + pub fn as_abstract_namespace(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(path) = self.address() { + Some(path) + } else { + None + } + } + } +} + +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), + AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), + } + } +} + +impl<'a> fmt::Display for AsciiEscaped<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"")?; + for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { + write!(fmt, "{}", byte as char)?; + } + write!(fmt, "\"") + } +} diff --git a/third_party/rust/mio/src/sys/unix/uds/stream.rs b/third_party/rust/mio/src/sys/unix/uds/stream.rs new file mode 100644 index 0000000000..149dd14e1d --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/uds/stream.rs @@ -0,0 +1,39 @@ +use super::{socket_addr, SocketAddr}; +use crate::sys::unix::net::new_socket; + +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::net; +use std::path::Path; + +pub(crate) fn connect(path: &Path) -> io::Result<net::UnixStream> { + let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (sockaddr, socklen) = socket_addr(path)?; + let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr; + + match syscall!(connect(socket, sockaddr, socklen)) { + Ok(_) => {} + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} + Err(e) => { + // Close the socket if we hit an error, ignoring the error + // from closing since we can't pass back two errors. + let _ = unsafe { libc::close(socket) }; + + return Err(e); + } + } + + Ok(unsafe { net::UnixStream::from_raw_fd(socket) }) +} + +pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> { + super::pair(libc::SOCK_STREAM) +} + +pub(crate) fn local_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> { + super::local_addr(socket.as_raw_fd()) +} + +pub(crate) fn peer_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> { + super::peer_addr(socket.as_raw_fd()) +} diff --git a/third_party/rust/mio/src/sys/unix/waker.rs b/third_party/rust/mio/src/sys/unix/waker.rs new file mode 100644 index 0000000000..684fee981e --- /dev/null +++ b/third_party/rust/mio/src/sys/unix/waker.rs @@ -0,0 +1,178 @@ +#[cfg(any(target_os = "linux", target_os = "android"))] +mod eventfd { + use crate::sys::Selector; + use crate::{Interest, Token}; + + use std::fs::File; + use std::io::{self, Read, Write}; + use std::os::unix::io::FromRawFd; + + /// Waker backed by `eventfd`. + /// + /// `eventfd` is effectively an 64 bit counter. All writes must be of 8 + /// bytes (64 bits) and are converted (native endian) into an 64 bit + /// unsigned integer and added to the count. Reads must also be 8 bytes and + /// reset the count to 0, returning the count. + #[derive(Debug)] + pub struct Waker { + fd: File, + } + + impl Waker { + pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> { + syscall!(eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK)).and_then(|fd| { + // Turn the file descriptor into a file first so we're ensured + // it's closed when dropped, e.g. when register below fails. + let file = unsafe { File::from_raw_fd(fd) }; + selector + .register(fd, token, Interest::READABLE) + .map(|()| Waker { fd: file }) + }) + } + + pub fn wake(&self) -> io::Result<()> { + let buf: [u8; 8] = 1u64.to_ne_bytes(); + match (&self.fd).write(&buf) { + Ok(_) => Ok(()), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { + // Writing only blocks if the counter is going to overflow. + // So we'll reset the counter to 0 and wake it again. + self.reset()?; + self.wake() + } + Err(err) => Err(err), + } + } + + /// Reset the eventfd object, only need to call this if `wake` fails. + fn reset(&self) -> io::Result<()> { + let mut buf: [u8; 8] = 0u64.to_ne_bytes(); + match (&self.fd).read(&mut buf) { + Ok(_) => Ok(()), + // If the `Waker` hasn't been awoken yet this will return a + // `WouldBlock` error which we can safely ignore. + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()), + Err(err) => Err(err), + } + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub use self::eventfd::Waker; + +#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] +mod kqueue { + use crate::sys::Selector; + use crate::Token; + + use std::io; + + /// Waker backed by kqueue user space notifications (`EVFILT_USER`). + /// + /// The implementation is fairly simple, first the kqueue must be setup to + /// receive waker events this done by calling `Selector.setup_waker`. Next + /// we need access to kqueue, thus we need to duplicate the file descriptor. + /// Now waking is as simple as adding an event to the kqueue. + #[derive(Debug)] + pub struct Waker { + selector: Selector, + token: Token, + } + + impl Waker { + pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> { + selector.try_clone().and_then(|selector| { + selector + .setup_waker(token) + .map(|()| Waker { selector, token }) + }) + } + + pub fn wake(&self) -> io::Result<()> { + self.selector.wake(self.token) + } + } +} + +#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] +pub use self::kqueue::Waker; + +#[cfg(any( + target_os = "dragonfly", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", +))] +mod pipe { + use crate::sys::unix::Selector; + use crate::{Interest, Token}; + + use std::fs::File; + use std::io::{self, Read, Write}; + use std::os::unix::io::FromRawFd; + + /// Waker backed by a unix pipe. + /// + /// Waker controls both the sending and receiving ends and empties the pipe + /// if writing to it (waking) fails. + #[derive(Debug)] + pub struct Waker { + sender: File, + receiver: File, + } + + impl Waker { + pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> { + let mut fds = [-1; 2]; + syscall!(pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK | libc::O_CLOEXEC))?; + // Turn the file descriptors into files first so we're ensured + // they're closed when dropped, e.g. when register below fails. + let sender = unsafe { File::from_raw_fd(fds[1]) }; + let receiver = unsafe { File::from_raw_fd(fds[0]) }; + selector + .register(fds[0], token, Interest::READABLE) + .map(|()| Waker { sender, receiver }) + } + + pub fn wake(&self) -> io::Result<()> { + // The epoll emulation on some illumos systems currently requires + // the pipe buffer to be completely empty for an edge-triggered + // wakeup on the pipe read side. + #[cfg(target_os = "illumos")] + self.empty(); + + match (&self.sender).write(&[1]) { + Ok(_) => Ok(()), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { + // The reading end is full so we'll empty the buffer and try + // again. + self.empty(); + self.wake() + } + Err(ref err) if err.kind() == io::ErrorKind::Interrupted => self.wake(), + Err(err) => Err(err), + } + } + + /// Empty the pipe's buffer, only need to call this if `wake` fails. + /// This ignores any errors. + fn empty(&self) { + let mut buf = [0; 4096]; + loop { + match (&self.receiver).read(&mut buf) { + Ok(n) if n > 0 => continue, + _ => return, + } + } + } + } +} + +#[cfg(any( + target_os = "dragonfly", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", +))] +pub use self::pipe::Waker; diff --git a/third_party/rust/mio/src/sys/windows/afd.rs b/third_party/rust/mio/src/sys/windows/afd.rs new file mode 100644 index 0000000000..6eae3bc035 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/afd.rs @@ -0,0 +1,237 @@ +use ntapi::ntioapi::{IO_STATUS_BLOCK_u, IO_STATUS_BLOCK}; +use ntapi::ntioapi::{NtCancelIoFileEx, NtDeviceIoControlFile}; +use ntapi::ntrtl::RtlNtStatusToDosError; +use std::fmt; +use std::fs::File; +use std::io; +use std::mem::size_of; +use std::os::windows::io::AsRawHandle; +use std::ptr::null_mut; +use winapi::shared::ntdef::{HANDLE, LARGE_INTEGER, NTSTATUS, PVOID, ULONG}; +use winapi::shared::ntstatus::{STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS}; + +const IOCTL_AFD_POLL: ULONG = 0x00012024; + +/// Winsock2 AFD driver instance. +/// +/// All operations are unsafe due to IO_STATUS_BLOCK parameter are being used by Afd driver during STATUS_PENDING before I/O Completion Port returns its result. +#[derive(Debug)] +pub struct Afd { + fd: File, +} + +#[repr(C)] +#[derive(Debug)] +pub struct AfdPollHandleInfo { + pub handle: HANDLE, + pub events: ULONG, + pub status: NTSTATUS, +} + +unsafe impl Send for AfdPollHandleInfo {} + +#[repr(C)] +pub struct AfdPollInfo { + pub timeout: LARGE_INTEGER, + // Can have only value 1. + pub number_of_handles: ULONG, + pub exclusive: ULONG, + pub handles: [AfdPollHandleInfo; 1], +} + +impl fmt::Debug for AfdPollInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AfdPollInfo").finish() + } +} + +impl Afd { + /// Poll `Afd` instance with `AfdPollInfo`. + /// + /// # Unsafety + /// + /// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`). + /// `iosb` needs to be untouched after the call while operation is in effective at ALL TIME except for `cancel` method. + /// So be careful not to `poll` twice while polling. + /// User should deallocate there overlapped value when error to prevent memory leak. + pub unsafe fn poll( + &self, + info: &mut AfdPollInfo, + iosb: *mut IO_STATUS_BLOCK, + overlapped: PVOID, + ) -> io::Result<bool> { + let info_ptr: PVOID = info as *mut _ as PVOID; + (*iosb).u.Status = STATUS_PENDING; + let status = NtDeviceIoControlFile( + self.fd.as_raw_handle(), + null_mut(), + None, + overlapped, + iosb, + IOCTL_AFD_POLL, + info_ptr, + size_of::<AfdPollInfo>() as u32, + info_ptr, + size_of::<AfdPollInfo>() as u32, + ); + match status { + STATUS_SUCCESS => Ok(true), + STATUS_PENDING => Ok(false), + _ => Err(io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + )), + } + } + + /// Cancel previous polled request of `Afd`. + /// + /// iosb needs to be used by `poll` first for valid `cancel`. + /// + /// # Unsafety + /// + /// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`). + /// Use it only with request is still being polled so that you have valid `IO_STATUS_BLOCK` to use. + /// User should NOT deallocate there overlapped value after the `cancel` to prevent double free. + pub unsafe fn cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()> { + if (*iosb).u.Status != STATUS_PENDING { + return Ok(()); + } + + let mut cancel_iosb = IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }; + let status = NtCancelIoFileEx(self.fd.as_raw_handle(), iosb, &mut cancel_iosb); + if status == STATUS_SUCCESS || status == STATUS_NOT_FOUND { + return Ok(()); + } + Err(io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + )) + } +} + +cfg_io_source! { + use std::mem::zeroed; + use std::os::windows::io::{FromRawHandle, RawHandle}; + use std::sync::atomic::{AtomicUsize, Ordering}; + + use miow::iocp::CompletionPort; + use ntapi::ntioapi::{NtCreateFile, FILE_OPEN}; + use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING, USHORT, WCHAR}; + use winapi::um::handleapi::INVALID_HANDLE_VALUE; + use winapi::um::winbase::{SetFileCompletionNotificationModes, FILE_SKIP_SET_EVENT_ON_HANDLE}; + use winapi::um::winnt::{SYNCHRONIZE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + + const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { + Length: size_of::<OBJECT_ATTRIBUTES>() as ULONG, + RootDirectory: null_mut(), + ObjectName: &AFD_OBJ_NAME as *const _ as *mut _, + Attributes: 0, + SecurityDescriptor: null_mut(), + SecurityQualityOfService: null_mut(), + }; + + const AFD_OBJ_NAME: UNICODE_STRING = UNICODE_STRING { + Length: (AFD_HELPER_NAME.len() * size_of::<WCHAR>()) as USHORT, + MaximumLength: (AFD_HELPER_NAME.len() * size_of::<WCHAR>()) as USHORT, + Buffer: AFD_HELPER_NAME.as_ptr() as *mut _, + }; + + const AFD_HELPER_NAME: &[WCHAR] = &[ + '\\' as _, + 'D' as _, + 'e' as _, + 'v' as _, + 'i' as _, + 'c' as _, + 'e' as _, + '\\' as _, + 'A' as _, + 'f' as _, + 'd' as _, + '\\' as _, + 'M' as _, + 'i' as _, + 'o' as _ + ]; + + static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(0); + + impl AfdPollInfo { + pub fn zeroed() -> AfdPollInfo { + unsafe { zeroed() } + } + } + + impl Afd { + /// Create new Afd instance. + pub fn new(cp: &CompletionPort) -> io::Result<Afd> { + let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE; + let mut iosb = IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }; + + unsafe { + let status = NtCreateFile( + &mut afd_helper_handle as *mut _, + SYNCHRONIZE, + &AFD_HELPER_ATTRIBUTES as *const _ as *mut _, + &mut iosb, + null_mut(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + null_mut(), + 0, + ); + if status != STATUS_SUCCESS { + let raw_err = io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + ); + let msg = format!("Failed to open \\Device\\Afd\\Mio: {}", raw_err); + return Err(io::Error::new(raw_err.kind(), msg)); + } + let fd = File::from_raw_handle(afd_helper_handle as RawHandle); + // Increment by 2 to reserve space for other types of handles. + // Non-AFD types (currently only NamedPipe), use odd numbered + // tokens. This allows the selector to differentate between them + // and dispatch events accordingly. + let token = NEXT_TOKEN.fetch_add(2, Ordering::Relaxed) + 2; + let afd = Afd { fd }; + cp.add_handle(token, &afd.fd)?; + match SetFileCompletionNotificationModes( + afd_helper_handle, + FILE_SKIP_SET_EVENT_ON_HANDLE, + ) { + 0 => Err(io::Error::last_os_error()), + _ => Ok(afd), + } + } + } + } +} + +pub const POLL_RECEIVE: u32 = 0b0_0000_0001; +pub const POLL_RECEIVE_EXPEDITED: u32 = 0b0_0000_0010; +pub const POLL_SEND: u32 = 0b0_0000_0100; +pub const POLL_DISCONNECT: u32 = 0b0_0000_1000; +pub const POLL_ABORT: u32 = 0b0_0001_0000; +pub const POLL_LOCAL_CLOSE: u32 = 0b0_0010_0000; +// Not used as it indicated in each event where a connection is connected, not +// just the first time a connection is established. +// Also see https://github.com/piscisaureus/wepoll/commit/8b7b340610f88af3d83f40fb728e7b850b090ece. +pub const POLL_CONNECT: u32 = 0b0_0100_0000; +pub const POLL_ACCEPT: u32 = 0b0_1000_0000; +pub const POLL_CONNECT_FAIL: u32 = 0b1_0000_0000; + +pub const KNOWN_EVENTS: u32 = POLL_RECEIVE + | POLL_RECEIVE_EXPEDITED + | POLL_SEND + | POLL_DISCONNECT + | POLL_ABORT + | POLL_LOCAL_CLOSE + | POLL_ACCEPT + | POLL_CONNECT_FAIL; diff --git a/third_party/rust/mio/src/sys/windows/event.rs b/third_party/rust/mio/src/sys/windows/event.rs new file mode 100644 index 0000000000..a49252a296 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/event.rs @@ -0,0 +1,162 @@ +use std::fmt; + +use miow::iocp::CompletionStatus; + +use super::afd; +use crate::Token; + +#[derive(Clone)] +pub struct Event { + pub flags: u32, + pub data: u64, +} + +pub fn token(event: &Event) -> Token { + Token(event.data as usize) +} + +impl Event { + pub(super) fn new(token: Token) -> Event { + Event { + flags: 0, + data: usize::from(token) as u64, + } + } + + pub(super) fn set_readable(&mut self) { + self.flags |= afd::POLL_RECEIVE + } + + #[cfg(feature = "os-ext")] + pub(super) fn set_writable(&mut self) { + self.flags |= afd::POLL_SEND; + } + + pub(super) fn from_completion_status(status: &CompletionStatus) -> Event { + Event { + flags: status.bytes_transferred(), + data: status.token() as u64, + } + } + + pub(super) fn to_completion_status(&self) -> CompletionStatus { + CompletionStatus::new(self.flags, self.data as usize, std::ptr::null_mut()) + } +} + +pub(crate) const READABLE_FLAGS: u32 = afd::POLL_RECEIVE + | afd::POLL_DISCONNECT + | afd::POLL_ACCEPT + | afd::POLL_ABORT + | afd::POLL_CONNECT_FAIL; +pub(crate) const WRITABLE_FLAGS: u32 = afd::POLL_SEND | afd::POLL_ABORT | afd::POLL_CONNECT_FAIL; +pub(crate) const ERROR_FLAGS: u32 = afd::POLL_CONNECT_FAIL; +pub(crate) const READ_CLOSED_FLAGS: u32 = + afd::POLL_DISCONNECT | afd::POLL_ABORT | afd::POLL_CONNECT_FAIL; +pub(crate) const WRITE_CLOSED_FLAGS: u32 = afd::POLL_ABORT | afd::POLL_CONNECT_FAIL; + +pub fn is_readable(event: &Event) -> bool { + event.flags & READABLE_FLAGS != 0 +} + +pub fn is_writable(event: &Event) -> bool { + event.flags & WRITABLE_FLAGS != 0 +} + +pub fn is_error(event: &Event) -> bool { + event.flags & ERROR_FLAGS != 0 +} + +pub fn is_read_closed(event: &Event) -> bool { + event.flags & READ_CLOSED_FLAGS != 0 +} + +pub fn is_write_closed(event: &Event) -> bool { + event.flags & WRITE_CLOSED_FLAGS != 0 +} + +pub fn is_priority(event: &Event) -> bool { + event.flags & afd::POLL_RECEIVE_EXPEDITED != 0 +} + +pub fn is_aio(_: &Event) -> bool { + // Not supported. + false +} + +pub fn is_lio(_: &Event) -> bool { + // Not supported. + false +} + +pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn check_flags(got: &u32, want: &u32) -> bool { + (got & want) != 0 + } + debug_detail!( + FlagsDetails(u32), + check_flags, + afd::POLL_RECEIVE, + afd::POLL_RECEIVE_EXPEDITED, + afd::POLL_SEND, + afd::POLL_DISCONNECT, + afd::POLL_ABORT, + afd::POLL_LOCAL_CLOSE, + afd::POLL_CONNECT, + afd::POLL_ACCEPT, + afd::POLL_CONNECT_FAIL, + ); + + f.debug_struct("event") + .field("flags", &FlagsDetails(event.flags)) + .field("data", &event.data) + .finish() +} + +pub struct Events { + /// Raw I/O event completions are filled in here by the call to `get_many` + /// on the completion port above. These are then processed to run callbacks + /// which figure out what to do after the event is done. + pub statuses: Box<[CompletionStatus]>, + + /// Literal events returned by `get` to the upwards `EventLoop`. This file + /// doesn't really modify this (except for the waker), instead almost all + /// events are filled in by the `ReadinessQueue` from the `poll` module. + pub events: Vec<Event>, +} + +impl Events { + pub fn with_capacity(cap: usize) -> Events { + // Note that it's possible for the output `events` to grow beyond the + // capacity as it can also include deferred events, but that's certainly + // not the end of the world! + Events { + statuses: vec![CompletionStatus::zero(); cap].into_boxed_slice(), + events: Vec::with_capacity(cap), + } + } + + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + + pub fn capacity(&self) -> usize { + self.events.capacity() + } + + pub fn len(&self) -> usize { + self.events.len() + } + + pub fn get(&self, idx: usize) -> Option<&Event> { + self.events.get(idx) + } + + pub fn clear(&mut self) { + self.events.clear(); + for status in self.statuses.iter_mut() { + *status = CompletionStatus::zero(); + } + } +} diff --git a/third_party/rust/mio/src/sys/windows/io_status_block.rs b/third_party/rust/mio/src/sys/windows/io_status_block.rs new file mode 100644 index 0000000000..3e60334961 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/io_status_block.rs @@ -0,0 +1,40 @@ +use std::fmt; +use std::ops::{Deref, DerefMut}; + +use ntapi::ntioapi::IO_STATUS_BLOCK; + +pub struct IoStatusBlock(IO_STATUS_BLOCK); + +cfg_io_source! { + use ntapi::ntioapi::IO_STATUS_BLOCK_u; + + impl IoStatusBlock { + pub fn zeroed() -> Self { + Self(IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }) + } + } +} + +unsafe impl Send for IoStatusBlock {} + +impl Deref for IoStatusBlock { + type Target = IO_STATUS_BLOCK; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for IoStatusBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Debug for IoStatusBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IoStatusBlock").finish() + } +} diff --git a/third_party/rust/mio/src/sys/windows/mod.rs b/third_party/rust/mio/src/sys/windows/mod.rs new file mode 100644 index 0000000000..7048351355 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/mod.rs @@ -0,0 +1,147 @@ +mod afd; +mod io_status_block; + +pub mod event; +pub use event::{Event, Events}; + +mod selector; +pub use selector::{Selector, SelectorInner, SockState}; + +mod overlapped; +use overlapped::Overlapped; + +// Macros must be defined before the modules that use them +cfg_net! { + /// Helper macro to execute a system call that returns an `io::Result`. + // + // Macro must be defined before any modules that uses them. + macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{ + let res = unsafe { $fn($($arg, )*) }; + if $err_test(&res, &$err_value) { + Err(io::Error::last_os_error()) + } else { + Ok(res) + } + }}; + } + + mod net; + + pub(crate) mod tcp; + pub(crate) mod udp; +} + +cfg_os_ext! { + pub(crate) mod named_pipe; +} + +mod waker; +pub(crate) use waker::Waker; + +cfg_io_source! { + use std::io; + use std::os::windows::io::RawSocket; + use std::pin::Pin; + use std::sync::{Arc, Mutex}; + + use crate::{Interest, Registry, Token}; + + struct InternalState { + selector: Arc<SelectorInner>, + token: Token, + interests: Interest, + sock_state: Pin<Arc<Mutex<SockState>>>, + } + + impl Drop for InternalState { + fn drop(&mut self) { + let mut sock_state = self.sock_state.lock().unwrap(); + sock_state.mark_delete(); + } + } + + pub struct IoSourceState { + // This is `None` if the socket has not yet been registered. + // + // We box the internal state to not increase the size on the stack as the + // type might move around a lot. + inner: Option<Box<InternalState>>, + } + + impl IoSourceState { + pub fn new() -> IoSourceState { + IoSourceState { inner: None } + } + + pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R> + where + F: FnOnce(&T) -> io::Result<R>, + { + let result = f(io); + if let Err(ref e) = result { + if e.kind() == io::ErrorKind::WouldBlock { + self.inner.as_ref().map_or(Ok(()), |state| { + state + .selector + .reregister(state.sock_state.clone(), state.token, state.interests) + })?; + } + } + result + } + + pub fn register( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + socket: RawSocket, + ) -> io::Result<()> { + if self.inner.is_some() { + Err(io::ErrorKind::AlreadyExists.into()) + } else { + registry + .selector() + .register(socket, token, interests) + .map(|state| { + self.inner = Some(Box::new(state)); + }) + } + } + + pub fn reregister( + &mut self, + registry: &Registry, + token: Token, + interests: Interest, + ) -> io::Result<()> { + match self.inner.as_mut() { + Some(state) => { + registry + .selector() + .reregister(state.sock_state.clone(), token, interests) + .map(|()| { + state.token = token; + state.interests = interests; + }) + } + None => Err(io::ErrorKind::NotFound.into()), + } + } + + pub fn deregister(&mut self) -> io::Result<()> { + match self.inner.as_mut() { + Some(state) => { + { + let mut sock_state = state.sock_state.lock().unwrap(); + sock_state.mark_delete(); + } + self.inner = None; + Ok(()) + } + None => Err(io::ErrorKind::NotFound.into()), + } + } + } +} diff --git a/third_party/rust/mio/src/sys/windows/named_pipe.rs b/third_party/rust/mio/src/sys/windows/named_pipe.rs new file mode 100644 index 0000000000..adda51f23c --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/named_pipe.rs @@ -0,0 +1,782 @@ +use std::ffi::OsStr; +use std::io::{self, Read, Write}; +use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use std::sync::atomic::Ordering::{Relaxed, SeqCst}; +use std::sync::atomic::{AtomicBool, AtomicUsize}; +use std::sync::{Arc, Mutex}; +use std::{fmt, mem, slice}; + +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, OVERLAPPED_ENTRY}; + +use crate::event::Source; +use crate::sys::windows::{Event, Overlapped}; +use crate::Registry; +use crate::{Interest, Token}; + +/// Non-blocking windows named pipe. +/// +/// This structure internally contains a `HANDLE` which represents the named +/// pipe, and also maintains state associated with the mio event loop and active +/// I/O operations that have been scheduled to translate IOCP to a readiness +/// model. +/// +/// Note, IOCP is a *completion* based model whereas mio is a *readiness* based +/// model. To bridge this, `NamedPipe` performs internal buffering. Writes are +/// written to an internal buffer and the buffer is submitted to IOCP. IOCP +/// reads are submitted using internal buffers and `NamedPipe::read` reads from +/// this internal buffer. +/// +/// # Trait implementations +/// +/// The `Read` and `Write` traits are implemented for `NamedPipe` and for +/// `&NamedPipe`. This represents that a named pipe can be concurrently read and +/// written to and also can be read and written to at all. Typically a named +/// pipe needs to be connected to a client before it can be read or written, +/// however. +/// +/// Note that for I/O operations on a named pipe to succeed then the named pipe +/// needs to be associated with an event loop. Until this happens all I/O +/// operations will return a "would block" error. +/// +/// # Managing connections +/// +/// The `NamedPipe` type supports a `connect` method to connect to a client and +/// a `disconnect` method to disconnect from that client. These two methods only +/// work once a named pipe is associated with an event loop. +/// +/// The `connect` method will succeed asynchronously and a completion can be +/// detected once the object receives a writable notification. +/// +/// # Named pipe clients +/// +/// Currently to create a client of a named pipe server then you can use the +/// `OpenOptions` type in the standard library to create a `File` that connects +/// to a named pipe. Afterwards you can use the `into_raw_handle` method coupled +/// with the `NamedPipe::from_raw_handle` method to convert that to a named pipe +/// that can operate asynchronously. Don't forget to pass the +/// `FILE_FLAG_OVERLAPPED` flag when opening the `File`. +pub struct NamedPipe { + inner: Arc<Inner>, +} + +/// # Notes +/// +/// The memory layout of this structure must be fixed as the +/// `ptr_from_*_overlapped` methods depend on it, see the `ptr_from` 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<Io>, + pool: Mutex<BufferPool>, +} + +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 *mut Overlapped).wrapping_sub(1) 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 *mut Overlapped).wrapping_sub(2) as *const Inner + } +} + +#[test] +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; + 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 { + // Uniquely identifies the selector associated with this named pipe + cp: Option<Arc<CompletionPort>>, + // Token used to identify events + token: Option<Token>, + read: State, + write: State, + connect_error: Option<io::Error>, +} + +#[derive(Debug)] +enum State { + None, + Pending(Vec<u8>, usize), + Ok(Vec<u8>, usize), + Err(io::Error), +} + +// Odd tokens are for named pipes +static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(1); + +fn would_block() -> io::Error { + io::ErrorKind::WouldBlock.into() +} + +impl NamedPipe { + /// Creates a new named pipe at the specified `addr` given a "reasonable + /// set" of initial configuration options. + pub fn new<A: AsRef<OsStr>>(addr: A) -> io::Result<NamedPipe> { + let pipe = pipe::NamedPipe::new(addr)?; + // Safety: nothing actually unsafe about this. The trait fn includes + // `unsafe`. + Ok(unsafe { NamedPipe::from_raw_handle(pipe.into_raw_handle()) }) + } + + /// Attempts to call `ConnectNamedPipe`, if possible. + /// + /// This function will attempt to connect this pipe to a client in an + /// asynchronous fashion. If the function immediately establishes a + /// connection to a client then `Ok(())` is returned. Otherwise if a + /// connection attempt was issued and is now in progress then a "would + /// block" error is returned. + /// + /// When the connection is finished then this object will be flagged as + /// being ready for a write, or otherwise in the writable state. + /// + /// # Errors + /// + /// This function will return a "would block" error if the pipe has not yet + /// been registered with an event loop, if the connection operation has + /// previously been issued but has not yet completed, or if the connect + /// itself was issued and didn't finish immediately. + /// + /// Normal I/O errors from the call to `ConnectNamedPipe` are returned + /// immediately. + pub fn connect(&self) -> io::Result<()> { + // "Acquire the connecting lock" or otherwise just make sure we're the + // only operation that's using the `connect` overlapped instance. + if self.inner.connecting.swap(true, SeqCst) { + return Err(would_block()); + } + + // Now that we've flagged ourselves in the connecting state, issue the + // connection attempt. Afterwards interpret the return value and set + // internal state accordingly. + let res = unsafe { + let overlapped = self.inner.connect.as_ptr() as *mut _; + self.inner.handle.connect_overlapped(overlapped) + }; + + match res { + // The connection operation finished immediately, so let's schedule + // reads/writes and such. + Ok(true) => { + self.inner.connecting.store(false, SeqCst); + Inner::post_register(&self.inner, None); + Ok(()) + } + + // If the overlapped operation was successful and didn't finish + // immediately then we forget a copy of the arc we hold + // internally. This ensures that when the completion status comes + // in for the I/O operation finishing it'll have a reference + // associated with it and our data will still be valid. The + // `connect_done` function will "reify" this forgotten pointer to + // drop the refcount on the other side. + Ok(false) => { + mem::forget(self.inner.clone()); + Err(would_block()) + } + + Err(e) => { + self.inner.connecting.store(false, SeqCst); + Err(e) + } + } + } + + /// Takes any internal error that has happened after the last I/O operation + /// which hasn't been retrieved yet. + /// + /// This is particularly useful when detecting failed attempts to `connect`. + /// After a completed `connect` flags this pipe as writable then callers + /// must invoke this method to determine whether the connection actually + /// succeeded. If this function returns `None` then a client is connected, + /// otherwise it returns an error of what happened and a client shouldn't be + /// connected. + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + Ok(self.inner.io.lock().unwrap().connect_error.take()) + } + + /// Disconnects this named pipe from a connected client. + /// + /// This function will disconnect the pipe from a connected client, if any, + /// transitively calling the `DisconnectNamedPipe` function. + /// + /// After a `disconnect` is issued, then a `connect` may be called again to + /// connect to another client. + pub fn disconnect(&self) -> io::Result<()> { + self.inner.handle.disconnect() + } +} + +impl FromRawHandle for NamedPipe { + unsafe fn from_raw_handle(handle: RawHandle) -> NamedPipe { + NamedPipe { + inner: Arc::new(Inner { + // Safety: not really unsafe + handle: pipe::NamedPipe::from_raw_handle(handle), + // transmutes to straddle winapi versions (mio 0.6 is on an + // older winapi) + connect: Overlapped::new(connect_done), + connecting: AtomicBool::new(false), + read: Overlapped::new(read_done), + write: Overlapped::new(write_done), + io: Mutex::new(Io { + cp: None, + token: None, + read: State::None, + write: State::None, + connect_error: None, + }), + pool: Mutex::new(BufferPool::with_capacity(2)), + }), + } + } +} + +impl Read for NamedPipe { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + <&NamedPipe as Read>::read(&mut &*self, buf) + } +} + +impl Write for NamedPipe { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + <&NamedPipe as Write>::write(&mut &*self, buf) + } + + fn flush(&mut self) -> io::Result<()> { + <&NamedPipe as Write>::flush(&mut &*self) + } +} + +impl<'a> Read for &'a NamedPipe { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let mut state = self.inner.io.lock().unwrap(); + + if state.token.is_none() { + return Err(would_block()); + } + + match mem::replace(&mut state.read, State::None) { + // In theory not possible with `token` checked above, + // but return would block for now. + State::None => Err(would_block()), + + // A read is in flight, still waiting for it to finish + State::Pending(buf, amt) => { + state.read = State::Pending(buf, amt); + Err(would_block()) + } + + // We previously read something into `data`, try to copy out some + // data. If we copy out all the data schedule a new read and + // otherwise store the buffer to get read later. + State::Ok(data, cur) => { + let n = { + let mut remaining = &data[cur..]; + remaining.read(buf)? + }; + let next = cur + n; + if next != data.len() { + state.read = State::Ok(data, next); + } else { + self.inner.put_buffer(data); + Inner::schedule_read(&self.inner, &mut state, None); + } + Ok(n) + } + + // Looks like an in-flight read hit an error, return that here while + // we schedule a new one. + State::Err(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) + } + } + } + } +} + +impl<'a> Write for &'a NamedPipe { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + // Make sure there's no writes pending + let mut io = self.inner.io.lock().unwrap(); + + if io.token.is_none() { + return Err(would_block()); + } + + match io.write { + State::None => {} + State::Err(_) => match mem::replace(&mut io.write, State::None) { + State::Err(e) => return Err(e), + // `io` is locked, so this branch is unreachable + _ => unreachable!(), + }, + // any other state should be handled in `write_done` + _ => { + return Err(would_block()); + } + } + + // Move `buf` onto the heap and fire off the write + let mut owned_buf = self.inner.get_buffer(); + owned_buf.extend(buf); + match Inner::maybe_schedule_write(&self.inner, owned_buf, 0, &mut io)? { + // Some bytes are written immediately + Some(n) => Ok(n), + // Write operation is anqueued for whole buffer + None => Ok(buf.len()), + } + } + + 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(); + + io.cp = Some(selector.clone_port()); + + let inner_token = NEXT_TOKEN.fetch_add(2, Relaxed) + 2; + selector + .inner + .cp + .add_handle(inner_token, &self.inner.handle)?; + } + + io.token = Some(token); + drop(io); + + Inner::post_register(&self.inner, 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); + drop(io); + + Inner::post_register(&self.inner, 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(io::Error::new( + io::ErrorKind::NotFound, + "I/O source not registered with `Registry`", + )); + } + + io.token = None; + Ok(()) + } +} + +impl AsRawHandle for NamedPipe { + fn as_raw_handle(&self) -> RawHandle { + self.inner.handle.as_raw_handle() + } +} + +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) { + // Cancel pending reads/connects, but don't cancel writes to ensure that + // everything is flushed out. + unsafe { + if self.inner.connecting.load(SeqCst) { + drop(cancel(&self.inner.handle, &self.inner.connect)); + } + + let io = self.inner.io.lock().unwrap(); + if let State::Pending(..) = io.read { + drop(cancel(&self.inner.handle, &self.inner.read)); + } + } + } +} + +impl Inner { + /// Schedules a read to happen in the background, executing an overlapped + /// operation. + /// + /// This function returns `true` if a normal error happens or if the read + /// is scheduled in the background. If the pipe is no longer connected + /// (ERROR_PIPE_LISTENING) then `false` is returned and no read is + /// scheduled. + fn schedule_read(me: &Arc<Inner>, io: &mut Io, events: Option<&mut Vec<Event>>) -> bool { + // Check to see if a read is already scheduled/completed + match io.read { + State::None => {} + _ => return true, + } + + // Allocate a buffer and schedule the read. + let mut buf = me.get_buffer(); + let e = unsafe { + let overlapped = me.read.as_ptr() as *mut _; + let slice = slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.capacity()); + me.handle.read_overlapped(slice, overlapped) + }; + + match e { + // See `NamedPipe::connect` above for the rationale behind `forget` + Ok(_) => { + io.read = State::Pending(buf, 0); // 0 is ignored on read side + mem::forget(me.clone()); + true + } + + // If ERROR_PIPE_LISTENING happens then it's not a real read error, + // we just need to wait for a connect. + Err(ref e) if e.raw_os_error() == Some(ERROR_PIPE_LISTENING as i32) => false, + + // If some other error happened, though, we're now readable to give + // out the error. + Err(e) => { + io.read = State::Err(e); + io.notify_readable(events); + true + } + } + } + + /// Maybe schedules overlapped write operation. + /// + /// * `None` means that overlapped operation was enqueued + /// * `Some(n)` means that `n` bytes was immediately written. + /// Note, that `write_done` will fire anyway to clean up the state. + fn maybe_schedule_write( + me: &Arc<Inner>, + buf: Vec<u8>, + pos: usize, + io: &mut Io, + ) -> io::Result<Option<usize>> { + // Very similar to `schedule_read` above, just done for the write half. + let e = unsafe { + let overlapped = me.write.as_ptr() as *mut _; + me.handle.write_overlapped(&buf[pos..], overlapped) + }; + + // See `connect` above for the rationale behind `forget` + match e { + // `n` bytes are written immediately + Ok(Some(n)) => { + io.write = State::Ok(buf, pos); + mem::forget(me.clone()); + Ok(Some(n)) + } + // write operation is enqueued + Ok(None) => { + io.write = State::Pending(buf, pos); + mem::forget(me.clone()); + Ok(None) + } + Err(e) => Err(e), + } + } + + fn schedule_write( + me: &Arc<Inner>, + buf: Vec<u8>, + pos: usize, + io: &mut Io, + events: Option<&mut Vec<Event>>, + ) { + match Inner::maybe_schedule_write(me, buf, pos, io) { + Ok(Some(_)) => { + // immediate result will be handled in `write_done`, + // so we'll reinterpret the `Ok` state + let state = mem::replace(&mut io.write, State::None); + io.write = match state { + State::Ok(buf, pos) => State::Pending(buf, pos), + // io is locked, so this branch is unreachable + _ => unreachable!(), + }; + mem::forget(me.clone()); + } + Ok(None) => (), + Err(e) => { + io.write = State::Err(e); + io.notify_writable(events); + } + } + } + + fn post_register(me: &Arc<Inner>, mut events: Option<&mut Vec<Event>>) { + let mut io = me.io.lock().unwrap(); + #[allow(clippy::needless_option_as_deref)] + if Inner::schedule_read(me, &mut io, events.as_deref_mut()) { + if let State::None = io.write { + io.notify_writable(events); + } + } + } + + fn get_buffer(&self) -> Vec<u8> { + self.pool.lock().unwrap().get(4 * 1024) + } + + fn put_buffer(&self, buf: Vec<u8>) { + self.pool.lock().unwrap().put(buf) + } +} + +unsafe fn cancel<T: AsRawHandle>(handle: &T, overlapped: &Overlapped) -> io::Result<()> { + let ret = CancelIoEx(handle.as_raw_handle(), overlapped.as_ptr() as *mut _); + // `CancelIoEx` returns 0 on error: + // https://docs.microsoft.com/en-us/windows/win32/fileio/cancelioex-func + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +fn connect_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) { + let status = CompletionStatus::from_entry(status); + + // Acquire the `Arc<Inner>`. 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(Inner::ptr_from_conn_overlapped(status.overlapped())) }; + + // Flag ourselves as no longer using the `connect` overlapped instances. + let prev = me.connecting.swap(false, SeqCst); + assert!(prev, "NamedPipe was not previously connecting"); + + // Stash away our connect error if one happened + debug_assert_eq!(status.bytes_transferred(), 0); + unsafe { + match me.handle.result(status.overlapped()) { + Ok(n) => debug_assert_eq!(n, 0), + Err(e) => me.io.lock().unwrap().connect_error = Some(e), + } + } + + // We essentially just finished a registration, so kick off a + // read and register write readiness. + Inner::post_register(&me, events); +} + +fn read_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) { + let status = CompletionStatus::from_entry(status); + + // Acquire the `FromRawArc<Inner>`. 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(Inner::ptr_from_read_overlapped(status.overlapped())) }; + + // Move from the `Pending` to `Ok` state. + let mut io = me.io.lock().unwrap(); + let mut buf = match mem::replace(&mut io.read, State::None) { + State::Pending(buf, _) => buf, + _ => unreachable!(), + }; + unsafe { + match me.handle.result(status.overlapped()) { + Ok(n) => { + debug_assert_eq!(status.bytes_transferred() as usize, n); + buf.set_len(status.bytes_transferred() as usize); + io.read = State::Ok(buf, 0); + } + Err(e) => { + debug_assert_eq!(status.bytes_transferred(), 0); + io.read = State::Err(e); + } + } + } + + // Flag our readiness that we've got data. + io.notify_readable(events); +} + +fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) { + let status = CompletionStatus::from_entry(status); + + // Acquire the `Arc<Inner>`. 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(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. + let mut io = me.io.lock().unwrap(); + let (buf, pos) = match mem::replace(&mut io.write, State::None) { + // `Ok` here means, that the operation was completed immediately + // `bytes_transferred` is already reported to a client + State::Ok(..) => { + io.notify_writable(events); + return; + } + State::Pending(buf, pos) => (buf, pos), + _ => unreachable!(), + }; + + unsafe { + match me.handle.result(status.overlapped()) { + Ok(n) => { + debug_assert_eq!(status.bytes_transferred() as usize, n); + let new_pos = pos + (status.bytes_transferred() as usize); + if new_pos == buf.len() { + me.put_buffer(buf); + io.notify_writable(events); + } else { + Inner::schedule_write(&me, buf, new_pos, &mut io, events); + } + } + Err(e) => { + debug_assert_eq!(status.bytes_transferred(), 0); + io.write = State::Err(e); + io.notify_writable(events); + } + } + } +} + +impl Io { + 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(io::Error::new( + io::ErrorKind::NotFound, + "I/O source not registered with `Registry`", + )), + _ => Ok(()), + } + } + + fn notify_readable(&self, 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 { + let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status()); + } + } + } + + fn notify_writable(&self, 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 { + let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status()); + } + } + } +} + +struct BufferPool { + pool: Vec<Vec<u8>>, +} + +impl BufferPool { + fn with_capacity(cap: usize) -> BufferPool { + BufferPool { + pool: Vec::with_capacity(cap), + } + } + + fn get(&mut self, default_cap: usize) -> Vec<u8> { + self.pool + .pop() + .unwrap_or_else(|| Vec::with_capacity(default_cap)) + } + + fn put(&mut self, mut buf: Vec<u8>) { + if self.pool.len() < self.pool.capacity() { + unsafe { + buf.set_len(0); + } + self.pool.push(buf); + } + } +} diff --git a/third_party/rust/mio/src/sys/windows/net.rs b/third_party/rust/mio/src/sys/windows/net.rs new file mode 100644 index 0000000000..db1896f198 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/net.rs @@ -0,0 +1,108 @@ +use std::io; +use std::mem; +use std::net::SocketAddr; +use std::sync::Once; + +use winapi::ctypes::c_int; +use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR}; +use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR}; +use winapi::shared::ws2def::{ADDRESS_FAMILY, AF_INET, AF_INET6, SOCKADDR, SOCKADDR_IN}; +use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH_u, SOCKADDR_IN6_LH}; +use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET}; + +/// Initialise the network stack for Windows. +pub(crate) fn init() { + static INIT: Once = Once::new(); + INIT.call_once(|| { + // Let standard library call `WSAStartup` for us, we can't do it + // ourselves because otherwise using any type in `std::net` would panic + // when it tries to call `WSAStartup` a second time. + drop(std::net::UdpSocket::bind("127.0.0.1:0")); + }); +} + +/// Create a new non-blocking socket. +pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> { + use winapi::um::winsock2::{PF_INET, PF_INET6}; + + let domain = match addr { + SocketAddr::V4(..) => PF_INET, + SocketAddr::V6(..) => PF_INET6, + }; + + new_socket(domain, socket_type) +} + +pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result<SOCKET> { + syscall!( + socket(domain, socket_type, 0), + PartialEq::eq, + INVALID_SOCKET + ) + .and_then(|socket| { + syscall!(ioctlsocket(socket, FIONBIO, &mut 1), PartialEq::ne, 0).map(|_| socket as SOCKET) + }) +} + +/// A type with the same memory layout as `SOCKADDR`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `SOCKADDR_STORAGE` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: SOCKADDR_IN, + v6: SOCKADDR_IN6_LH, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const SOCKADDR { + self as *const _ as *const SOCKADDR + } +} + +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, c_int) { + match addr { + SocketAddr::V4(ref addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + let sin_addr = unsafe { + let mut s_un = mem::zeroed::<in_addr_S_un>(); + *s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + IN_ADDR { S_un: s_un } + }; + + let sockaddr_in = SOCKADDR_IN { + sin_family: AF_INET as ADDRESS_FAMILY, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + }; + + let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + (sockaddr, mem::size_of::<SOCKADDR_IN>() as c_int) + } + SocketAddr::V6(ref addr) => { + let sin6_addr = unsafe { + let mut u = mem::zeroed::<in6_addr_u>(); + *u.Byte_mut() = addr.ip().octets(); + IN6_ADDR { u } + }; + let u = unsafe { + let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>(); + *u.sin6_scope_id_mut() = addr.scope_id(); + u + }; + + let sockaddr_in6 = SOCKADDR_IN6_LH { + sin6_family: AF_INET6 as ADDRESS_FAMILY, + sin6_port: addr.port().to_be(), + sin6_addr, + sin6_flowinfo: addr.flowinfo(), + u, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + (sockaddr, mem::size_of::<SOCKADDR_IN6_LH>() as c_int) + } + } +} diff --git a/third_party/rust/mio/src/sys/windows/overlapped.rs b/third_party/rust/mio/src/sys/windows/overlapped.rs new file mode 100644 index 0000000000..837b78b60a --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/overlapped.rs @@ -0,0 +1,37 @@ +use crate::sys::windows::Event; + +use std::cell::UnsafeCell; +use std::fmt; + +#[cfg(feature = "os-ext")] +use winapi::um::minwinbase::OVERLAPPED; +use winapi::um::minwinbase::OVERLAPPED_ENTRY; + +#[repr(C)] +pub(crate) struct Overlapped { + inner: UnsafeCell<miow::Overlapped>, + pub(crate) callback: fn(&OVERLAPPED_ENTRY, Option<&mut Vec<Event>>), +} + +#[cfg(feature = "os-ext")] +impl Overlapped { + pub(crate) fn new(cb: fn(&OVERLAPPED_ENTRY, Option<&mut Vec<Event>>)) -> Overlapped { + Overlapped { + inner: UnsafeCell::new(miow::Overlapped::zero()), + callback: cb, + } + } + + pub(crate) fn as_ptr(&self) -> *const OVERLAPPED { + unsafe { (*self.inner.get()).raw() } + } +} + +impl fmt::Debug for Overlapped { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Overlapped").finish() + } +} + +unsafe impl Send for Overlapped {} +unsafe impl Sync for Overlapped {} diff --git a/third_party/rust/mio/src/sys/windows/selector.rs b/third_party/rust/mio/src/sys/windows/selector.rs new file mode 100644 index 0000000000..133fefe895 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/selector.rs @@ -0,0 +1,748 @@ +use super::afd::{self, Afd, AfdPollInfo}; +use super::io_status_block::IoStatusBlock; +use super::Event; +use crate::sys::Events; + +cfg_net! { + use crate::sys::event::{ + ERROR_FLAGS, READABLE_FLAGS, READ_CLOSED_FLAGS, WRITABLE_FLAGS, WRITE_CLOSED_FLAGS, + }; + use crate::Interest; +} + +use miow::iocp::{CompletionPort, CompletionStatus}; +use std::collections::VecDeque; +use std::io; +use std::marker::PhantomPinned; +use std::os::windows::io::RawSocket; +use std::pin::Pin; +#[cfg(debug_assertions)] +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use winapi::shared::ntdef::NT_SUCCESS; +use winapi::shared::ntdef::{HANDLE, PVOID}; +use winapi::shared::ntstatus::STATUS_CANCELLED; +use winapi::shared::winerror::{ERROR_INVALID_HANDLE, ERROR_IO_PENDING, WAIT_TIMEOUT}; +use winapi::um::minwinbase::OVERLAPPED; + +#[derive(Debug)] +struct AfdGroup { + #[cfg_attr(not(feature = "net"), allow(dead_code))] + cp: Arc<CompletionPort>, + afd_group: Mutex<Vec<Arc<Afd>>>, +} + +impl AfdGroup { + pub fn new(cp: Arc<CompletionPort>) -> AfdGroup { + AfdGroup { + afd_group: Mutex::new(Vec::new()), + cp, + } + } + + pub fn release_unused_afd(&self) { + let mut afd_group = self.afd_group.lock().unwrap(); + afd_group.retain(|g| Arc::strong_count(g) > 1); + } +} + +cfg_io_source! { + const POLL_GROUP__MAX_GROUP_SIZE: usize = 32; + + impl AfdGroup { + pub fn acquire(&self) -> io::Result<Arc<Afd>> { + let mut afd_group = self.afd_group.lock().unwrap(); + if afd_group.len() == 0 { + self._alloc_afd_group(&mut afd_group)?; + } else { + // + 1 reference in Vec + if Arc::strong_count(afd_group.last().unwrap()) > POLL_GROUP__MAX_GROUP_SIZE { + self._alloc_afd_group(&mut afd_group)?; + } + } + + match afd_group.last() { + Some(arc) => Ok(arc.clone()), + None => unreachable!( + "Cannot acquire afd, {:#?}, afd_group: {:#?}", + self, afd_group + ), + } + } + + fn _alloc_afd_group(&self, afd_group: &mut Vec<Arc<Afd>>) -> io::Result<()> { + let afd = Afd::new(&self.cp)?; + let arc = Arc::new(afd); + afd_group.push(arc); + Ok(()) + } + } +} + +#[derive(Debug)] +enum SockPollStatus { + Idle, + Pending, + Cancelled, +} + +#[derive(Debug)] +pub struct SockState { + iosb: IoStatusBlock, + poll_info: AfdPollInfo, + afd: Arc<Afd>, + + base_socket: RawSocket, + + user_evts: u32, + pending_evts: u32, + + user_data: u64, + + poll_status: SockPollStatus, + delete_pending: bool, + + // last raw os error + error: Option<i32>, + + _pinned: PhantomPinned, +} + +impl SockState { + fn update(&mut self, self_arc: &Pin<Arc<Mutex<SockState>>>) -> io::Result<()> { + assert!(!self.delete_pending); + + // make sure to reset previous error before a new update + self.error = None; + + if let SockPollStatus::Pending = self.poll_status { + if (self.user_evts & afd::KNOWN_EVENTS & !self.pending_evts) == 0 { + /* All the events the user is interested in are already being monitored by + * the pending poll operation. It might spuriously complete because of an + * event that we're no longer interested in; when that happens we'll submit + * a new poll operation with the updated event mask. */ + } else { + /* A poll operation is already pending, but it's not monitoring for all the + * events that the user is interested in. Therefore, cancel the pending + * poll operation; when we receive it's completion package, a new poll + * operation will be submitted with the correct event mask. */ + if let Err(e) = self.cancel() { + self.error = e.raw_os_error(); + return Err(e); + } + return Ok(()); + } + } else if let SockPollStatus::Cancelled = self.poll_status { + /* The poll operation has already been cancelled, we're still waiting for + * it to return. For now, there's nothing that needs to be done. */ + } else if let SockPollStatus::Idle = self.poll_status { + /* No poll operation is pending; start one. */ + self.poll_info.exclusive = 0; + self.poll_info.number_of_handles = 1; + *unsafe { self.poll_info.timeout.QuadPart_mut() } = std::i64::MAX; + self.poll_info.handles[0].handle = self.base_socket as HANDLE; + self.poll_info.handles[0].status = 0; + self.poll_info.handles[0].events = self.user_evts | afd::POLL_LOCAL_CLOSE; + + // Increase the ref count as the memory will be used by the kernel. + let overlapped_ptr = into_overlapped(self_arc.clone()); + + let result = unsafe { + self.afd + .poll(&mut self.poll_info, &mut *self.iosb, overlapped_ptr) + }; + if let Err(e) = result { + let code = e.raw_os_error().unwrap(); + if code == ERROR_IO_PENDING as i32 { + /* Overlapped poll operation in progress; this is expected. */ + } else { + // Since the operation failed it means the kernel won't be + // using the memory any more. + drop(from_overlapped(overlapped_ptr as *mut _)); + if code == ERROR_INVALID_HANDLE as i32 { + /* Socket closed; it'll be dropped. */ + self.mark_delete(); + return Ok(()); + } else { + self.error = e.raw_os_error(); + return Err(e); + } + } + } + + self.poll_status = SockPollStatus::Pending; + self.pending_evts = self.user_evts; + } else { + unreachable!("Invalid poll status during update, {:#?}", self) + } + + Ok(()) + } + + fn cancel(&mut self) -> io::Result<()> { + match self.poll_status { + SockPollStatus::Pending => {} + _ => unreachable!("Invalid poll status during cancel, {:#?}", self), + }; + unsafe { + self.afd.cancel(&mut *self.iosb)?; + } + self.poll_status = SockPollStatus::Cancelled; + self.pending_evts = 0; + Ok(()) + } + + // This is the function called from the overlapped using as Arc<Mutex<SockState>>. Watch out for reference counting. + fn feed_event(&mut self) -> Option<Event> { + self.poll_status = SockPollStatus::Idle; + self.pending_evts = 0; + + let mut afd_events = 0; + // We use the status info in IO_STATUS_BLOCK to determine the socket poll status. It is unsafe to use a pointer of IO_STATUS_BLOCK. + unsafe { + if self.delete_pending { + return None; + } else if self.iosb.u.Status == STATUS_CANCELLED { + /* The poll request was cancelled by CancelIoEx. */ + } else if !NT_SUCCESS(self.iosb.u.Status) { + /* The overlapped request itself failed in an unexpected way. */ + afd_events = afd::POLL_CONNECT_FAIL; + } else if self.poll_info.number_of_handles < 1 { + /* This poll operation succeeded but didn't report any socket events. */ + } else if self.poll_info.handles[0].events & afd::POLL_LOCAL_CLOSE != 0 { + /* The poll operation reported that the socket was closed. */ + self.mark_delete(); + return None; + } else { + afd_events = self.poll_info.handles[0].events; + } + } + + afd_events &= self.user_evts; + + if afd_events == 0 { + return None; + } + + // In mio, we have to simulate Edge-triggered behavior to match API usage. + // The strategy here is to intercept all read/write from user that could cause WouldBlock usage, + // then reregister the socket to reset the interests. + self.user_evts &= !afd_events; + + Some(Event { + data: self.user_data, + flags: afd_events, + }) + } + + pub fn is_pending_deletion(&self) -> bool { + self.delete_pending + } + + pub fn mark_delete(&mut self) { + if !self.delete_pending { + if let SockPollStatus::Pending = self.poll_status { + drop(self.cancel()); + } + + self.delete_pending = true; + } + } + + fn has_error(&self) -> bool { + self.error.is_some() + } +} + +cfg_io_source! { + impl SockState { + fn new(raw_socket: RawSocket, afd: Arc<Afd>) -> io::Result<SockState> { + Ok(SockState { + iosb: IoStatusBlock::zeroed(), + poll_info: AfdPollInfo::zeroed(), + afd, + base_socket: get_base_socket(raw_socket)?, + user_evts: 0, + pending_evts: 0, + user_data: 0, + poll_status: SockPollStatus::Idle, + delete_pending: false, + error: None, + _pinned: PhantomPinned, + }) + } + + /// True if need to be added on update queue, false otherwise. + fn set_event(&mut self, ev: Event) -> bool { + /* afd::POLL_CONNECT_FAIL and afd::POLL_ABORT are always reported, even when not requested by the caller. */ + let events = ev.flags | afd::POLL_CONNECT_FAIL | afd::POLL_ABORT; + + self.user_evts = events; + self.user_data = ev.data; + + (events & !self.pending_evts) != 0 + } + } +} + +impl Drop for SockState { + fn drop(&mut self) { + self.mark_delete(); + } +} + +/// Converts the pointer to a `SockState` into a raw pointer. +/// To revert see `from_overlapped`. +fn into_overlapped(sock_state: Pin<Arc<Mutex<SockState>>>) -> PVOID { + let overlapped_ptr: *const Mutex<SockState> = + unsafe { Arc::into_raw(Pin::into_inner_unchecked(sock_state)) }; + overlapped_ptr as *mut _ +} + +/// Convert a raw overlapped pointer into a reference to `SockState`. +/// Reverts `into_overlapped`. +fn from_overlapped(ptr: *mut OVERLAPPED) -> Pin<Arc<Mutex<SockState>>> { + let sock_ptr: *const Mutex<SockState> = ptr as *const _; + unsafe { Pin::new_unchecked(Arc::from_raw(sock_ptr)) } +} + +/// Each Selector has a globally unique(ish) ID associated with it. This ID +/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first +/// registered with the `Selector`. If a type that is previously associated with +/// a `Selector` attempts to register itself with a different `Selector`, the +/// operation will return with an error. This matches windows behavior. +#[cfg(debug_assertions)] +static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + +/// Windows implementaion of `sys::Selector` +/// +/// Edge-triggered event notification is simulated by resetting internal event flag of each socket state `SockState` +/// and setting all events back by intercepting all requests that could cause `io::ErrorKind::WouldBlock` happening. +/// +/// This selector is currently only support socket due to `Afd` driver is winsock2 specific. +#[derive(Debug)] +pub struct Selector { + #[cfg(debug_assertions)] + id: usize, + pub(super) inner: Arc<SelectorInner>, + #[cfg(debug_assertions)] + has_waker: AtomicBool, +} + +impl Selector { + pub fn new() -> io::Result<Selector> { + SelectorInner::new().map(|inner| { + #[cfg(debug_assertions)] + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1; + Selector { + #[cfg(debug_assertions)] + id, + inner: Arc::new(inner), + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(false), + } + }) + } + + pub fn try_clone(&self) -> io::Result<Selector> { + Ok(Selector { + #[cfg(debug_assertions)] + id: self.id, + inner: Arc::clone(&self.inner), + #[cfg(debug_assertions)] + has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)), + }) + } + + /// # Safety + /// + /// This requires a mutable reference to self because only a single thread + /// can poll IOCP at a time. + pub fn select(&mut self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { + self.inner.select(events, timeout) + } + + #[cfg(debug_assertions)] + pub fn register_waker(&self) -> bool { + self.has_waker.swap(true, Ordering::AcqRel) + } + + pub(super) fn clone_port(&self) -> Arc<CompletionPort> { + self.inner.cp.clone() + } + + #[cfg(feature = "os-ext")] + pub(super) fn same_port(&self, other: &Arc<CompletionPort>) -> bool { + Arc::ptr_eq(&self.inner.cp, other) + } +} + +cfg_io_source! { + use super::InternalState; + use crate::Token; + + impl Selector { + pub(super) fn register( + &self, + socket: RawSocket, + token: Token, + interests: Interest, + ) -> io::Result<InternalState> { + SelectorInner::register(&self.inner, socket, token, interests) + } + + pub(super) fn reregister( + &self, + state: Pin<Arc<Mutex<SockState>>>, + token: Token, + interests: Interest, + ) -> io::Result<()> { + self.inner.reregister(state, token, interests) + } + + #[cfg(debug_assertions)] + pub fn id(&self) -> usize { + self.id + } + } +} + +#[derive(Debug)] +pub struct SelectorInner { + pub(super) cp: Arc<CompletionPort>, + update_queue: Mutex<VecDeque<Pin<Arc<Mutex<SockState>>>>>, + afd_group: AfdGroup, + is_polling: AtomicBool, +} + +// We have ensured thread safety by introducing lock manually. +unsafe impl Sync for SelectorInner {} + +impl SelectorInner { + pub fn new() -> io::Result<SelectorInner> { + CompletionPort::new(0).map(|cp| { + let cp = Arc::new(cp); + let cp_afd = Arc::clone(&cp); + + SelectorInner { + cp, + update_queue: Mutex::new(VecDeque::new()), + afd_group: AfdGroup::new(cp_afd), + is_polling: AtomicBool::new(false), + } + }) + } + + /// # Safety + /// + /// May only be calling via `Selector::select`. + pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> { + events.clear(); + + if timeout.is_none() { + loop { + let len = self.select2(&mut events.statuses, &mut events.events, None)?; + if len == 0 { + continue; + } + break Ok(()); + } + } else { + self.select2(&mut events.statuses, &mut events.events, timeout)?; + Ok(()) + } + } + + pub fn select2( + &self, + statuses: &mut [CompletionStatus], + events: &mut Vec<Event>, + timeout: Option<Duration>, + ) -> io::Result<usize> { + assert!(!self.is_polling.swap(true, Ordering::AcqRel)); + + unsafe { self.update_sockets_events() }?; + + let result = self.cp.get_many(statuses, timeout); + + self.is_polling.store(false, Ordering::Relaxed); + + match result { + Ok(iocp_events) => Ok(unsafe { self.feed_events(events, iocp_events) }), + Err(ref e) if e.raw_os_error() == Some(WAIT_TIMEOUT as i32) => Ok(0), + Err(e) => Err(e), + } + } + + unsafe fn update_sockets_events(&self) -> io::Result<()> { + let mut update_queue = self.update_queue.lock().unwrap(); + for sock in update_queue.iter_mut() { + let mut sock_internal = sock.lock().unwrap(); + if !sock_internal.is_pending_deletion() { + sock_internal.update(sock)?; + } + } + + // remove all sock which do not have error, they have afd op pending + update_queue.retain(|sock| sock.lock().unwrap().has_error()); + + self.afd_group.release_unused_afd(); + Ok(()) + } + + // It returns processed count of iocp_events rather than the events itself. + unsafe fn feed_events( + &self, + events: &mut Vec<Event>, + iocp_events: &[CompletionStatus], + ) -> usize { + let mut n = 0; + let mut update_queue = self.update_queue.lock().unwrap(); + for iocp_event in iocp_events.iter() { + if iocp_event.overlapped().is_null() { + events.push(Event::from_completion_status(iocp_event)); + n += 1; + continue; + } else if iocp_event.token() % 2 == 1 { + // Handle is a named pipe. This could be extended to be any non-AFD event. + let callback = (*(iocp_event.overlapped() as *mut super::Overlapped)).callback; + + let len = events.len(); + callback(iocp_event.entry(), Some(events)); + n += events.len() - len; + continue; + } + + let sock_state = from_overlapped(iocp_event.overlapped()); + let mut sock_guard = sock_state.lock().unwrap(); + if let Some(e) = sock_guard.feed_event() { + events.push(e); + n += 1; + } + + if !sock_guard.is_pending_deletion() { + update_queue.push_back(sock_state.clone()); + } + } + self.afd_group.release_unused_afd(); + n + } +} + +cfg_io_source! { + use std::mem::size_of; + use std::ptr::null_mut; + use winapi::um::mswsock; + use winapi::um::winsock2::WSAGetLastError; + use winapi::um::winsock2::{WSAIoctl, SOCKET_ERROR}; + + impl SelectorInner { + fn register( + this: &Arc<Self>, + socket: RawSocket, + token: Token, + interests: Interest, + ) -> io::Result<InternalState> { + let flags = interests_to_afd_flags(interests); + + let sock = { + let sock = this._alloc_sock_for_rawsocket(socket)?; + let event = Event { + flags, + data: token.0 as u64, + }; + sock.lock().unwrap().set_event(event); + sock + }; + + let state = InternalState { + selector: this.clone(), + token, + interests, + sock_state: sock.clone(), + }; + + this.queue_state(sock); + unsafe { this.update_sockets_events_if_polling()? }; + + Ok(state) + } + + // Directly accessed in `IoSourceState::do_io`. + pub(super) fn reregister( + &self, + state: Pin<Arc<Mutex<SockState>>>, + token: Token, + interests: Interest, + ) -> io::Result<()> { + { + let event = Event { + flags: interests_to_afd_flags(interests), + data: token.0 as u64, + }; + + state.lock().unwrap().set_event(event); + } + + // FIXME: a sock which has_error true should not be re-added to + // the update queue because it's already there. + self.queue_state(state); + unsafe { self.update_sockets_events_if_polling() } + } + + /// This function is called by register() and reregister() to start an + /// IOCTL_AFD_POLL operation corresponding to the registered events, but + /// only if necessary. + /// + /// Since it is not possible to modify or synchronously cancel an AFD_POLL + /// operation, and there can be only one active AFD_POLL operation per + /// (socket, completion port) pair at any time, it is expensive to change + /// a socket's event registration after it has been submitted to the kernel. + /// + /// Therefore, if no other threads are polling when interest in a socket + /// event is (re)registered, the socket is added to the 'update queue', but + /// the actual syscall to start the IOCTL_AFD_POLL operation is deferred + /// until just before the GetQueuedCompletionStatusEx() syscall is made. + /// + /// However, when another thread is already blocked on + /// GetQueuedCompletionStatusEx() we tell the kernel about the registered + /// socket event(s) immediately. + unsafe fn update_sockets_events_if_polling(&self) -> io::Result<()> { + if self.is_polling.load(Ordering::Acquire) { + self.update_sockets_events() + } else { + Ok(()) + } + } + + fn queue_state(&self, sock_state: Pin<Arc<Mutex<SockState>>>) { + let mut update_queue = self.update_queue.lock().unwrap(); + update_queue.push_back(sock_state); + } + + fn _alloc_sock_for_rawsocket( + &self, + raw_socket: RawSocket, + ) -> io::Result<Pin<Arc<Mutex<SockState>>>> { + let afd = self.afd_group.acquire()?; + Ok(Arc::pin(Mutex::new(SockState::new(raw_socket, afd)?))) + } + } + + fn try_get_base_socket(raw_socket: RawSocket, ioctl: u32) -> Result<RawSocket, i32> { + let mut base_socket: RawSocket = 0; + let mut bytes: u32 = 0; + unsafe { + if WSAIoctl( + raw_socket as usize, + ioctl, + null_mut(), + 0, + &mut base_socket as *mut _ as PVOID, + size_of::<RawSocket>() as u32, + &mut bytes, + null_mut(), + None, + ) != SOCKET_ERROR + { + Ok(base_socket) + } else { + Err(WSAGetLastError()) + } + } + } + + fn get_base_socket(raw_socket: RawSocket) -> io::Result<RawSocket> { + let res = try_get_base_socket(raw_socket, mswsock::SIO_BASE_HANDLE); + if let Ok(base_socket) = res { + return Ok(base_socket); + } + + // The `SIO_BASE_HANDLE` should not be intercepted by LSPs, therefore + // it should not fail as long as `raw_socket` is a valid socket. See + // https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls. + // However, at least one known LSP deliberately breaks it, so we try + // some alternative IOCTLs, starting with the most appropriate one. + for &ioctl in &[ + mswsock::SIO_BSP_HANDLE_SELECT, + mswsock::SIO_BSP_HANDLE_POLL, + mswsock::SIO_BSP_HANDLE, + ] { + if let Ok(base_socket) = try_get_base_socket(raw_socket, ioctl) { + // Since we know now that we're dealing with an LSP (otherwise + // SIO_BASE_HANDLE would't have failed), only return any result + // when it is different from the original `raw_socket`. + if base_socket != raw_socket { + return Ok(base_socket); + } + } + } + + // If the alternative IOCTLs also failed, return the original error. + let os_error = res.unwrap_err(); + let err = io::Error::from_raw_os_error(os_error); + Err(err) + } +} + +impl Drop for SelectorInner { + fn drop(&mut self) { + loop { + let events_num: usize; + let mut statuses: [CompletionStatus; 1024] = [CompletionStatus::zero(); 1024]; + + let result = self + .cp + .get_many(&mut statuses, Some(std::time::Duration::from_millis(0))); + match result { + Ok(iocp_events) => { + events_num = iocp_events.iter().len(); + for iocp_event in iocp_events.iter() { + if iocp_event.overlapped().is_null() { + // Custom event + } else if iocp_event.token() % 2 == 1 { + // Named pipe, dispatch the event so it can release resources + let callback = unsafe { + (*(iocp_event.overlapped() as *mut super::Overlapped)).callback + }; + + callback(iocp_event.entry(), None); + } else { + // drain sock state to release memory of Arc reference + let _sock_state = from_overlapped(iocp_event.overlapped()); + } + } + } + + Err(_) => { + break; + } + } + + if events_num == 0 { + // continue looping until all completion statuses have been drained + break; + } + } + + self.afd_group.release_unused_afd(); + } +} + +cfg_net! { + fn interests_to_afd_flags(interests: Interest) -> u32 { + let mut flags = 0; + + if interests.is_readable() { + flags |= READABLE_FLAGS | READ_CLOSED_FLAGS | ERROR_FLAGS; + } + + if interests.is_writable() { + flags |= WRITABLE_FLAGS | WRITE_CLOSED_FLAGS | ERROR_FLAGS; + } + + flags + } +} diff --git a/third_party/rust/mio/src/sys/windows/tcp.rs b/third_party/rust/mio/src/sys/windows/tcp.rs new file mode 100644 index 0000000000..b3f05aec65 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/tcp.rs @@ -0,0 +1,71 @@ +use std::io; +use std::net::{self, SocketAddr}; +use std::os::windows::io::AsRawSocket; + +use winapi::um::winsock2::{self, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, SOCK_STREAM}; + +use crate::sys::windows::net::{init, new_socket, socket_addr}; + +pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result<SOCKET> { + init(); + let domain = match address { + SocketAddr::V4(_) => PF_INET, + SocketAddr::V6(_) => PF_INET6, + }; + new_socket(domain, SOCK_STREAM) +} + +pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> { + use winsock2::bind; + + let (raw_addr, raw_addr_length) = socket_addr(&addr); + syscall!( + bind( + socket.as_raw_socket() as _, + raw_addr.as_ptr(), + raw_addr_length + ), + PartialEq::eq, + SOCKET_ERROR + )?; + Ok(()) +} + +pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> { + use winsock2::connect; + + let (raw_addr, raw_addr_length) = socket_addr(&addr); + let res = syscall!( + connect( + socket.as_raw_socket() as _, + raw_addr.as_ptr(), + raw_addr_length + ), + PartialEq::eq, + SOCKET_ERROR + ); + + match res { + Err(err) if err.kind() != io::ErrorKind::WouldBlock => Err(err), + _ => Ok(()), + } +} + +pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { + use std::convert::TryInto; + use winsock2::listen; + + let backlog = backlog.try_into().unwrap_or(i32::max_value()); + syscall!( + listen(socket.as_raw_socket() as _, backlog), + PartialEq::eq, + SOCKET_ERROR + )?; + Ok(()) +} + +pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { + // The non-blocking state of `listener` is inherited. See + // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept#remarks. + listener.accept() +} diff --git a/third_party/rust/mio/src/sys/windows/udp.rs b/third_party/rust/mio/src/sys/windows/udp.rs new file mode 100644 index 0000000000..825ecccff4 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/udp.rs @@ -0,0 +1,53 @@ +use std::io; +use std::mem::{self, MaybeUninit}; +use std::net::{self, SocketAddr}; +use std::os::windows::io::{AsRawSocket, FromRawSocket}; +use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64. + +use winapi::ctypes::c_int; +use winapi::shared::ws2def::IPPROTO_IPV6; +use winapi::shared::ws2ipdef::IPV6_V6ONLY; +use winapi::um::winsock2::{bind as win_bind, closesocket, getsockopt, SOCKET_ERROR, SOCK_DGRAM}; + +use crate::sys::windows::net::{init, new_ip_socket, socket_addr}; + +pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> { + init(); + new_ip_socket(addr, SOCK_DGRAM).and_then(|socket| { + let (raw_addr, raw_addr_length) = socket_addr(&addr); + syscall!( + win_bind(socket, raw_addr.as_ptr(), raw_addr_length,), + PartialEq::eq, + SOCKET_ERROR + ) + .map_err(|err| { + // Close the socket if we hit an error, ignoring the error + // from closing since we can't pass back two errors. + let _ = unsafe { closesocket(socket) }; + err + }) + .map(|_| unsafe { net::UdpSocket::from_raw_socket(socket as StdSocket) }) + }) +} + +pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result<bool> { + let mut optval: MaybeUninit<c_int> = MaybeUninit::uninit(); + let mut optlen = mem::size_of::<c_int>() as c_int; + + syscall!( + getsockopt( + socket.as_raw_socket() as usize, + IPPROTO_IPV6 as c_int, + IPV6_V6ONLY as c_int, + optval.as_mut_ptr().cast(), + &mut optlen, + ), + PartialEq::eq, + SOCKET_ERROR + )?; + + debug_assert_eq!(optlen as usize, mem::size_of::<c_int>()); + // Safety: `getsockopt` initialised `optval` for us. + let optval = unsafe { optval.assume_init() }; + Ok(optval != 0) +} diff --git a/third_party/rust/mio/src/sys/windows/waker.rs b/third_party/rust/mio/src/sys/windows/waker.rs new file mode 100644 index 0000000000..ab12c3c689 --- /dev/null +++ b/third_party/rust/mio/src/sys/windows/waker.rs @@ -0,0 +1,29 @@ +use crate::sys::windows::Event; +use crate::sys::windows::Selector; +use crate::Token; + +use miow::iocp::CompletionPort; +use std::io; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Waker { + token: Token, + port: Arc<CompletionPort>, +} + +impl Waker { + pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> { + Ok(Waker { + token, + port: selector.clone_port(), + }) + } + + pub fn wake(&self) -> io::Result<()> { + let mut ev = Event::new(self.token); + ev.set_readable(); + + self.port.post(ev.to_completion_status()) + } +} |