chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

147
vendor/mio/src/sys/mod.rs vendored Normal file
View File

@@ -0,0 +1,147 @@
//! 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(any(unix, target_os = "hermit"))]
cfg_os_poll! {
mod unix;
#[allow(unused_imports)]
pub use self::unix::*;
}
#[cfg(windows)]
cfg_os_poll! {
mod windows;
pub use self::windows::*;
}
#[cfg(target_os = "wasi")]
cfg_os_poll! {
mod wasi;
pub(crate) use self::wasi::*;
}
cfg_not_os_poll! {
mod shell;
pub(crate) use self::shell::*;
#[cfg(unix)]
cfg_any_os_ext! {
mod unix;
#[cfg(feature = "os-ext")]
pub use self::unix::SourceFd;
}
}
/// Define the `listen` backlog parameters as in the standard library. This
/// helps avoid hardcoded unsynchronized values and allows better control of
/// default values depending on the target.
///
/// Selecting a “valid” default value can be tricky due to:
///
/// - It often serves only as a hint and may be rounded, trimmed, or ignored by
/// the OS.
///
/// - It is sometimes provided as a "magic" value, for example, -1. This
/// value is undocumented and not standard, but it is often used to represent
/// the largest possible backlog size. This happens due to signed/unsigned
/// conversion and rounding to the upper bound performed by the OS.
///
/// - Default values vary depending on the OS and its version. Common defaults
/// include: -1, 128, 1024, and 4096.
///
// Here is the original code from the standard library
// https://github.com/rust-lang/rust/blob/4f808ba6bf9f1c8dde30d009e73386d984491587/library/std/src/os/unix/net/listener.rs#L72
//
#[allow(dead_code)]
#[cfg(any(
target_os = "windows",
target_os = "redox",
target_os = "espidf",
target_os = "horizon"
))]
pub(crate) const LISTEN_BACKLOG_SIZE: i32 = 128;
/// This is a special case for some target(s) supported by `mio`. This value
/// is needed because `libc::SOMAXCON` (used as a fallback for unknown targets)
/// is not implemented for them. Feel free to update this if the `libc` crate
/// changes.
#[allow(dead_code)]
#[cfg(target_os = "hermit")]
pub(crate) const LISTEN_BACKLOG_SIZE: i32 = 1024;
#[allow(dead_code)]
#[cfg(any(
// Silently capped to `/proc/sys/net/core/somaxconn`.
target_os = "linux",
// Silently capped to `kern.ipc.soacceptqueue`.
target_os = "freebsd",
// Silently capped to `kern.somaxconn sysctl`.
target_os = "openbsd",
// Silently capped to the default 128.
target_vendor = "apple",
))]
pub(crate) const LISTEN_BACKLOG_SIZE: i32 = -1;
#[allow(dead_code)]
#[cfg(not(any(
target_os = "windows",
target_os = "redox",
target_os = "espidf",
target_os = "horizon",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "wasi",
target_os = "hermit",
target_vendor = "apple",
)))]
pub(crate) const LISTEN_BACKLOG_SIZE: i32 = libc::SOMAXCONN;

105
vendor/mio/src/sys/shell/mod.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
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};
#[cfg(not(target_os = "wasi"))]
mod waker;
#[cfg(not(target_os = "wasi"))]
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(any(unix))]
use std::os::fd::RawFd;
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::RawFd;
#[cfg(windows)]
use std::os::windows::io::RawSocket;
#[cfg(any(windows, unix, target_os = "hermit"))]
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(any(unix, target_os = "hermit"))]
impl IoSourceState {
pub fn register(
&mut self,
_: &Registry,
_: Token,
_: Interest,
_: RawFd,
) -> io::Result<()> {
os_required!()
}
pub fn reregister(
&mut self,
_: &Registry,
_: Token,
_: Interest,
_: RawFd,
) -> io::Result<()> {
os_required!()
}
pub fn deregister(&mut self, _: &Registry, _: RawFd) -> io::Result<()> {
os_required!()
}
}
#[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!()
}
}
}

122
vendor/mio/src/sys/shell/selector.rs vendored Normal file
View File

@@ -0,0 +1,122 @@
use std::io;
#[cfg(unix)]
use std::os::fd::{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(unix)]
cfg_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(target_os = "wasi")]
cfg_any_os_ext! {
use crate::{Interest, Token};
impl Selector {
pub fn register(&self, _: wasi::Fd, _: Token, _: Interest) -> io::Result<()> {
os_required!();
}
pub fn reregister(&self, _: wasi::Fd, _: Token, _: Interest) -> io::Result<()> {
os_required!();
}
pub fn deregister(&self, _: wasi::Fd) -> 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!();
}
}

31
vendor/mio/src/sys/shell/tcp.rs vendored Normal file
View File

@@ -0,0 +1,31 @@
use std::io;
use std::net::{self, SocketAddr};
#[cfg(not(target_os = "wasi"))]
pub(crate) fn new_for_addr(_: SocketAddr) -> io::Result<i32> {
os_required!();
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn bind(_: &net::TcpListener, _: SocketAddr) -> io::Result<()> {
os_required!();
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn connect(_: &net::TcpStream, _: SocketAddr) -> io::Result<()> {
os_required!();
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn listen(_: &net::TcpListener, _: i32) -> io::Result<()> {
os_required!();
}
#[cfg(any(unix, target_os = "hermit"))]
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!();
}

11
vendor/mio/src/sys/shell/udp.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
#![cfg(not(target_os = "wasi"))]
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!()
}

44
vendor/mio/src/sys/shell/uds.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
pub(crate) mod datagram {
use std::io;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn bind_addr(_: &SocketAddr) -> 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) mod listener {
use std::io;
use std::os::unix::net::{self, SocketAddr};
use crate::net::UnixStream;
pub(crate) fn bind_addr(_: &SocketAddr) -> io::Result<net::UnixListener> {
os_required!()
}
pub(crate) fn accept(_: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
os_required!()
}
}
pub(crate) mod stream {
use std::io;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn connect_addr(_: &SocketAddr) -> io::Result<net::UnixStream> {
os_required!()
}
pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> {
os_required!()
}
}

16
vendor/mio/src/sys/shell/waker.rs vendored Normal file
View File

@@ -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!();
}
}

167
vendor/mio/src/sys/unix/mod.rs vendored Normal file
View File

@@ -0,0 +1,167 @@
/// 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),* $(,)* ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($($arg, )*) };
if res < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
}
}};
}
cfg_os_poll! {
#[cfg_attr(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
)
), path = "selector/epoll.rs")]
#[cfg_attr(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)
), path = "selector/kqueue.rs")]
#[cfg_attr(any(
mio_unsupported_force_poll_poll,
target_os = "aix",
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita",
target_os = "cygwin",
), path = "selector/poll.rs")]
mod selector;
pub(crate) use self::selector::*;
#[cfg_attr(all(
not(mio_unsupported_force_waker_pipe),
any(
target_os = "android",
target_os = "espidf",
target_os = "fuchsia",
target_os = "hermit",
target_os = "illumos",
target_os = "linux",
)
), path = "waker/eventfd.rs")]
#[cfg_attr(all(
not(mio_unsupported_force_waker_pipe),
not(mio_unsupported_force_poll_poll), // `kqueue(2)` based waker doesn't work with `poll(2)`.
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)
), path = "waker/kqueue.rs")]
#[cfg_attr(any(
// NOTE: also add to the list for the `pipe` module below.
mio_unsupported_force_waker_pipe,
all(
// `kqueue(2)` based waker doesn't work with `poll(2)`.
mio_unsupported_force_poll_poll,
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
),
),
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "nto",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
target_os = "cygwin",
), path = "waker/pipe.rs")]
mod waker;
// NOTE: the `Waker` type is expected in the selector module as the
// `poll(2)` implementation needs to do some special stuff.
#[cfg(feature = "os-ext")]
mod sourcefd;
#[cfg(feature = "os-ext")]
pub use self::sourcefd::SourceFd;
cfg_net! {
mod net;
pub(crate) mod tcp;
pub(crate) mod udp;
#[cfg(not(target_os = "hermit"))]
pub(crate) mod uds;
}
#[cfg(all(
any(
// For the public `pipe` module, must match `cfg_os_ext` macro.
feature = "os-ext",
// For the `Waker` type based on a pipe.
mio_unsupported_force_waker_pipe,
all(
// `kqueue(2)` based waker doesn't work with `poll(2)`.
mio_unsupported_force_poll_poll,
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
),
),
// NOTE: also add to the list for the `pipe` module below.
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "nto",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
target_os = "cygwin",
),
// Hermit doesn't support pipes.
not(target_os = "hermit"),
))]
pub(crate) mod pipe;
}
cfg_not_os_poll! {
cfg_os_ext! {
mod sourcefd;
pub use self::sourcefd::SourceFd;
}
}

216
vendor/mio/src/sys/unix/net.rs vendored Normal file
View File

@@ -0,0 +1,216 @@
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 = "hurd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "hermit",
target_os = "cygwin",
))]
let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
#[cfg(target_os = "nto")]
let socket_type = socket_type | libc::SOCK_CLOEXEC;
let socket = syscall!(socket(domain, socket_type, 0))?;
// Mimic `libstd` and set `SO_NOSIGPIPE` on apple systems.
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
if let Err(err) = 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
)) {
let _ = syscall!(close(socket));
return Err(err);
}
// Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
#[cfg(any(
target_os = "aix",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
))]
{
if let Err(err) = syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) {
let _ = syscall!(close(socket));
return Err(err);
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
if let Err(err) = syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)) {
let _ = syscall!(close(socket));
return Err(err);
}
}
Ok(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,
#[cfg(not(any(target_os = "haiku", target_os = "vita")))]
sin_zero: [0; 8],
#[cfg(target_os = "haiku")]
sin_zero: [0; 24],
#[cfg(target_os = "vita")]
sin_zero: [0; 6],
#[cfg(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "hurd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
))]
sin_len: 0,
#[cfg(target_os = "vita")]
sin_vport: addr.port().to_be(),
};
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 = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "hurd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
target_os = "hermit",
))]
sin6_len: 0,
#[cfg(target_os = "vita")]
sin6_vport: addr.port().to_be(),
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
__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()),
}
}

620
vendor/mio/src/sys/unix/pipe.rs vendored Normal file
View File

@@ -0,0 +1,620 @@
//! Unix pipe.
//!
//! See the [`new`] function for documentation.
use std::io;
use std::os::fd::RawFd;
pub(crate) fn new_raw() -> io::Result<[RawFd; 2]> {
let mut fds: [RawFd; 2] = [-1, -1];
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "hurd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
target_os = "cygwin",
))]
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 = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "nto",
))]
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 closing error though.
let _ = libc::close(fds[0]);
let _ = libc::close(fds[1]);
return Err(err);
}
}
}
Ok(fds)
}
cfg_os_ext! {
use std::fs::File;
use std::io::{IoSlice, IoSliceMut, Read, Write};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd};
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 fds = new_raw()?;
// SAFETY: `new_raw` 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)
}
/// Execute an I/O operation ensuring that the socket receives more events
/// if it hits a [`WouldBlock`] error.
///
/// # Notes
///
/// This method is required to be called for **all** I/O operations to
/// ensure the user will receive events once the socket is ready again after
/// returning a [`WouldBlock`] error.
///
/// [`WouldBlock`]: io::ErrorKind::WouldBlock
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::fd::AsRawFd;
/// use mio::unix::pipe;
///
/// let (sender, receiver) = pipe::new()?;
///
/// // Wait until the sender is writable...
///
/// // Write to the sender using a direct libc call, of course the
/// // `io::Write` implementation would be easier to use.
/// let buf = b"hello";
/// let n = sender.try_io(|| {
/// let buf_ptr = &buf as *const _ as *const _;
/// let res = unsafe { libc::write(sender.as_raw_fd(), buf_ptr, buf.len()) };
/// if res != -1 {
/// Ok(res as usize)
/// } else {
/// // If EAGAIN or EWOULDBLOCK is set by libc::write, the closure
/// // should return `WouldBlock` error.
/// Err(io::Error::last_os_error())
/// }
/// })?;
/// eprintln!("write {} bytes", n);
///
/// // Wait until the receiver is readable...
///
/// // Read from the receiver using a direct libc call, of course the
/// // `io::Read` implementation would be easier to use.
/// let mut buf = [0; 512];
/// let n = receiver.try_io(|| {
/// let buf_ptr = &mut buf as *mut _ as *mut _;
/// let res = unsafe { libc::read(receiver.as_raw_fd(), buf_ptr, buf.len()) };
/// if res != -1 {
/// Ok(res as usize)
/// } else {
/// // If EAGAIN or EWOULDBLOCK is set by libc::read, the closure
/// // should return `WouldBlock` error.
/// Err(io::Error::last_os_error())
/// }
/// })?;
/// eprintln!("read {} bytes", n);
/// # Ok(())
/// # }
/// ```
pub fn try_io<F, T>(&self, f: F) -> io::Result<T>
where
F: FnOnce() -> io::Result<T>,
{
self.inner.do_io(|_| f())
}
}
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(|mut sender| sender.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|mut sender| sender.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|mut sender| sender.flush())
}
}
impl Write for &Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|mut sender| sender.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|mut sender| sender.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|mut 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()
}
}
impl From<Sender> for OwnedFd {
fn from(sender: Sender) -> Self {
sender.inner.into_inner().into()
}
}
impl AsFd for Sender {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
impl From<OwnedFd> for Sender {
fn from(fd: OwnedFd) -> Self {
Sender {
inner: IoSource::new(File::from(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)
}
/// Execute an I/O operation ensuring that the socket receives more events
/// if it hits a [`WouldBlock`] error.
///
/// # Notes
///
/// This method is required to be called for **all** I/O operations to
/// ensure the user will receive events once the socket is ready again after
/// returning a [`WouldBlock`] error.
///
/// [`WouldBlock`]: io::ErrorKind::WouldBlock
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::fd::AsRawFd;
/// use mio::unix::pipe;
///
/// let (sender, receiver) = pipe::new()?;
///
/// // Wait until the sender is writable...
///
/// // Write to the sender using a direct libc call, of course the
/// // `io::Write` implementation would be easier to use.
/// let buf = b"hello";
/// let n = sender.try_io(|| {
/// let buf_ptr = &buf as *const _ as *const _;
/// let res = unsafe { libc::write(sender.as_raw_fd(), buf_ptr, buf.len()) };
/// if res != -1 {
/// Ok(res as usize)
/// } else {
/// // If EAGAIN or EWOULDBLOCK is set by libc::write, the closure
/// // should return `WouldBlock` error.
/// Err(io::Error::last_os_error())
/// }
/// })?;
/// eprintln!("write {} bytes", n);
///
/// // Wait until the receiver is readable...
///
/// // Read from the receiver using a direct libc call, of course the
/// // `io::Read` implementation would be easier to use.
/// let mut buf = [0; 512];
/// let n = receiver.try_io(|| {
/// let buf_ptr = &mut buf as *mut _ as *mut _;
/// let res = unsafe { libc::read(receiver.as_raw_fd(), buf_ptr, buf.len()) };
/// if res != -1 {
/// Ok(res as usize)
/// } else {
/// // If EAGAIN or EWOULDBLOCK is set by libc::read, the closure
/// // should return `WouldBlock` error.
/// Err(io::Error::last_os_error())
/// }
/// })?;
/// eprintln!("read {} bytes", n);
/// # Ok(())
/// # }
/// ```
pub fn try_io<F, T>(&self, f: F) -> io::Result<T>
where
F: FnOnce() -> io::Result<T>,
{
self.inner.do_io(|_| f())
}
}
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(|mut sender| sender.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|mut sender| sender.read_vectored(bufs))
}
}
impl Read for &Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|mut sender| sender.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|mut 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 IntoRawFd for Receiver {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
impl AsRawFd for Receiver {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl FromRawFd for Receiver {
unsafe fn from_raw_fd(fd: RawFd) -> Receiver {
Receiver {
inner: IoSource::new(File::from_raw_fd(fd)),
}
}
}
impl From<Receiver> for OwnedFd {
fn from(receiver: Receiver) -> Self {
receiver.inner.into_inner().into()
}
}
impl AsFd for Receiver {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
impl From<OwnedFd> for Receiver {
fn from(fd: OwnedFd) -> Self {
Receiver {
inner: IoSource::new(File::from(fd)),
}
}
}
#[cfg(not(any(target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "vita")))]
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(any(target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "vita"))]
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(())
}
} // `cfg_os_ext!`.

View File

@@ -0,0 +1,237 @@
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLPRI, EPOLLRDHUP};
use crate::{Interest, Token};
cfg_io_source! {
use std::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: OwnedFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
// SAFETY: `epoll_create1(2)` ensures the fd is valid.
let ep = unsafe { OwnedFd::from_raw_fd(syscall!(epoll_create1(libc::EPOLL_CLOEXEC))?) };
Ok(Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
ep,
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
self.ep.try_clone().map(|ep| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
ep,
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
let timeout = timeout
.map(|to| {
// `Duration::as_millis` truncates, so round up. This avoids
// turning sub-millisecond timeouts into a zero timeout, unless
// the caller explicitly requests that by specifying a zero
// timeout.
to.checked_add(Duration::from_nanos(999_999))
.unwrap_or(to)
.as_millis() as libc::c_int
})
.unwrap_or(-1);
events.clear();
syscall!(epoll_wait(
self.ep.as_raw_fd(),
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,
#[cfg(target_os = "redox")]
_pad: 0,
};
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
}
cfg_any_os_ext! {
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,
#[cfg(target_os = "redox")]
_pad: 0,
};
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
}
}
}
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.as_raw_fd()
}
}
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;
}
if interests.is_priority() {
kind |= EPOLLPRI;
}
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,
libc::EPOLLEXCLUSIVE,
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()
}
}
// No special requirement from the implementation around waking.
pub(crate) use crate::sys::unix::waker::Waker;
cfg_io_source! {
mod stateless_io_source;
pub(crate) use stateless_io_source::IoSourceState;
}

View File

@@ -0,0 +1,918 @@
use std::mem;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::slice;
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::{cmp, io, ptr};
use crate::Interest;
use crate::Token;
/// 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 = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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 = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
type Flags = u16;
#[cfg(target_os = "netbsd")]
type Flags = u32;
// Type of the `udata` field in the `kevent` structure.
type UData = *mut libc::c_void;
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,
udata: $data as UData,
..unsafe { mem::zeroed() }
}
};
}
#[derive(Debug)]
pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
kq: OwnedFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
// SAFETY: `kqueue(2)` ensures the fd is valid.
let kq = unsafe { OwnedFd::from_raw_fd(syscall!(kqueue())?) };
syscall!(fcntl(kq.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
Ok(Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
kq,
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
self.kq.try_clone().map(|kq| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
kq,
})
}
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 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.as_raw_fd(),
ptr::null(),
0,
events.as_mut_ptr().cast(),
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) };
})
}
#[cfg_attr(not(feature = "os-ext"), allow(dead_code))]
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.as_raw_fd(), changes, &[libc::EPIPE as i64])
}
cfg_any_os_ext! {
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.as_raw_fd(),
&mut changes,
&[libc::ENOENT as i64, libc::EPIPE as i64],
)
}
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.as_raw_fd(), &mut changes, &[libc::ENOENT as i64])
}
}
// Used by `Waker`.
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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
);
let kq = self.kq.as_raw_fd();
syscall!(kevent(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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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;
let kq = self.kq.as_raw_fd();
syscall!(kevent(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: &[i64],
) -> 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: &[i64]) -> 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 as _;
// 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.as_raw_fd()
}
}
#[repr(transparent)]
#[derive(Clone)]
pub struct Event(libc::kevent);
unsafe impl Send for Event {}
unsafe impl Sync for Event {}
impl Deref for Event {
type Target = libc::kevent;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Event {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub struct Events(Vec<Event>);
impl Deref for Events {
type Target = Vec<Event>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Events {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Events {
pub fn with_capacity(capacity: usize) -> Events {
Events(Vec::with_capacity(capacity))
}
}
// `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.0.udata as usize)
}
pub fn is_readable(event: &Event) -> bool {
event.0.filter == libc::EVFILT_READ || {
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
// 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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
)))]
{
false
}
}
}
pub fn is_writable(event: &Event) -> bool {
event.0.filter == libc::EVFILT_WRITE
}
pub fn is_error(event: &Event) -> bool {
(event.0.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.0.flags & libc::EV_EOF) != 0 && event.0.fflags != 0
}
pub fn is_read_closed(event: &Event) -> bool {
event.0.filter == libc::EVFILT_READ && event.0.flags & libc::EV_EOF != 0
}
pub fn is_write_closed(event: &Event) -> bool {
event.0.filter == libc::EVFILT_WRITE && event.0.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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
{
event.0.filter == libc::EVFILT_AIO
}
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)))]
{
false
}
}
#[allow(unused_variables)] // `event` is only used on FreeBSD.
pub fn is_lio(event: &Event) -> bool {
#[cfg(target_os = "freebsd")]
{
event.0.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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::EVFILT_FS,
#[cfg(target_os = "freebsd")]
libc::EVFILT_LIO,
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EVFILT_MACHPORT,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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,
// Not stable across OS versions on OpenBSD.
#[cfg(not(target_os = "openbsd"))]
libc::EV_SYSFLAGS,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EV_FLAG0,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EV_POLL,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_TRIGGER,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFNOP,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFAND,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFOR,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFCOPY,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFCTRLMASK,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXTEND,
libc::NOTE_ATTRIB,
libc::NOTE_LINK,
libc::NOTE_RENAME,
libc::NOTE_REVOKE,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_SIGNAL,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXITSTATUS,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
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",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_DETAIL_MASK,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_DECRYPTFAIL,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_MEMORY,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_CSERROR,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE_TERMINATE,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_ERROR,
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_SECONDS,
#[cfg(any(target_os = "freebsd"))]
libc::NOTE_MSECONDS,
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_USECONDS,
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_NSECONDS,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_ABSOLUTE,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_LEEWAY,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_CRITICAL,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_BACKGROUND,
);
// Can't reference fields in packed structures.
let ident = event.0.ident;
let data = event.0.data;
let udata = event.0.udata;
f.debug_struct("kevent")
.field("ident", &ident)
.field("filter", &FilterDetails(event.0.filter))
.field("flags", &FlagsDetails(event.0.flags))
.field("fflags", &FflagsDetails(event.0.fflags))
.field("data", &data)
.field("udata", &udata)
.finish()
}
}
// No special requirement from the implementation around waking.
pub(crate) use crate::sys::unix::waker::Waker;
cfg_io_source! {
mod stateless_io_source;
pub(crate) use stateless_io_source::IoSourceState;
}
#[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();
}

753
vendor/mio/src/sys/unix/selector/poll.rs vendored Normal file
View File

@@ -0,0 +1,753 @@
// This implementation is based on the one in the `polling` crate.
// Thanks to https://github.com/Kestrer for the original implementation!
// Permission to use this code has been granted by original author:
// https://github.com/tokio-rs/mio/pull/1602#issuecomment-1218441031
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use std::{cmp, fmt, io};
use crate::sys::unix::waker::Waker as WakerInternal;
use crate::{Interest, Token};
/// Unique id for use as `SelectorId`.
#[cfg(debug_assertions)]
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
#[derive(Debug)]
pub struct Selector {
state: Arc<SelectorState>,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
let state = SelectorState::new()?;
Ok(Selector {
state: Arc::new(state),
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
let state = self.state.clone();
Ok(Selector { state })
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
self.state.select(events, timeout)
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.state.register(fd, token, interests)
}
#[allow(dead_code)]
pub(crate) fn register_internal(
&self,
fd: RawFd,
token: Token,
interests: Interest,
) -> io::Result<Arc<RegistrationRecord>> {
self.state.register_internal(fd, token, interests)
}
cfg_any_os_ext! {
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.state.reregister(fd, token, interests)
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
self.state.deregister(fd)
}
}
pub fn wake(&self, token: Token) -> io::Result<()> {
self.state.wake(token)
}
cfg_io_source! {
#[cfg(debug_assertions)]
pub fn id(&self) -> usize {
self.state.id
}
}
}
/// Interface to poll.
#[derive(Debug)]
struct SelectorState {
/// File descriptors to poll.
fds: Mutex<Fds>,
/// File descriptors which will be removed before the next poll call.
///
/// When a file descriptor is deregistered while a poll is running, we need to filter
/// out all removed descriptors after that poll is finished running.
pending_removal: Mutex<Vec<RawFd>>,
/// Token associated with Waker that have recently asked to wake. This will
/// cause a synthetic behaviour where on any wakeup we add all pending tokens
/// to the list of emitted events.
pending_wake_token: Mutex<Option<Token>>,
/// Data is written to this to wake up the current instance of `wait`, which can occur when the
/// user notifies it (in which case `notified` would have been set) or when an operation needs
/// to occur (in which case `waiting_operations` would have been incremented).
notify_waker: WakerInternal,
/// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the
/// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero
/// again.
waiting_operations: AtomicUsize,
/// The condition variable that gets notified when `waiting_operations` reaches zero or
/// `notified` becomes true.
///
/// This is used with the `fds` mutex.
operations_complete: Condvar,
/// This selectors id.
#[cfg(debug_assertions)]
#[allow(dead_code)]
id: usize,
}
/// The file descriptors to poll in a `Poller`.
#[derive(Debug, Clone)]
struct Fds {
/// The list of `pollfds` taken by poll.
///
/// The first file descriptor is always present and is used to notify the poller.
poll_fds: Vec<PollFd>,
/// The map of each file descriptor to data associated with it. This does not include the file
/// descriptors created by the internal notify waker.
fd_data: HashMap<RawFd, FdData>,
}
/// Transparent wrapper around `libc::pollfd`, used to support `Debug` derives without adding the
/// `extra_traits` feature of `libc`.
#[repr(transparent)]
#[derive(Clone)]
struct PollFd(libc::pollfd);
impl Debug for PollFd {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("pollfd")
.field("fd", &self.0.fd)
.field("events", &self.0.events)
.field("revents", &self.0.revents)
.finish()
}
}
/// Data associated with a file descriptor in a poller.
#[derive(Debug, Clone)]
struct FdData {
/// The index into `poll_fds` this file descriptor is.
poll_fds_index: usize,
/// The key of the `Event` associated with this file descriptor.
token: Token,
/// Used to communicate with IoSourceState when we need to internally deregister
/// based on a closed fd.
shared_record: Arc<RegistrationRecord>,
}
impl SelectorState {
pub fn new() -> io::Result<SelectorState> {
let notify_waker = WakerInternal::new_unregistered()?;
Ok(Self {
fds: Mutex::new(Fds {
poll_fds: vec![PollFd(libc::pollfd {
fd: notify_waker.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
})],
fd_data: HashMap::new(),
}),
pending_removal: Mutex::new(Vec::new()),
pending_wake_token: Mutex::new(None),
notify_waker,
waiting_operations: AtomicUsize::new(0),
operations_complete: Condvar::new(),
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
events.clear();
let mut fds = self.fds.lock().unwrap();
// Keep track of fds that receive POLLHUP or POLLERR (i.e. won't receive further
// events) and internally deregister them before they are externally deregister'd. See
// IoSourceState below to track how the external deregister call will be handled
// when this state occurs.
let mut closed_raw_fds = Vec::new();
loop {
// Complete all current operations.
loop {
if self.waiting_operations.load(Ordering::SeqCst) == 0 {
break;
}
fds = self.operations_complete.wait(fds).unwrap();
}
// Perform the poll.
trace!("Polling on {:?}", &fds);
let num_events = poll(&mut fds.poll_fds, timeout)?;
trace!("Poll finished: {:?}", &fds);
if num_events == 0 {
return Ok(());
}
let waker_events = fds.poll_fds[0].0.revents;
let notified = waker_events != 0;
let mut num_fd_events = if notified { num_events - 1 } else { num_events };
let pending_wake_token = self.pending_wake_token.lock().unwrap().take();
if notified {
self.notify_waker.ack_and_reset();
if pending_wake_token.is_some() {
num_fd_events += 1;
}
}
// We now check whether this poll was performed with descriptors which were pending
// for removal and filter out any matching.
let mut pending_removal_guard = self.pending_removal.lock().unwrap();
let mut pending_removal = std::mem::replace(pending_removal_guard.as_mut(), Vec::new());
drop(pending_removal_guard);
// Store the events if there were any.
if num_fd_events > 0 {
let fds = &mut *fds;
events.reserve(num_fd_events);
// Add synthetic events we picked up from calls to wake()
if let Some(pending_wake_token) = pending_wake_token {
events.push(Event {
token: pending_wake_token,
events: waker_events,
});
}
for fd_data in fds.fd_data.values_mut() {
let PollFd(poll_fd) = &mut fds.poll_fds[fd_data.poll_fds_index];
if pending_removal.contains(&poll_fd.fd) {
// Fd was removed while poll was running
continue;
}
if poll_fd.revents != 0 {
// Store event
events.push(Event {
token: fd_data.token,
events: poll_fd.revents,
});
if poll_fd.revents & (libc::POLLHUP | libc::POLLERR) != 0 {
pending_removal.push(poll_fd.fd);
closed_raw_fds.push(poll_fd.fd);
}
// Remove the interest which just got triggered the IoSourceState's do_io
// wrapper used with this selector will add back the interest using
// reregister.
poll_fd.events &= !poll_fd.revents;
// Minor optimization to potentially avoid looping n times where n is the
// number of input fds (i.e. we might loop between m and n times where m is
// the number of fds with revents != 0).
if events.len() == num_fd_events {
break;
}
}
}
break; // No more polling.
}
// If we didn't break above it means we got woken up internally (for example for adding an fd), so we poll again.
}
drop(fds);
let _ = self.deregister_all(&closed_raw_fds);
Ok(())
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.register_internal(fd, token, interests).map(|_| ())
}
pub fn register_internal(
&self,
fd: RawFd,
token: Token,
interests: Interest,
) -> io::Result<Arc<RegistrationRecord>> {
#[cfg(debug_assertions)]
if fd == self.notify_waker.as_raw_fd() {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
// We must handle the unlikely case that the following order of operations happens:
//
// register(1 as RawFd)
// deregister(1 as RawFd)
// register(1 as RawFd)
// <poll happens>
//
// Fd's pending removal only get cleared when poll has been run. It is possible that
// between registering and deregistering and then _again_ registering the file descriptor
// poll never gets called, thus the fd stays stuck in the pending removal list.
//
// To avoid this scenario we remove an fd from pending removals when registering it.
let mut pending_removal = self.pending_removal.lock().unwrap();
if let Some(idx) = pending_removal.iter().position(|&pending| pending == fd) {
pending_removal.swap_remove(idx);
}
drop(pending_removal);
self.modify_fds(|fds| {
if fds.fd_data.contains_key(&fd) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"I/O source already registered this `Registry` \
(an old file descriptor might have been closed without deregistration)",
));
}
let poll_fds_index = fds.poll_fds.len();
let record = Arc::new(RegistrationRecord::new());
fds.fd_data.insert(
fd,
FdData {
poll_fds_index,
token,
shared_record: record.clone(),
},
);
fds.poll_fds.push(PollFd(libc::pollfd {
fd,
events: interests_to_poll(interests),
revents: 0,
}));
Ok(record)
})
}
cfg_any_os_ext! {
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.modify_fds(|fds| {
let data = fds.fd_data.get_mut(&fd).ok_or(io::ErrorKind::NotFound)?;
data.token = token;
let poll_fds_index = data.poll_fds_index;
fds.poll_fds[poll_fds_index].0.events = interests_to_poll(interests);
Ok(())
})
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
self.deregister_all(&[fd])
.map_err(|_| io::ErrorKind::NotFound)?;
Ok(())
}
}
/// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running.
fn modify_fds<T>(&self, f: impl FnOnce(&mut Fds) -> T) -> T {
self.waiting_operations.fetch_add(1, Ordering::SeqCst);
// Wake up the current caller of `wait` if there is one.
let sent_notification = self.notify_waker.wake().is_ok();
let mut fds = self.fds.lock().unwrap();
// If there was no caller of `wait` our notification was not removed from the pipe.
if sent_notification {
self.notify_waker.ack_and_reset();
}
let res = f(&mut *fds);
if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 {
self.operations_complete.notify_one();
}
res
}
/// Special optimized version of [Self::deregister] which handles multiple removals
/// at once. Ok result if all removals were performed, Err if any entries
/// were not found.
fn deregister_all(&self, targets: &[RawFd]) -> Result<(), ()> {
if targets.is_empty() {
return Ok(());
}
let mut pending_removal = self.pending_removal.lock().unwrap();
pending_removal.extend(targets);
drop(pending_removal);
self.modify_fds(|fds| {
let mut all_successful = true;
for target in targets {
match fds.fd_data.remove(target).ok_or(()) {
Ok(data) => {
data.shared_record.mark_unregistered();
fds.poll_fds.swap_remove(data.poll_fds_index);
if let Some(swapped_pollfd) = fds.poll_fds.get(data.poll_fds_index) {
fds.fd_data
.get_mut(&swapped_pollfd.0.fd)
.unwrap()
.poll_fds_index = data.poll_fds_index;
}
}
Err(_) => all_successful = false,
}
}
if all_successful {
Ok(())
} else {
Err(())
}
})
}
pub fn wake(&self, token: Token) -> io::Result<()> {
self.pending_wake_token.lock().unwrap().replace(token);
self.notify_waker.wake()
}
}
/// Shared record between IoSourceState and SelectorState that allows us to internally
/// deregister partially or fully closed fds (i.e. when we get POLLHUP or PULLERR) without
/// confusing IoSourceState and trying to deregister twice. This isn't strictly
/// required as technically deregister is idempotent but it is confusing
/// when trying to debug behaviour as we get imbalanced calls to register/deregister and
/// superfluous NotFound errors.
#[derive(Debug)]
pub(crate) struct RegistrationRecord {
is_unregistered: AtomicBool,
}
impl RegistrationRecord {
pub fn new() -> Self {
Self {
is_unregistered: AtomicBool::new(false),
}
}
pub fn mark_unregistered(&self) {
self.is_unregistered.store(true, Ordering::Relaxed);
}
#[allow(dead_code)]
pub fn is_registered(&self) -> bool {
!self.is_unregistered.load(Ordering::Relaxed)
}
}
#[cfg(target_os = "linux")]
const POLLRDHUP: libc::c_short = libc::POLLRDHUP;
#[cfg(not(target_os = "linux"))]
const POLLRDHUP: libc::c_short = 0;
const READ_EVENTS: libc::c_short = libc::POLLIN | POLLRDHUP;
const WRITE_EVENTS: libc::c_short = libc::POLLOUT;
const PRIORITY_EVENTS: libc::c_short = libc::POLLPRI;
/// Get the input poll events for the given event.
fn interests_to_poll(interest: Interest) -> libc::c_short {
let mut kind = 0;
if interest.is_readable() {
kind |= READ_EVENTS;
}
if interest.is_writable() {
kind |= WRITE_EVENTS;
}
if interest.is_priority() {
kind |= PRIORITY_EVENTS;
}
kind
}
/// Helper function to call poll.
fn poll(fds: &mut [PollFd], timeout: Option<Duration>) -> io::Result<usize> {
loop {
// 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 as u128;
let timeout = timeout
.map(|to| {
// `Duration::as_millis` truncates, so round up. This avoids
// turning sub-millisecond timeouts into a zero timeout, unless
// the caller explicitly requests that by specifying a zero
// timeout.
let to_ms = to
.checked_add(Duration::from_nanos(999_999))
.unwrap_or(to)
.as_millis();
cmp::min(MAX_SAFE_TIMEOUT, to_ms) as libc::c_int
})
.unwrap_or(-1);
let res = syscall!(poll(
fds.as_mut_ptr() as *mut libc::pollfd,
fds.len() as libc::nfds_t,
timeout,
));
match res {
Ok(num_events) => break Ok(num_events as usize),
// poll returns EAGAIN if we can retry it.
Err(e) if e.raw_os_error() == Some(libc::EAGAIN) => continue,
Err(e) => return Err(e),
}
}
}
#[derive(Debug, Clone)]
pub struct Event {
token: Token,
events: libc::c_short,
}
pub type Events = Vec<Event>;
pub mod event {
use std::fmt;
use crate::sys::Event;
use crate::Token;
use super::POLLRDHUP;
pub fn token(event: &Event) -> Token {
event.token
}
pub fn is_readable(event: &Event) -> bool {
(event.events & libc::POLLIN) != 0 || (event.events & libc::POLLPRI) != 0
}
pub fn is_writable(event: &Event) -> bool {
(event.events & libc::POLLOUT) != 0
}
pub fn is_error(event: &Event) -> bool {
(event.events & libc::POLLERR) != 0
}
pub fn is_read_closed(event: &Event) -> bool {
// Both halves of the socket have closed
(event.events & libc::POLLHUP) != 0
// Socket has received FIN or called shutdown(SHUT_RD)
|| (event.events & POLLRDHUP) != 0
}
pub fn is_write_closed(event: &Event) -> bool {
// Both halves of the socket have closed
(event.events & libc::POLLHUP) != 0
// Unix pipe write end has closed
|| ((event.events & libc::POLLOUT) != 0 && (event.events & libc::POLLERR) != 0)
// The other side (read end) of a Unix pipe has closed.
|| (event.events == libc::POLLERR)
}
pub fn is_priority(event: &Event) -> bool {
(event.events & libc::POLLPRI) != 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: &libc::c_short, want: &libc::c_short) -> bool {
(*got & want) != 0
}
debug_detail!(
EventsDetails(libc::c_short),
check_events,
libc::POLLIN,
libc::POLLPRI,
libc::POLLOUT,
libc::POLLRDNORM,
libc::POLLRDBAND,
libc::POLLWRNORM,
libc::POLLWRBAND,
libc::POLLERR,
libc::POLLHUP,
);
f.debug_struct("poll_event")
.field("token", &event.token)
.field("events", &EventsDetails(event.events))
.finish()
}
}
#[derive(Debug)]
pub(crate) struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
Ok(Waker {
selector: selector.try_clone()?,
token,
})
}
pub(crate) fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}
cfg_io_source! {
use crate::Registry;
struct InternalState {
selector: Selector,
token: Token,
interests: Interest,
fd: RawFd,
shared_record: Arc<RegistrationRecord>,
}
impl Drop for InternalState {
fn drop(&mut self) {
if self.shared_record.is_registered() {
let _ = self.selector.deregister(self.fd);
}
}
}
pub(crate) struct IoSourceState {
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(err) = &result {
if err.kind() == io::ErrorKind::WouldBlock {
self.inner.as_ref().map_or(Ok(()), |state| {
state
.selector
.reregister(state.fd, state.token, state.interests)
})?;
}
}
result
}
pub fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
if self.inner.is_some() {
Err(io::ErrorKind::AlreadyExists.into())
} else {
let selector = registry.selector().try_clone()?;
selector.register_internal(fd, token, interests).map(move |shared_record| {
let state = InternalState {
selector,
token,
interests,
fd,
shared_record,
};
self.inner = Some(Box::new(state));
})
}
}
pub fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
match self.inner.as_mut() {
Some(state) => registry
.selector()
.reregister(fd, token, interests)
.map(|()| {
state.token = token;
state.interests = interests;
}),
None => Err(io::ErrorKind::NotFound.into()),
}
}
pub fn deregister(&mut self, registry: &Registry, fd: RawFd) -> io::Result<()> {
if let Some(state) = self.inner.take() {
// Marking unregistered will short circuit the drop behaviour of calling
// deregister so the call to deregister below is strictly required.
state.shared_record.mark_unregistered();
}
registry.selector().deregister(fd)
}
}
}

View File

@@ -0,0 +1,50 @@
//! Both `kqueue(2)` and `epoll(2)` don't need to hold any user space state.
use std::io;
use std::os::fd::RawFd;
use crate::{Interest, Registry, Token};
pub(crate) struct IoSourceState;
impl IoSourceState {
pub(crate) fn new() -> IoSourceState {
IoSourceState
}
pub(crate) 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)
}
pub(crate) fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().register(fd, token, interests)
}
pub(crate) fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().reregister(fd, token, interests)
}
pub(crate) fn deregister(&mut self, registry: &Registry, fd: RawFd) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().deregister(fd)
}
}

121
vendor/mio/src/sys/unix/sourcefd.rs vendored Normal file
View File

@@ -0,0 +1,121 @@
use std::io;
#[cfg(not(target_os = "hermit"))]
use std::os::fd::RawFd;
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::RawFd;
use crate::{event, Interest, Registry, Token};
/// 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::fd::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::fd::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)
}
}

135
vendor/mio/src/sys/unix/tcp.rs vendored Normal file
View File

@@ -0,0 +1,135 @@
use std::io;
use std::mem::{size_of, MaybeUninit};
use std::net::{self, SocketAddr};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::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: i32) -> io::Result<()> {
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 = i32::from(reuseaddr);
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 = "fuchsia",
target_os = "hurd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "cygwin",
))]
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)
// OSs inherit the non-blocking flag from the listener, so we just have to
// set `CLOEXEC`.
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
all(target_arch = "x86", target_os = "android"),
))]
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| {
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
// See https://github.com/tokio-rs/mio/issues/1450
#[cfg(any(
all(target_arch = "x86", target_os = "android"),
target_os = "aix",
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
))]
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))
}

36
vendor/mio/src/sys/unix/udp.rs vendored Normal file
View File

@@ -0,0 +1,36 @@
use std::io;
use std::mem;
use std::net::{self, SocketAddr};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd};
use crate::sys::unix::net::{new_ip_socket, socket_addr};
pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
let fd = new_ip_socket(addr, libc::SOCK_DGRAM)?;
let socket = unsafe { net::UdpSocket::from_raw_fd(fd) };
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(bind(fd, raw_addr.as_ptr(), raw_addr_length))?;
Ok(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)
}

25
vendor/mio/src/sys/unix/uds/datagram.rs vendored Normal file
View File

@@ -0,0 +1,25 @@
use std::io;
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::net::{self, SocketAddr};
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::unix_addr;
pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result<net::UnixDatagram> {
let socket = unbound()?;
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
syscall!(bind(socket.as_raw_fd(), sockaddr, addrlen))?;
Ok(socket)
}
pub(crate) fn unbound() -> io::Result<net::UnixDatagram> {
let fd = new_socket(libc::AF_UNIX, libc::SOCK_DGRAM)?;
Ok(unsafe { net::UnixDatagram::from_raw_fd(fd) })
}
pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> {
super::pair(libc::SOCK_DGRAM)
}

122
vendor/mio/src/sys/unix/uds/listener.rs vendored Normal file
View File

@@ -0,0 +1,122 @@
use std::ffi::OsStr;
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;
use std::{io, mem};
use crate::net::UnixStream;
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::{path_offset, unix_addr};
use crate::sys::LISTEN_BACKLOG_SIZE;
pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result<net::UnixListener> {
let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
let socket = unsafe { net::UnixListener::from_raw_fd(fd) };
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
syscall!(bind(fd, sockaddr, addrlen))?;
syscall!(listen(fd, LISTEN_BACKLOG_SIZE))?;
Ok(socket)
}
pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
// SAFETY: `libc::sockaddr_un` zero filled 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 { mem::zeroed::<libc::sockaddr_un>() };
let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
// 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 = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
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) };
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?;
// See https://github.com/tokio-rs/mio/issues/1450
#[cfg(any(
all(target_arch = "x86", target_os = "android"),
target_os = "espidf",
target_os = "vita",
target_os = "nto",
))]
syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?;
Ok(s)
});
let socket = socket.map(UnixStream::from_std)?;
#[allow(unused_mut)] // See below.
let mut path_len = socklen as usize - path_offset(&sockaddr);
// On FreeBSD and Darwin, it returns a length of 14/16, but an unnamed (all
// zero) address. Map that to a length of 0 to match other OS.
if sockaddr.sun_path[0] == 0 {
path_len = 0;
}
// SAFETY: going from i8 to u8 is fine in this context.
let mut path =
unsafe { &*(&sockaddr.sun_path[..path_len] as *const [libc::c_char] as *const [u8]) };
// Remove last null as `SocketAddr::from_pathname` doesn't accept it.
if let Some(0) = path.last() {
path = &path[..path.len() - 1];
}
let address = SocketAddr::from_pathname(Path::new(OsStr::from_bytes(path)))?;
Ok((socket, address))
}

177
vendor/mio/src/sys/unix/uds/mod.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::FromRawFd;
use std::os::unix::net::SocketAddr;
use std::{io, mem, ptr};
pub(crate) mod datagram;
pub(crate) mod listener;
pub(crate) mod stream;
const UNNAMED_ADDRESS: &[u8] = &[];
/// Get the `sun_path` field offset of `sockaddr_un` for the target OS.
///
/// On Linux, this function 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.
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
}
/// Converts a Rust `SocketAddr` into the system representation.
fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) {
// SAFETY: `libc::sockaddr_un` zero filled 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 { mem::zeroed::<libc::sockaddr_un>() };
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
#[allow(unused_mut)] // Only used with abstract namespaces.
let mut offset = 0;
let addr = match address.as_pathname() {
Some(path) => path.as_os_str().as_bytes(),
#[cfg(any(target_os = "android", target_os = "linux"))]
None => match address.as_abstract_name() {
Some(name) => {
offset += 1;
name
}
None => UNNAMED_ADDRESS,
},
#[cfg(not(any(target_os = "android", target_os = "linux")))]
None => UNNAMED_ADDRESS,
};
// SAFETY: `addr` and `sockaddr.sun_path` are not overlapping and both point
// to valid memory.
// SAFETY: since `addr` is a valid Unix address, it must not be larger than
// `SUN_LEN` bytes, thus we won't overwrite the size of sockaddr.sun_path.
// SAFETY: null byte is already written because we zeroed the address above.
debug_assert!(offset + addr.len() <= sockaddr.sun_path.len());
unsafe {
ptr::copy_nonoverlapping(
addr.as_ptr(),
sockaddr.sun_path.as_mut_ptr().add(offset).cast(),
addr.len(),
)
};
let mut addrlen = path_offset(&sockaddr) + addr.len();
// +1 for null byte at the end of the path, not needed for abstract
// namespaces (which start with a null byte).
match addr.first() {
Some(&0) | None => {}
Some(_) => addrlen += 1,
}
// SAFETY: the length is fine to cast to `socklen_t` as it's 32 bits and the
// address can be at most `SUN_LEN` bytes.
(sockaddr, addrlen as _)
}
fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
where
T: FromRawFd,
{
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
)))]
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 (and others) 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 = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
))]
{
syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
}
Ok(pair)
}
#[cfg(test)]
mod tests {
use std::os::unix::net::SocketAddr;
use std::path::Path;
use std::str;
use super::{path_offset, unix_addr};
#[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 address = SocketAddr::from_pathname(Path::new(PATH)).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset + 1;
assert_eq!(expected as libc::socklen_t, actual)
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn abstract_address() {
#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
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 address = SocketAddr::from_abstract_name(PATH).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset;
assert_eq!(expected as libc::socklen_t, actual)
}
}

25
vendor/mio/src/sys/unix/uds/stream.rs vendored Normal file
View File

@@ -0,0 +1,25 @@
use std::io;
use std::os::fd::FromRawFd;
use std::os::unix::net::{self, SocketAddr};
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::unix_addr;
pub(crate) fn connect_addr(address: &SocketAddr) -> io::Result<net::UnixStream> {
let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
let socket = unsafe { net::UnixStream::from_raw_fd(fd) };
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
match syscall!(connect(fd, sockaddr, addrlen)) {
Ok(_) => {}
Err(ref err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {}
Err(e) => return Err(e),
}
Ok(socket)
}
pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> {
super::pair(libc::SOCK_STREAM)
}

View File

@@ -0,0 +1,88 @@
use std::fs::File;
use std::io::{self, Read, Write};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd};
use crate::sys::Selector;
use crate::{Interest, Token};
/// 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(crate) struct Waker {
fd: File,
}
impl Waker {
#[allow(dead_code)] // Not used by the `poll(2)` implementation.
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let waker = Waker::new_unregistered()?;
selector.register(waker.fd.as_raw_fd(), token, Interest::READABLE)?;
Ok(waker)
}
pub(crate) fn new_unregistered() -> io::Result<Waker> {
#[cfg(not(target_os = "espidf"))]
let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK;
// ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag.
#[cfg(target_os = "espidf")]
let flags = 0;
let fd = syscall!(eventfd(0, flags))?;
let file = unsafe { File::from_raw_fd(fd) };
Ok(Waker { fd: file })
}
#[allow(clippy::unused_io_amount)] // Don't care about partial writes.
pub(crate) fn wake(&self) -> io::Result<()> {
// The epoll emulation on some illumos systems currently requires
// the eventfd to be read before an edge-triggered read event is
// generated.
// See https://www.illumos.org/issues/16700.
#[cfg(target_os = "illumos")]
self.reset()?;
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),
}
}
#[allow(dead_code)] // Only used by the `poll(2)` implementation.
pub(crate) fn ack_and_reset(&self) {
let _ = self.reset();
}
/// Reset the eventfd object, only need to call this if `wake` fails.
#[allow(clippy::unused_io_amount)] // Don't care about partial reads.
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),
}
}
}
impl AsRawFd for Waker {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}

28
vendor/mio/src/sys/unix/waker/kqueue.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
use std::io;
use crate::sys::Selector;
use crate::Token;
/// 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(crate) struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let selector = selector.try_clone()?;
selector.setup_waker(token)?;
Ok(Waker { selector, token })
}
pub(crate) fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}

82
vendor/mio/src/sys/unix/waker/pipe.rs vendored Normal file
View File

@@ -0,0 +1,82 @@
use std::fs::File;
use std::io::{self, Read, Write};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd};
use crate::sys::unix::pipe;
use crate::sys::Selector;
use crate::{Interest, Token};
/// 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(crate) struct Waker {
sender: File,
receiver: File,
}
impl Waker {
#[allow(dead_code)] // Not used by the `poll(2)` implementation.
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let waker = Waker::new_unregistered()?;
selector.register(waker.receiver.as_raw_fd(), token, Interest::READABLE)?;
Ok(waker)
}
pub(crate) fn new_unregistered() -> io::Result<Waker> {
let [receiver, sender] = pipe::new_raw()?;
let sender = unsafe { File::from_raw_fd(sender) };
let receiver = unsafe { File::from_raw_fd(receiver) };
Ok(Waker { sender, receiver })
}
pub(crate) 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.
// See https://www.illumos.org/issues/13436.
#[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),
}
}
#[allow(dead_code)] // Only used by the `poll(2)` implementation.
pub(crate) fn ack_and_reset(&self) {
self.empty();
}
/// 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,
}
}
}
}
impl AsRawFd for Waker {
fn as_raw_fd(&self) -> RawFd {
self.receiver.as_raw_fd()
}
}

369
vendor/mio/src/sys/wasi/mod.rs vendored Normal file
View File

@@ -0,0 +1,369 @@
//! # Notes
//!
//! The current implementation is somewhat limited. The `Waker` is not
//! implemented, as at the time of writing there is no way to support to wake-up
//! a thread from calling `poll_oneoff`.
//!
//! Furthermore the (re/de)register functions also don't work while concurrently
//! polling as both registering and polling requires a lock on the
//! `subscriptions`.
//!
//! Finally `Selector::try_clone`, required by `Registry::try_clone`, doesn't
//! work. However this could be implemented by use of an `Arc`.
//!
//! In summary, this only (barely) works using a single thread.
use std::cmp::min;
use std::io;
#[cfg(all(feature = "net", debug_assertions))]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[cfg(feature = "net")]
use crate::{Interest, Token};
cfg_net! {
pub(crate) mod tcp {
use std::io;
use std::net::{self, SocketAddr};
pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
let (stream, addr) = listener.accept()?;
stream.set_nonblocking(true)?;
Ok((stream, addr))
}
}
}
/// Unique id for use as `SelectorId`.
#[cfg(all(debug_assertions, feature = "net"))]
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
pub(crate) struct Selector {
#[cfg(all(debug_assertions, feature = "net"))]
id: usize,
/// Subscriptions (reads events) we're interested in.
subscriptions: Arc<Mutex<Vec<wasi::Subscription>>>,
}
impl Selector {
pub(crate) fn new() -> io::Result<Selector> {
Ok(Selector {
#[cfg(all(debug_assertions, feature = "net"))]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
subscriptions: Arc::new(Mutex::new(Vec::new())),
})
}
#[cfg(all(debug_assertions, feature = "net"))]
pub(crate) fn id(&self) -> usize {
self.id
}
pub(crate) fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
events.clear();
let mut subscriptions = self.subscriptions.lock().unwrap();
// If we want to a use a timeout in the `wasi_poll_oneoff()` function
// we need another subscription to the list.
if let Some(timeout) = timeout {
subscriptions.push(timeout_subscription(timeout));
}
// `poll_oneoff` needs the same number of events as subscriptions.
let length = subscriptions.len();
events.reserve(length);
debug_assert!(events.capacity() >= length);
#[cfg(debug_assertions)]
if length == 0 {
warn!(
"calling mio::Poll::poll with empty subscriptions, this likely not what you want"
);
}
let res = unsafe { wasi::poll_oneoff(subscriptions.as_ptr(), events.as_mut_ptr(), length) };
// Remove the timeout subscription we possibly added above.
if timeout.is_some() {
let timeout_sub = subscriptions.pop();
debug_assert_eq!(
timeout_sub.unwrap().u.tag,
wasi::EVENTTYPE_CLOCK.raw(),
"failed to remove timeout subscription"
);
}
drop(subscriptions); // Unlock.
match res {
Ok(n_events) => {
// Safety: `poll_oneoff` initialises the `events` for us.
unsafe { events.set_len(n_events) };
// Remove the timeout event.
if timeout.is_some() {
if let Some(index) = events.iter().position(is_timeout_event) {
events.swap_remove(index);
}
}
check_errors(&events)
}
Err(err) => Err(io_err(err)),
}
}
pub(crate) fn try_clone(&self) -> io::Result<Selector> {
Ok(Selector {
#[cfg(all(debug_assertions, feature = "net"))]
id: self.id,
subscriptions: self.subscriptions.clone(),
})
}
cfg_io_source! {
pub(crate) fn register(
&self,
fd: wasi::Fd,
token: Token,
interests: Interest,
) -> io::Result<()> {
let mut subscriptions = self.subscriptions.lock().unwrap();
if interests.is_writable() {
let subscription = wasi::Subscription {
userdata: token.0 as wasi::Userdata,
u: wasi::SubscriptionU {
tag: wasi::EVENTTYPE_FD_WRITE.raw(),
u: wasi::SubscriptionUU {
fd_write: wasi::SubscriptionFdReadwrite {
file_descriptor: fd,
},
},
},
};
subscriptions.push(subscription);
}
if interests.is_readable() {
let subscription = wasi::Subscription {
userdata: token.0 as wasi::Userdata,
u: wasi::SubscriptionU {
tag: wasi::EVENTTYPE_FD_READ.raw(),
u: wasi::SubscriptionUU {
fd_read: wasi::SubscriptionFdReadwrite {
file_descriptor: fd,
},
},
},
};
subscriptions.push(subscription);
}
Ok(())
}
pub(crate) fn reregister(
&self,
fd: wasi::Fd,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.deregister(fd)
.and_then(|()| self.register(fd, token, interests))
}
pub(crate) fn deregister(&self, fd: wasi::Fd) -> io::Result<()> {
let mut subscriptions = self.subscriptions.lock().unwrap();
let predicate = |subscription: &wasi::Subscription| {
// Safety: `subscription.u.tag` defines the type of the union in
// `subscription.u.u`.
match subscription.u.tag {
t if t == wasi::EVENTTYPE_FD_WRITE.raw() => unsafe {
subscription.u.u.fd_write.file_descriptor == fd
},
t if t == wasi::EVENTTYPE_FD_READ.raw() => unsafe {
subscription.u.u.fd_read.file_descriptor == fd
},
_ => false,
}
};
let mut ret = Err(io::ErrorKind::NotFound.into());
while let Some(index) = subscriptions.iter().position(predicate) {
subscriptions.swap_remove(index);
ret = Ok(())
}
ret
}
}
}
/// Token used to a add a timeout subscription, also used in removing it again.
const TIMEOUT_TOKEN: wasi::Userdata = wasi::Userdata::MAX;
/// Returns a `wasi::Subscription` for `timeout`.
fn timeout_subscription(timeout: Duration) -> wasi::Subscription {
wasi::Subscription {
userdata: TIMEOUT_TOKEN,
u: wasi::SubscriptionU {
tag: wasi::EVENTTYPE_CLOCK.raw(),
u: wasi::SubscriptionUU {
clock: wasi::SubscriptionClock {
id: wasi::CLOCKID_MONOTONIC,
// Timestamp is in nanoseconds.
timeout: min(wasi::Timestamp::MAX as u128, timeout.as_nanos())
as wasi::Timestamp,
// Give the implementation another millisecond to coalesce
// events.
precision: Duration::from_millis(1).as_nanos() as wasi::Timestamp,
// Zero means the `timeout` is considered relative to the
// current time.
flags: 0,
},
},
},
}
}
fn is_timeout_event(event: &wasi::Event) -> bool {
event.type_ == wasi::EVENTTYPE_CLOCK && event.userdata == TIMEOUT_TOKEN
}
/// Check all events for possible errors, it returns the first error found.
fn check_errors(events: &[Event]) -> io::Result<()> {
for event in events {
if event.error != wasi::ERRNO_SUCCESS {
return Err(io_err(event.error));
}
}
Ok(())
}
/// Convert `wasi::Errno` into an `io::Error`.
fn io_err(errno: wasi::Errno) -> io::Error {
// TODO: check if this is valid.
io::Error::from_raw_os_error(errno.raw() as i32)
}
pub(crate) type Events = Vec<Event>;
pub(crate) type Event = wasi::Event;
pub(crate) mod event {
use std::fmt;
use crate::sys::Event;
use crate::Token;
pub(crate) fn token(event: &Event) -> Token {
Token(event.userdata as usize)
}
pub(crate) fn is_readable(event: &Event) -> bool {
event.type_ == wasi::EVENTTYPE_FD_READ
}
pub(crate) fn is_writable(event: &Event) -> bool {
event.type_ == wasi::EVENTTYPE_FD_WRITE
}
pub(crate) fn is_error(_: &Event) -> bool {
// Not supported? It could be that `wasi::Event.error` could be used for
// this, but the docs say `error that occurred while processing the
// subscription request`, so it's checked in `Select::select` already.
false
}
pub(crate) fn is_read_closed(event: &Event) -> bool {
event.type_ == wasi::EVENTTYPE_FD_READ
// Safety: checked the type of the union above.
&& (event.fd_readwrite.flags & wasi::EVENTRWFLAGS_FD_READWRITE_HANGUP) != 0
}
pub(crate) fn is_write_closed(event: &Event) -> bool {
event.type_ == wasi::EVENTTYPE_FD_WRITE
// Safety: checked the type of the union above.
&& (event.fd_readwrite.flags & wasi::EVENTRWFLAGS_FD_READWRITE_HANGUP) != 0
}
pub(crate) fn is_priority(_: &Event) -> bool {
// Not supported.
false
}
pub(crate) fn is_aio(_: &Event) -> bool {
// Not supported.
false
}
pub(crate) fn is_lio(_: &Event) -> bool {
// Not supported.
false
}
pub(crate) fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result {
debug_detail!(
TypeDetails(wasi::Eventtype),
PartialEq::eq,
wasi::EVENTTYPE_CLOCK,
wasi::EVENTTYPE_FD_READ,
wasi::EVENTTYPE_FD_WRITE,
);
#[allow(clippy::trivially_copy_pass_by_ref)]
fn check_flag(got: &wasi::Eventrwflags, want: &wasi::Eventrwflags) -> bool {
(got & want) != 0
}
debug_detail!(
EventrwflagsDetails(wasi::Eventrwflags),
check_flag,
wasi::EVENTRWFLAGS_FD_READWRITE_HANGUP,
);
struct EventFdReadwriteDetails(wasi::EventFdReadwrite);
impl fmt::Debug for EventFdReadwriteDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventFdReadwrite")
.field("nbytes", &self.0.nbytes)
.field("flags", &EventrwflagsDetails(self.0.flags))
.finish()
}
}
f.debug_struct("Event")
.field("userdata", &event.userdata)
.field("error", &event.error)
.field("type", &TypeDetails(event.type_))
.field("fd_readwrite", &EventFdReadwriteDetails(event.fd_readwrite))
.finish()
}
}
cfg_os_poll! {
cfg_io_source! {
pub(crate) struct IoSourceState;
impl IoSourceState {
pub(crate) fn new() -> IoSourceState {
IoSourceState
}
pub(crate) 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)
}
}
}
}

243
vendor/mio/src/sys/windows/afd.rs vendored Normal file
View File

@@ -0,0 +1,243 @@
use std::ffi::c_void;
use std::fmt;
use std::fs::File;
use std::io;
use std::mem::size_of;
use std::os::windows::io::AsRawHandle;
use windows_sys::Wdk::Storage::FileSystem::NtCancelIoFileEx;
use windows_sys::Wdk::System::IO::NtDeviceIoControlFile;
use windows_sys::Win32::Foundation::{
RtlNtStatusToDosError, HANDLE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS,
};
use windows_sys::Win32::System::IO::{IO_STATUS_BLOCK, IO_STATUS_BLOCK_0};
const IOCTL_AFD_POLL: u32 = 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: u32,
pub status: NTSTATUS,
}
unsafe impl Send for AfdPollHandleInfo {}
#[repr(C)]
pub struct AfdPollInfo {
pub timeout: i64,
// Can have only value 1.
pub number_of_handles: u32,
pub exclusive: u32,
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: *mut c_void,
) -> io::Result<bool> {
let info_ptr = info as *mut _ as *mut c_void;
(*iosb).Anonymous.Status = STATUS_PENDING;
let status = NtDeviceIoControlFile(
self.fd.as_raw_handle() as HANDLE,
std::ptr::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).Anonymous.Status != STATUS_PENDING {
return Ok(());
}
let mut cancel_iosb = IO_STATUS_BLOCK {
Anonymous: IO_STATUS_BLOCK_0 { Status: 0 },
Information: 0,
};
let status = NtCancelIoFileEx(self.fd.as_raw_handle() as 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::ptr::null_mut;
use std::sync::atomic::{AtomicUsize, Ordering};
use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES;
use windows_sys::Wdk::Storage::FileSystem::{NtCreateFile, FILE_OPEN};
use windows_sys::Win32::Foundation::{INVALID_HANDLE_VALUE, UNICODE_STRING};
use windows_sys::Win32::Storage::FileSystem::{
SetFileCompletionNotificationModes, FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE,
};
use windows_sys::Win32::System::WindowsProgramming::FILE_SKIP_SET_EVENT_ON_HANDLE;
use super::iocp::CompletionPort;
const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
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::<u16>()) as u16,
MaximumLength: (AFD_HELPER_NAME.len() * size_of::<u16>()) as u16,
Buffer: AFD_HELPER_NAME.as_ptr() as *mut _,
};
const AFD_HELPER_NAME: &[u16] = &[
'\\' 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(crate) fn new(cp: &CompletionPort) -> io::Result<Afd> {
let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE;
let mut iosb = IO_STATUS_BLOCK {
Anonymous: IO_STATUS_BLOCK_0 { 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 differentiate 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 as u8 // This is just 2, so fits in u8
) {
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;

169
vendor/mio/src/sys/windows/event.rs vendored Normal file
View File

@@ -0,0 +1,169 @@
use std::fmt;
use super::afd;
use super::iocp::CompletionStatus;
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())
}
#[cfg(feature = "os-ext")]
pub(super) fn to_completion_status_with_overlapped(
&self,
overlapped: *mut super::Overlapped,
) -> CompletionStatus {
CompletionStatus::new(self.flags, self.data as usize, overlapped)
}
}
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();
}
}
}

30
vendor/mio/src/sys/windows/handle.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
use std::os::windows::io::RawHandle;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
/// Wrapper around a Windows HANDLE so that we close it upon drop in all scenarios
#[derive(Debug)]
pub struct Handle(HANDLE);
impl Handle {
#[inline]
pub fn new(handle: HANDLE) -> Self {
Self(handle)
}
pub fn raw(&self) -> HANDLE {
self.0
}
pub fn into_raw(self) -> RawHandle {
let ret = self.0;
// This is super important so that drop is not called!
std::mem::forget(self);
ret as RawHandle
}
}
impl Drop for Handle {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}

View File

@@ -0,0 +1,40 @@
use std::fmt;
use std::ops::{Deref, DerefMut};
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
pub struct IoStatusBlock(IO_STATUS_BLOCK);
cfg_io_source! {
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK_0;
impl IoStatusBlock {
pub fn zeroed() -> Self {
Self(IO_STATUS_BLOCK {
Anonymous: IO_STATUS_BLOCK_0 { 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()
}
}

282
vendor/mio/src/sys/windows/iocp.rs vendored Normal file
View File

@@ -0,0 +1,282 @@
//! Bindings to IOCP, I/O Completion Ports
use super::{Handle, Overlapped};
use std::cmp;
use std::fmt;
use std::io;
use std::mem;
use std::os::windows::io::*;
use std::ptr::null_mut;
use std::time::Duration;
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, OVERLAPPED,
OVERLAPPED_ENTRY,
};
/// A handle to an Windows I/O Completion Port.
#[derive(Debug)]
pub(crate) struct CompletionPort {
handle: Handle,
}
// SAFETY: `Handles`s are, in general, not thread-safe. However, we only used `Handle`s for
// resources that are thread-safe in `CompletionPort`.
unsafe impl Send for CompletionPort {}
// SAFETY: `Handles`s are, in general, not thread-safe. However, we only used `Handle`s for
// resources that are thread-safe in `CompletionPort`.
unsafe impl Sync for CompletionPort {}
/// A status message received from an I/O completion port.
///
/// These statuses can be created via the `new` or `empty` constructors and then
/// provided to a completion port, or they are read out of a completion port.
/// The fields of each status are read through its accessor methods.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct CompletionStatus(OVERLAPPED_ENTRY);
impl fmt::Debug for CompletionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CompletionStatus(OVERLAPPED_ENTRY)")
}
}
unsafe impl Send for CompletionStatus {}
unsafe impl Sync for CompletionStatus {}
impl CompletionPort {
/// Creates a new I/O completion port with the specified concurrency value.
///
/// The number of threads given corresponds to the level of concurrency
/// allowed for threads associated with this port. Consult the Windows
/// documentation for more information about this value.
pub fn new(threads: u32) -> io::Result<CompletionPort> {
let ret = unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, null_mut(), 0, threads) };
if ret.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(CompletionPort {
handle: Handle::new(ret),
})
}
}
/// Associates a new `HANDLE` to this I/O completion port.
///
/// This function will associate the given handle to this port with the
/// given `token` to be returned in status messages whenever it receives a
/// notification.
///
/// Any object which is convertible to a `HANDLE` via the `AsRawHandle`
/// trait can be provided to this function, such as `std::fs::File` and
/// friends.
#[cfg(any(feature = "net", feature = "os-ext"))]
pub fn add_handle<T: AsRawHandle + ?Sized>(&self, token: usize, t: &T) -> io::Result<()> {
let ret = unsafe {
CreateIoCompletionPort(t.as_raw_handle() as HANDLE, self.handle.raw(), token, 0)
};
if ret.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
/// Dequeues a number of completion statuses from this I/O completion port.
///
/// This function is the same as `get` except that it may return more than
/// one status. A buffer of "zero" statuses is provided (the contents are
/// not read) and then on success this function will return a sub-slice of
/// statuses which represent those which were dequeued from this port. This
/// function does not wait to fill up the entire list of statuses provided.
///
/// Like with `get`, a timeout may be specified for this operation.
pub fn get_many<'a>(
&self,
list: &'a mut [CompletionStatus],
timeout: Option<Duration>,
) -> io::Result<&'a mut [CompletionStatus]> {
debug_assert_eq!(
mem::size_of::<CompletionStatus>(),
mem::size_of::<OVERLAPPED_ENTRY>()
);
let mut removed = 0;
let timeout = duration_millis(timeout);
let len = cmp::min(list.len(), u32::MAX as usize) as u32;
let ret = unsafe {
GetQueuedCompletionStatusEx(
self.handle.raw(),
list.as_ptr() as *mut _,
len,
&mut removed,
timeout,
0,
)
};
if ret == 0 {
Err(io::Error::last_os_error())
} else {
Ok(&mut list[..removed as usize])
}
}
/// Posts a new completion status onto this I/O completion port.
///
/// This function will post the given status, with custom parameters, to the
/// port. Threads blocked in `get` or `get_many` will eventually receive
/// this status.
pub fn post(&self, status: CompletionStatus) -> io::Result<()> {
let ret = unsafe {
PostQueuedCompletionStatus(
self.handle.raw(),
status.0.dwNumberOfBytesTransferred,
status.0.lpCompletionKey,
status.0.lpOverlapped,
)
};
if ret == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
impl AsRawHandle for CompletionPort {
fn as_raw_handle(&self) -> RawHandle {
self.handle.raw() as RawHandle
}
}
impl FromRawHandle for CompletionPort {
unsafe fn from_raw_handle(handle: RawHandle) -> CompletionPort {
CompletionPort {
handle: Handle::new(handle as HANDLE),
}
}
}
impl IntoRawHandle for CompletionPort {
fn into_raw_handle(self) -> RawHandle {
self.handle.into_raw()
}
}
impl CompletionStatus {
/// Creates a new completion status with the provided parameters.
///
/// This function is useful when creating a status to send to a port with
/// the `post` method. The parameters are opaquely passed through and not
/// interpreted by the system at all.
pub(crate) fn new(bytes: u32, token: usize, overlapped: *mut Overlapped) -> Self {
CompletionStatus(OVERLAPPED_ENTRY {
dwNumberOfBytesTransferred: bytes,
lpCompletionKey: token,
lpOverlapped: overlapped as *mut _,
Internal: 0,
})
}
/// Creates a new borrowed completion status from the borrowed
/// `OVERLAPPED_ENTRY` argument provided.
///
/// This method will wrap the `OVERLAPPED_ENTRY` in a `CompletionStatus`,
/// returning the wrapped structure.
#[cfg(feature = "os-ext")]
pub fn from_entry(entry: &OVERLAPPED_ENTRY) -> &Self {
// Safety: CompletionStatus is repr(transparent) w/ OVERLAPPED_ENTRY, so
// a reference to one is guaranteed to be layout compatible with the
// reference to another.
unsafe { &*(entry as *const _ as *const _) }
}
/// Creates a new "zero" completion status.
///
/// This function is useful when creating a stack buffer or vector of
/// completion statuses to be passed to the `get_many` function.
pub fn zero() -> Self {
Self::new(0, 0, null_mut())
}
/// Returns the number of bytes that were transferred for the I/O operation
/// associated with this completion status.
pub fn bytes_transferred(&self) -> u32 {
self.0.dwNumberOfBytesTransferred
}
/// Returns the completion key value associated with the file handle whose
/// I/O operation has completed.
///
/// A completion key is a per-handle key that is specified when it is added
/// to an I/O completion port via `add_handle` or `add_socket`.
pub fn token(&self) -> usize {
self.0.lpCompletionKey as usize
}
/// Returns a pointer to the `Overlapped` structure that was specified when
/// the I/O operation was started.
pub fn overlapped(&self) -> *mut OVERLAPPED {
self.0.lpOverlapped
}
/// Returns a pointer to the internal `OVERLAPPED_ENTRY` object.
pub fn entry(&self) -> &OVERLAPPED_ENTRY {
&self.0
}
}
#[inline]
fn duration_millis(dur: Option<Duration>) -> u32 {
if let Some(dur) = dur {
// `Duration::as_millis` truncates, so round up. This avoids
// turning sub-millisecond timeouts into a zero timeout, unless
// the caller explicitly requests that by specifying a zero
// timeout.
let dur_ms = dur
.checked_add(Duration::from_nanos(999_999))
.unwrap_or(dur)
.as_millis();
cmp::min(dur_ms, u32::MAX as u128) as u32
} else {
u32::MAX
}
}
#[cfg(test)]
mod tests {
use super::{CompletionPort, CompletionStatus};
#[test]
fn is_send_sync() {
fn is_send_sync<T: Send + Sync>() {}
is_send_sync::<CompletionPort>();
}
#[test]
fn get_many() {
let c = CompletionPort::new(1).unwrap();
c.post(CompletionStatus::new(1, 2, 3 as *mut _)).unwrap();
c.post(CompletionStatus::new(4, 5, 6 as *mut _)).unwrap();
let mut s = vec![CompletionStatus::zero(); 4];
{
let s = c.get_many(&mut s, None).unwrap();
assert_eq!(s.len(), 2);
assert_eq!(s[0].bytes_transferred(), 1);
assert_eq!(s[0].token(), 2);
assert_eq!(s[0].overlapped(), 3 as *mut _);
assert_eq!(s[1].bytes_transferred(), 4);
assert_eq!(s[1].token(), 5);
assert_eq!(s[1].overlapped(), 6 as *mut _);
}
assert_eq!(s[2].bytes_transferred(), 0);
assert_eq!(s[2].token(), 0);
assert_eq!(s[2].overlapped(), 0 as *mut _);
}
}

154
vendor/mio/src/sys/windows/mod.rs vendored Normal file
View File

@@ -0,0 +1,154 @@
mod afd;
pub mod event;
pub use event::{Event, Events};
mod handle;
use handle::Handle;
mod io_status_block;
mod iocp;
mod overlapped;
use overlapped::Overlapped;
mod selector;
pub use selector::Selector;
// 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;
pub use selector::{SelectorInner, SockState};
}
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()),
}
}
}
}

1105
vendor/mio/src/sys/windows/named_pipe.rs vendored Normal file

File diff suppressed because it is too large Load Diff

111
vendor/mio/src/sys/windows/net.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
use std::io;
use std::mem;
use std::net::SocketAddr;
use std::sync::Once;
use windows_sys::Win32::Networking::WinSock::{
closesocket, ioctlsocket, socket, AF_INET, AF_INET6, FIONBIO, IN6_ADDR, IN6_ADDR_0,
INVALID_SOCKET, IN_ADDR, IN_ADDR_0, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_IN6_0,
SOCKET,
};
/// Initialise the network stack for Windows.
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: i32) -> io::Result<SOCKET> {
let domain = match addr {
SocketAddr::V4(..) => AF_INET,
SocketAddr::V6(..) => AF_INET6,
};
new_socket(domain.into(), socket_type)
}
pub(crate) fn new_socket(domain: u32, socket_type: i32) -> io::Result<SOCKET> {
init();
let socket = syscall!(
socket(domain as i32, socket_type, 0),
PartialEq::eq,
INVALID_SOCKET
)?;
if let Err(err) = syscall!(ioctlsocket(socket, FIONBIO, &mut 1), PartialEq::ne, 0) {
let _ = unsafe { closesocket(socket) };
return Err(err);
}
Ok(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,
}
impl SocketAddrCRepr {
pub(crate) fn as_ptr(&self) -> *const SOCKADDR {
self as *const _ as *const SOCKADDR
}
}
pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, i32) {
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_0>();
s_un.S_addr = u32::from_ne_bytes(addr.ip().octets());
IN_ADDR { S_un: s_un }
};
let sockaddr_in = SOCKADDR_IN {
sin_family: AF_INET as u16, // 1
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 i32)
}
SocketAddr::V6(ref addr) => {
let sin6_addr = unsafe {
let mut u = mem::zeroed::<IN6_ADDR_0>();
u.Byte = addr.ip().octets();
IN6_ADDR { u }
};
let u = unsafe {
let mut u = mem::zeroed::<SOCKADDR_IN6_0>();
u.sin6_scope_id = addr.scope_id();
u
};
let sockaddr_in6 = SOCKADDR_IN6 {
sin6_family: AF_INET6 as u16, // 23
sin6_port: addr.port().to_be(),
sin6_addr,
sin6_flowinfo: addr.flowinfo(),
Anonymous: u,
};
let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
(sockaddr, mem::size_of::<SOCKADDR_IN6>() as i32)
}
}
}

View File

@@ -0,0 +1,35 @@
use crate::sys::windows::Event;
use std::cell::UnsafeCell;
use std::fmt;
use windows_sys::Win32::System::IO::{OVERLAPPED, OVERLAPPED_ENTRY};
#[repr(C)]
pub(crate) struct Overlapped {
inner: UnsafeCell<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(unsafe { std::mem::zeroed() }),
callback: cb,
}
}
pub(crate) fn as_ptr(&self) -> *const OVERLAPPED {
self.inner.get()
}
}
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 {}

741
vendor/mio/src/sys/windows/selector.rs vendored Normal file
View File

@@ -0,0 +1,741 @@
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 super::iocp::{CompletionPort, CompletionStatus};
use std::collections::VecDeque;
use std::ffi::c_void;
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 windows_sys::Win32::Foundation::{
ERROR_INVALID_HANDLE, ERROR_IO_PENDING, HANDLE, STATUS_CANCELLED, WAIT_TIMEOUT,
};
use windows_sys::Win32::System::IO::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;
self.poll_info.timeout = 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.Anonymous.Status == STATUS_CANCELLED {
/* The poll request was cancelled by CancelIoEx. */
} else if self.iosb.Anonymous.Status < 0 {
/* 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>>>) -> *mut c_void {
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 implementation 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>,
}
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),
}
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
Ok(Selector {
#[cfg(debug_assertions)]
id: self.id,
inner: Arc::clone(&self.inner),
})
}
/// # 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)
}
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 windows_sys::Win32::Networking::WinSock::{
WSAGetLastError, WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE,
SIO_BSP_HANDLE_POLL, SIO_BSP_HANDLE_SELECT, 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 *mut c_void,
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, 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 &[
SIO_BSP_HANDLE_SELECT,
SIO_BSP_HANDLE_POLL,
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 wouldn'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
}
}

64
vendor/mio/src/sys/windows/tcp.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
use std::io;
use std::net::{self, SocketAddr};
use std::os::windows::io::AsRawSocket;
use windows_sys::Win32::Networking::WinSock::{self, SOCKET, SOCKET_ERROR, SOCK_STREAM};
use crate::sys::windows::net::{new_ip_socket, socket_addr};
pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result<SOCKET> {
new_ip_socket(address, SOCK_STREAM)
}
pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> {
use WinSock::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 WinSock::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: i32) -> io::Result<()> {
use WinSock::listen;
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()
}

46
vendor/mio/src/sys/windows/udp.rs vendored Normal file
View File

@@ -0,0 +1,46 @@
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; // windows-sys uses usize, stdlib uses u32/u64.
use crate::sys::windows::net::{new_ip_socket, socket_addr};
use windows_sys::Win32::Networking::WinSock::{
bind as win_bind, getsockopt, IPPROTO_IPV6, IPV6_V6ONLY, SOCKET_ERROR, SOCK_DGRAM,
};
pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
let raw_socket = new_ip_socket(addr, SOCK_DGRAM)?;
let socket = unsafe { net::UdpSocket::from_raw_socket(raw_socket as StdSocket) };
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(
win_bind(raw_socket, raw_addr.as_ptr(), raw_addr_length),
PartialEq::eq,
SOCKET_ERROR
)?;
Ok(socket)
}
pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result<bool> {
let mut optval: MaybeUninit<i32> = MaybeUninit::uninit();
let mut optlen = mem::size_of::<i32>() as i32;
syscall!(
getsockopt(
socket.as_raw_socket() as usize,
IPPROTO_IPV6 as i32,
IPV6_V6ONLY as i32,
optval.as_mut_ptr().cast(),
&mut optlen,
),
PartialEq::eq,
SOCKET_ERROR
)?;
debug_assert_eq!(optlen as usize, mem::size_of::<i32>());
// Safety: `getsockopt` initialised `optval` for us.
let optval = unsafe { optval.assume_init() };
Ok(optval != 0)
}

29
vendor/mio/src/sys/windows/waker.rs vendored Normal file
View File

@@ -0,0 +1,29 @@
use crate::sys::windows::Event;
use crate::sys::windows::Selector;
use crate::Token;
use super::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())
}
}