Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

514
vendor/polling/src/epoll.rs vendored Normal file
View File

@@ -0,0 +1,514 @@
//! Bindings to epoll (Linux, Android).
use std::io;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::time::{Duration, Instant};
#[cfg(not(target_os = "redox"))]
use rustix::event::{eventfd, EventfdFlags};
#[cfg(not(target_os = "redox"))]
use rustix::time::{
timerfd_create, timerfd_settime, Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags,
};
use rustix::buffer::spare_capacity;
use rustix::event::{epoll, Timespec};
use rustix::fd::OwnedFd;
use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags};
use rustix::pipe::{pipe, pipe_with, PipeFlags};
use crate::{Event, PollMode};
/// Interface to epoll.
#[derive(Debug)]
pub struct Poller {
/// File descriptor for the epoll instance.
epoll_fd: OwnedFd,
/// Notifier used to wake up epoll.
notifier: Notifier,
/// File descriptor for the timerfd that produces timeouts.
///
/// Redox does not support timerfd.
#[cfg(not(target_os = "redox"))]
timer_fd: Option<OwnedFd>,
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
// Create an epoll instance.
//
// Use `epoll_create1` with `EPOLL_CLOEXEC`.
let epoll_fd = epoll::create(epoll::CreateFlags::CLOEXEC)?;
// Set up notifier and timerfd.
let notifier = Notifier::new()?;
#[cfg(not(target_os = "redox"))]
let timer_fd = timerfd_create(
TimerfdClockId::Monotonic,
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
)
.ok();
let poller = Poller {
epoll_fd,
notifier,
#[cfg(not(target_os = "redox"))]
timer_fd,
};
unsafe {
#[cfg(not(target_os = "redox"))]
if let Some(ref timer_fd) = poller.timer_fd {
poller.add(
timer_fd.as_raw_fd(),
Event::none(crate::NOTIFY_KEY),
PollMode::Oneshot,
)?;
}
poller.add(
poller.notifier.as_fd().as_raw_fd(),
Event::readable(crate::NOTIFY_KEY),
PollMode::Oneshot,
)?;
}
#[cfg(feature = "tracing")]
tracing::trace!(
epoll_fd = ?poller.epoll_fd.as_raw_fd(),
notifier = ?poller.notifier,
"new",
);
Ok(poller)
}
/// Whether this poller supports level-triggered events.
pub fn supports_level(&self) -> bool {
true
}
/// Whether the poller supports edge-triggered events.
pub fn supports_edge(&self) -> bool {
true
}
/// Adds a new file descriptor.
///
/// # Safety
///
/// The `fd` must be a valid file descriptor. The usual condition of remaining registered in
/// the `Poller` doesn't apply to `epoll`.
pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"add",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
epoll::add(
&self.epoll_fd,
unsafe { rustix::fd::BorrowedFd::borrow_raw(fd) },
epoll::EventData::new_u64(ev.key as u64),
epoll_flags(&ev, mode) | ev.extra.flags,
)?;
Ok(())
}
/// Modifies an existing file descriptor.
pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
epoll::modify(
&self.epoll_fd,
fd,
epoll::EventData::new_u64(ev.key as u64),
epoll_flags(&ev, mode) | ev.extra.flags,
)?;
Ok(())
}
/// Deletes a file descriptor.
#[cfg_attr(not(feature = "tracing"), inline(always))]
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
epoll::delete(&self.epoll_fd, fd)?;
Ok(())
}
/// Waits for I/O events with an optional deadline.
#[allow(clippy::needless_update)]
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
#[cfg(not(target_os = "redox"))]
if let Some(ref timer_fd) = self.timer_fd {
// Configure the timeout using timerfd.
let new_val = Itimerspec {
it_interval: TS_ZERO,
it_value: match timeout {
None => TS_ZERO,
Some(t) => {
let mut ts = TS_ZERO;
ts.tv_sec = t.as_secs() as _;
ts.tv_nsec = t.subsec_nanos() as _;
ts
}
},
..unsafe { std::mem::zeroed() }
};
timerfd_settime(timer_fd, TimerfdTimerFlags::empty(), &new_val)?;
// Set interest in timerfd.
self.modify(
timer_fd.as_fd(),
Event::readable(crate::NOTIFY_KEY),
PollMode::Oneshot,
)?;
}
#[cfg(not(target_os = "redox"))]
let timer_fd = &self.timer_fd;
#[cfg(target_os = "redox")]
let timer_fd: Option<core::convert::Infallible> = None;
// Timeout for epoll. In case of overflow, use no timeout.
let timeout = match (timer_fd, timeout) {
(_, Some(t)) if t == Duration::from_secs(0) => Some(Timespec::default()),
(None, Some(t)) => Timespec::try_from(t).ok(),
_ => None,
};
// Wait for I/O events.
epoll::wait(
&self.epoll_fd,
spare_capacity(&mut events.list),
timeout.as_ref(),
)?;
#[cfg(feature = "tracing")]
tracing::trace!(
epoll_fd = ?self.epoll_fd.as_raw_fd(),
res = ?events.list.len(),
"new events",
);
// Clear the notification (if received) and re-register interest in it.
self.notifier.clear();
self.modify(
self.notifier.as_fd(),
Event::readable(crate::NOTIFY_KEY),
PollMode::Oneshot,
)?;
Ok(())
}
/// Sends a notification to wake up the current or next `wait()` call.
pub fn notify(&self) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
notifier = ?self.notifier,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
self.notifier.notify();
Ok(())
}
}
impl AsRawFd for Poller {
fn as_raw_fd(&self) -> RawFd {
self.epoll_fd.as_raw_fd()
}
}
impl AsFd for Poller {
fn as_fd(&self) -> BorrowedFd<'_> {
self.epoll_fd.as_fd()
}
}
impl Drop for Poller {
fn drop(&mut self) {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"drop",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
notifier = ?self.notifier,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
#[cfg(not(target_os = "redox"))]
if let Some(timer_fd) = self.timer_fd.take() {
let _ = self.delete(timer_fd.as_fd());
}
let _ = self.delete(self.notifier.as_fd());
}
}
/// `timespec` value that equals zero.
#[cfg(not(target_os = "redox"))]
const TS_ZERO: Timespec = unsafe { std::mem::transmute([0u8; std::mem::size_of::<Timespec>()]) };
/// Get the EPOLL flags for the interest.
fn epoll_flags(interest: &Event, mode: PollMode) -> epoll::EventFlags {
let mut flags = match mode {
PollMode::Oneshot => epoll::EventFlags::ONESHOT,
PollMode::Level => epoll::EventFlags::empty(),
PollMode::Edge => epoll::EventFlags::ET,
PollMode::EdgeOneshot => epoll::EventFlags::ET | epoll::EventFlags::ONESHOT,
};
if interest.readable {
flags |= read_flags();
}
if interest.writable {
flags |= write_flags();
}
flags
}
/// Epoll flags for all possible readability events.
fn read_flags() -> epoll::EventFlags {
use epoll::EventFlags as Epoll;
Epoll::IN | Epoll::HUP | Epoll::ERR | Epoll::PRI
}
/// Epoll flags for all possible writability events.
fn write_flags() -> epoll::EventFlags {
use epoll::EventFlags as Epoll;
Epoll::OUT | Epoll::HUP | Epoll::ERR
}
/// A list of reported I/O events.
pub struct Events {
list: Vec<epoll::Event>,
}
unsafe impl Send for Events {}
impl Events {
/// Creates an empty list.
pub fn with_capacity(cap: usize) -> Events {
Events {
list: Vec::with_capacity(cap),
}
}
/// Iterates over I/O events.
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list.iter().map(|ev| {
let flags = ev.flags;
Event {
key: ev.data.u64() as usize,
readable: flags.intersects(read_flags()),
writable: flags.intersects(write_flags()),
extra: EventExtra { flags },
}
})
}
/// Clear the list.
pub fn clear(&mut self) {
self.list.clear();
}
/// Get the capacity of the list.
pub fn capacity(&self) -> usize {
self.list.capacity()
}
}
/// Extra information about this event.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EventExtra {
flags: epoll::EventFlags,
}
impl EventExtra {
/// Create an empty version of the data.
#[inline]
pub const fn empty() -> EventExtra {
EventExtra {
flags: epoll::EventFlags::empty(),
}
}
/// Add the interrupt flag to this event.
#[inline]
pub fn set_hup(&mut self, active: bool) {
self.flags.set(epoll::EventFlags::HUP, active);
}
/// Add the priority flag to this event.
#[inline]
pub fn set_pri(&mut self, active: bool) {
self.flags.set(epoll::EventFlags::PRI, active);
}
/// Tell if the interrupt flag is set.
#[inline]
pub fn is_hup(&self) -> bool {
self.flags.contains(epoll::EventFlags::HUP)
}
/// Tell if the priority flag is set.
#[inline]
pub fn is_pri(&self) -> bool {
self.flags.contains(epoll::EventFlags::PRI)
}
#[inline]
pub fn is_connect_failed(&self) -> Option<bool> {
Some(
self.flags.contains(epoll::EventFlags::ERR)
&& self.flags.contains(epoll::EventFlags::HUP),
)
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(epoll::EventFlags::ERR))
}
}
/// The notifier for Linux.
///
/// Certain container runtimes do not expose eventfd to the client, as it relies on the host and
/// can be used to "escape" the container under certain conditions. Gramine is the prime example,
/// see [here](gramine). In this case, fall back to using a pipe.
///
/// [gramine]: https://gramine.readthedocs.io/en/stable/manifest-syntax.html#allowing-eventfd
#[derive(Debug)]
enum Notifier {
/// The primary notifier, using eventfd.
#[cfg(not(target_os = "redox"))]
EventFd(OwnedFd),
/// The fallback notifier, using a pipe.
Pipe {
/// The read end of the pipe.
read_pipe: OwnedFd,
/// The write end of the pipe.
write_pipe: OwnedFd,
},
}
impl Notifier {
/// Create a new notifier.
fn new() -> io::Result<Self> {
// Skip eventfd for testing if necessary.
#[cfg(not(target_os = "redox"))]
{
if !cfg!(polling_test_epoll_pipe) {
// Try to create an eventfd.
match eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK) {
Ok(fd) => {
#[cfg(feature = "tracing")]
tracing::trace!("created eventfd for notifier");
return Ok(Notifier::EventFd(fd));
}
Err(_err) => {
#[cfg(feature = "tracing")]
tracing::warn!(
"eventfd() failed with error ({}), falling back to pipe",
_err
);
}
}
}
}
let (read, write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| {
let (read, write) = pipe()?;
fcntl_setfd(&read, fcntl_getfd(&read)? | FdFlags::CLOEXEC)?;
fcntl_setfd(&write, fcntl_getfd(&write)? | FdFlags::CLOEXEC)?;
io::Result::Ok((read, write))
})?;
fcntl_setfl(&read, fcntl_getfl(&read)? | OFlags::NONBLOCK)?;
Ok(Notifier::Pipe {
read_pipe: read,
write_pipe: write,
})
}
/// The file descriptor to register in the poller.
fn as_fd(&self) -> BorrowedFd<'_> {
match self {
#[cfg(not(target_os = "redox"))]
Notifier::EventFd(fd) => fd.as_fd(),
Notifier::Pipe {
read_pipe: read, ..
} => read.as_fd(),
}
}
/// Notify the poller.
fn notify(&self) {
match self {
#[cfg(not(target_os = "redox"))]
Self::EventFd(fd) => {
let buf: [u8; 8] = 1u64.to_ne_bytes();
let _ = write(fd, &buf);
}
Self::Pipe { write_pipe, .. } => {
write(write_pipe, &[0; 1]).ok();
}
}
}
/// Clear the notification.
fn clear(&self) {
match self {
#[cfg(not(target_os = "redox"))]
Self::EventFd(fd) => {
let mut buf = [0u8; 8];
let _ = read(fd, &mut buf);
}
Self::Pipe { read_pipe, .. } => while read(read_pipe, &mut [0u8; 1024]).is_ok() {},
}
}
}

665
vendor/polling/src/iocp/afd.rs vendored Normal file
View File

@@ -0,0 +1,665 @@
//! Safe wrapper around \Device\Afd
use super::port::{Completion, CompletionHandle};
use std::cell::UnsafeCell;
use std::fmt;
use std::io;
use std::marker::{PhantomData, PhantomPinned};
use std::mem::{self, size_of, transmute, MaybeUninit};
use std::ops;
use std::os::windows::prelude::{AsRawHandle, RawHandle, RawSocket};
use std::pin::Pin;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES;
use windows_sys::Wdk::Storage::FileSystem::FILE_OPEN;
use windows_sys::Win32::Foundation::{
CloseHandle, HANDLE, HMODULE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS,
UNICODE_STRING,
};
use windows_sys::Win32::Networking::WinSock::{
WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE_POLL, SOCKET_ERROR,
};
use windows_sys::Win32::Storage::FileSystem::{FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE};
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress};
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
#[derive(Default)]
#[repr(C)]
pub(super) struct AfdPollInfo {
/// The timeout for this poll.
timeout: i64,
/// The number of handles being polled.
handle_count: u32,
/// Whether or not this poll is exclusive for this handle.
exclusive: u32,
/// The handles to poll.
handles: [AfdPollHandleInfo; 1],
}
#[repr(C)]
struct AfdPollHandleInfo {
/// The handle to poll.
handle: HANDLE,
/// The events to poll for.
events: AfdPollMask,
/// The status of the poll.
status: NTSTATUS,
}
impl Default for AfdPollHandleInfo {
fn default() -> Self {
Self {
handle: ptr::null_mut(),
events: Default::default(),
status: Default::default(),
}
}
}
impl AfdPollInfo {
pub(super) fn handle_count(&self) -> u32 {
self.handle_count
}
pub(super) fn events(&self) -> AfdPollMask {
self.handles[0].events
}
}
#[derive(Default, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub(super) struct AfdPollMask(u32);
impl AfdPollMask {
pub(crate) const RECEIVE: AfdPollMask = AfdPollMask(0x001);
pub(crate) const RECEIVE_EXPEDITED: AfdPollMask = AfdPollMask(0x002);
pub(crate) const SEND: AfdPollMask = AfdPollMask(0x004);
pub(crate) const DISCONNECT: AfdPollMask = AfdPollMask(0x008);
pub(crate) const ABORT: AfdPollMask = AfdPollMask(0x010);
pub(crate) const LOCAL_CLOSE: AfdPollMask = AfdPollMask(0x020);
pub(crate) const ACCEPT: AfdPollMask = AfdPollMask(0x080);
pub(crate) const CONNECT_FAIL: AfdPollMask = AfdPollMask(0x100);
/// Creates an empty mask.
pub(crate) const fn empty() -> AfdPollMask {
AfdPollMask(0)
}
/// Checks if this mask contains the other mask.
pub(crate) fn intersects(self, other: AfdPollMask) -> bool {
(self.0 & other.0) != 0
}
/// Sets a flag.
pub(crate) fn set(&mut self, other: AfdPollMask, value: bool) {
if value {
*self |= other;
} else {
self.0 &= !other.0;
}
}
}
impl fmt::Debug for AfdPollMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const FLAGS: &[(&str, AfdPollMask)] = &[
("RECEIVE", AfdPollMask::RECEIVE),
("RECEIVE_EXPEDITED", AfdPollMask::RECEIVE_EXPEDITED),
("SEND", AfdPollMask::SEND),
("DISCONNECT", AfdPollMask::DISCONNECT),
("ABORT", AfdPollMask::ABORT),
("LOCAL_CLOSE", AfdPollMask::LOCAL_CLOSE),
("ACCEPT", AfdPollMask::ACCEPT),
("CONNECT_FAIL", AfdPollMask::CONNECT_FAIL),
];
let mut first = true;
for (name, value) in FLAGS {
if self.intersects(*value) {
if !first {
write!(f, " | ")?;
}
first = false;
write!(f, "{name}")?;
}
}
Ok(())
}
}
impl ops::BitOr for AfdPollMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
AfdPollMask(self.0 | rhs.0)
}
}
impl ops::BitOrAssign for AfdPollMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl ops::BitAnd for AfdPollMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
AfdPollMask(self.0 & rhs.0)
}
}
impl ops::BitAndAssign for AfdPollMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
pub(super) trait HasAfdInfo {
fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell<AfdPollInfo>>;
}
macro_rules! define_ntdll_import {
(
$(
$(#[$attr:meta])*
fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty;
)*
) => {
/// Imported functions from ntdll.dll.
#[allow(non_snake_case)]
pub(super) struct NtdllImports {
$(
$(#[$attr])*
$name: unsafe extern "system" fn($($arg_ty),*) -> $ret,
)*
}
#[allow(non_snake_case)]
impl NtdllImports {
unsafe fn load(ntdll: HMODULE) -> io::Result<Self> {
$(
#[allow(clippy::missing_transmute_annotations)]
let $name = {
const NAME: &str = concat!(stringify!($name), "\0");
let addr = GetProcAddress(ntdll, NAME.as_ptr() as *const _);
let addr = match addr {
Some(addr) => addr,
None => {
#[cfg(feature = "tracing")]
tracing::error!("Failed to load ntdll function {}", NAME);
return Err(io::Error::last_os_error());
},
};
transmute::<_, unsafe extern "system" fn($($arg_ty),*) -> $ret>(addr)
};
)*
Ok(Self {
$(
$name,
)*
})
}
$(
$(#[$attr])*
unsafe fn $name(&self, $($arg: $arg_ty),*) -> $ret {
(self.$name)($($arg),*)
}
)*
}
};
}
define_ntdll_import! {
/// Cancels an ongoing I/O operation.
fn NtCancelIoFileEx(
FileHandle: HANDLE,
IoRequestToCancel: *mut IO_STATUS_BLOCK,
IoStatusBlock: *mut IO_STATUS_BLOCK
) -> NTSTATUS;
/// Opens or creates a file handle.
#[allow(clippy::too_many_arguments)]
fn NtCreateFile(
FileHandle: *mut HANDLE,
DesiredAccess: u32,
ObjectAttributes: *mut OBJECT_ATTRIBUTES,
IoStatusBlock: *mut IO_STATUS_BLOCK,
AllocationSize: *mut i64,
FileAttributes: u32,
ShareAccess: u32,
CreateDisposition: u32,
CreateOptions: u32,
EaBuffer: *mut (),
EaLength: u32
) -> NTSTATUS;
/// Runs an I/O control on a file handle.
///
/// Practically equivalent to `ioctl`.
#[allow(clippy::too_many_arguments)]
fn NtDeviceIoControlFile(
FileHandle: HANDLE,
Event: HANDLE,
ApcRoutine: *mut (),
ApcContext: *mut (),
IoStatusBlock: *mut IO_STATUS_BLOCK,
IoControlCode: u32,
InputBuffer: *mut (),
InputBufferLength: u32,
OutputBuffer: *mut (),
OutputBufferLength: u32
) -> NTSTATUS;
/// Converts `NTSTATUS` to a DOS error code.
fn RtlNtStatusToDosError(
Status: NTSTATUS
) -> u32;
}
impl NtdllImports {
fn get() -> io::Result<&'static Self> {
macro_rules! s {
($e:expr) => {{
$e as u16
}};
}
// ntdll.dll
static NTDLL_NAME: &[u16] = &[
s!('n'),
s!('t'),
s!('d'),
s!('l'),
s!('l'),
s!('.'),
s!('d'),
s!('l'),
s!('l'),
s!('\0'),
];
static NTDLL_IMPORTS: OnceLock<io::Result<NtdllImports>> = OnceLock::new();
NTDLL_IMPORTS
.get_or_init(|| unsafe {
let ntdll = GetModuleHandleW(NTDLL_NAME.as_ptr() as *const _);
if ntdll.is_null() {
#[cfg(feature = "tracing")]
tracing::error!("Failed to load ntdll.dll");
return Err(io::Error::last_os_error());
}
NtdllImports::load(ntdll)
})
.as_ref()
.map_err(|e| io::Error::from(e.kind()))
}
pub(super) fn force_load() -> io::Result<()> {
Self::get()?;
Ok(())
}
}
/// The handle to the AFD device.
pub(super) struct Afd<T> {
/// The handle to the AFD device.
handle: HANDLE,
/// We own `T`.
_marker: PhantomData<T>,
}
impl<T> fmt::Debug for Afd<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct WriteAsHex(HANDLE);
impl fmt::Debug for WriteAsHex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:010x}", self.0 as usize)
}
}
f.debug_struct("Afd")
.field("handle", &WriteAsHex(self.handle))
.finish()
}
}
impl<T> Drop for Afd<T> {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
impl<T> AsRawHandle for Afd<T> {
fn as_raw_handle(&self) -> RawHandle {
self.handle as _
}
}
impl<T: CompletionHandle> Afd<T>
where
T::Completion: AsIoStatusBlock + HasAfdInfo,
{
/// Create a new AFD device.
pub(super) fn new() -> io::Result<Self> {
macro_rules! s {
($e:expr) => {
($e) as u16
};
}
/// \Device\Afd\Smol
const AFD_NAME: &[u16] = &[
s!('\\'),
s!('D'),
s!('e'),
s!('v'),
s!('i'),
s!('c'),
s!('e'),
s!('\\'),
s!('A'),
s!('f'),
s!('d'),
s!('\\'),
s!('S'),
s!('m'),
s!('o'),
s!('l'),
s!('\0'),
];
// Set up device attributes.
let mut device_name = UNICODE_STRING {
Length: mem::size_of_val(AFD_NAME) as u16,
MaximumLength: mem::size_of_val(AFD_NAME) as u16,
Buffer: AFD_NAME.as_ptr() as *mut _,
};
let mut device_attributes = OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: ptr::null_mut(),
ObjectName: &mut device_name,
Attributes: 0,
SecurityDescriptor: ptr::null_mut(),
SecurityQualityOfService: ptr::null_mut(),
};
let mut handle = MaybeUninit::<HANDLE>::uninit();
let mut iosb = MaybeUninit::<IO_STATUS_BLOCK>::zeroed();
let ntdll = NtdllImports::get()?;
let result = unsafe {
ntdll.NtCreateFile(
handle.as_mut_ptr(),
SYNCHRONIZE,
&mut device_attributes,
iosb.as_mut_ptr(),
ptr::null_mut(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0,
ptr::null_mut(),
0,
)
};
if result != STATUS_SUCCESS {
let real_code = unsafe { ntdll.RtlNtStatusToDosError(result) };
return Err(io::Error::from_raw_os_error(real_code as i32));
}
let handle = unsafe { handle.assume_init() };
Ok(Self {
handle,
_marker: PhantomData,
})
}
/// Begin polling with the provided handle.
pub(super) fn poll(
&self,
packet: T,
base_socket: RawSocket,
afd_events: AfdPollMask,
) -> io::Result<()> {
const IOCTL_AFD_POLL: u32 = 0x00012024;
// Lock the packet.
if !packet.get().try_lock() {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
"packet is already in use",
));
}
// Set up the AFD poll info.
let poll_info = unsafe {
let poll_info = Pin::into_inner_unchecked(packet.get().afd_info()).get();
// Initialize the AFD poll info.
(*poll_info).exclusive = false.into();
(*poll_info).handle_count = 1;
(*poll_info).timeout = i64::MAX;
(*poll_info).handles[0].handle = base_socket as HANDLE;
(*poll_info).handles[0].status = 0;
(*poll_info).handles[0].events = afd_events;
poll_info
};
let iosb = T::into_ptr(packet).cast::<IO_STATUS_BLOCK>();
// Set Status to pending
unsafe {
(*iosb).Anonymous.Status = STATUS_PENDING;
}
let ntdll = NtdllImports::get()?;
let result = unsafe {
ntdll.NtDeviceIoControlFile(
self.handle,
ptr::null_mut(),
ptr::null_mut(),
iosb.cast(),
iosb.cast(),
IOCTL_AFD_POLL,
poll_info.cast(),
size_of::<AfdPollInfo>() as u32,
poll_info.cast(),
size_of::<AfdPollInfo>() as u32,
)
};
match result {
STATUS_SUCCESS => Ok(()),
STATUS_PENDING => Err(io::ErrorKind::WouldBlock.into()),
status => {
let real_code = unsafe { ntdll.RtlNtStatusToDosError(status) };
Err(io::Error::from_raw_os_error(real_code as i32))
}
}
}
/// Cancel an ongoing poll operation.
///
/// # Safety
///
/// The poll operation must currently be in progress for this AFD.
pub(super) unsafe fn cancel(&self, packet: &T) -> io::Result<()> {
let ntdll = NtdllImports::get()?;
let result = {
// First, check if the packet is still in use.
let iosb = packet.as_ptr().cast::<IO_STATUS_BLOCK>();
if (*iosb).Anonymous.Status != STATUS_PENDING {
return Ok(());
}
// Cancel the packet.
let mut cancel_iosb = MaybeUninit::<IO_STATUS_BLOCK>::zeroed();
ntdll.NtCancelIoFileEx(self.handle, iosb, cancel_iosb.as_mut_ptr())
};
if result == STATUS_SUCCESS || result == STATUS_NOT_FOUND {
Ok(())
} else {
let real_code = ntdll.RtlNtStatusToDosError(result);
Err(io::Error::from_raw_os_error(real_code as i32))
}
}
}
pin_project_lite::pin_project! {
/// An I/O status block paired with some auxiliary data.
#[repr(C)]
pub(super) struct IoStatusBlock<T> {
// The I/O status block.
iosb: UnsafeCell<IO_STATUS_BLOCK>,
// Whether or not the block is in use.
in_use: AtomicBool,
// The auxiliary data.
#[pin]
data: T,
// This block is not allowed to move.
#[pin]
_marker: PhantomPinned,
}
}
impl<T: fmt::Debug> fmt::Debug for IoStatusBlock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IoStatusBlock")
.field("iosb", &"..")
.field("in_use", &self.in_use)
.field("data", &self.data)
.finish()
}
}
unsafe impl<T: Send> Send for IoStatusBlock<T> {}
unsafe impl<T: Sync> Sync for IoStatusBlock<T> {}
impl<T> From<T> for IoStatusBlock<T> {
fn from(data: T) -> Self {
Self {
iosb: UnsafeCell::new(unsafe { std::mem::zeroed() }),
in_use: AtomicBool::new(false),
data,
_marker: PhantomPinned,
}
}
}
impl<T> IoStatusBlock<T> {
pub(super) fn iosb(self: Pin<&Self>) -> &UnsafeCell<IO_STATUS_BLOCK> {
self.project_ref().iosb
}
pub(super) fn data(self: Pin<&Self>) -> Pin<&T> {
self.project_ref().data
}
}
impl<T: HasAfdInfo> HasAfdInfo for IoStatusBlock<T> {
fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell<AfdPollInfo>> {
self.project_ref().data.afd_info()
}
}
/// Can be transmuted to an I/O status block.
///
/// # Safety
///
/// A pointer to `T` must be able to be converted to a pointer to `IO_STATUS_BLOCK`
/// without any issues.
pub(super) unsafe trait AsIoStatusBlock {}
unsafe impl<T> AsIoStatusBlock for IoStatusBlock<T> {}
unsafe impl<T> Completion for IoStatusBlock<T> {
fn try_lock(self: Pin<&Self>) -> bool {
!self.in_use.swap(true, Ordering::SeqCst)
}
unsafe fn unlock(self: Pin<&Self>) {
self.in_use.store(false, Ordering::SeqCst);
}
}
/// Get the base socket associated with a socket.
pub(super) fn base_socket(sock: RawSocket) -> io::Result<RawSocket> {
// First, try the SIO_BASE_HANDLE ioctl.
let result = unsafe { try_socket_ioctl(sock, SIO_BASE_HANDLE) };
match result {
Ok(sock) => return Ok(sock),
Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Err(e),
Err(_) => {}
}
// Some poorly coded LSPs may not handle SIO_BASE_HANDLE properly, but in some cases may
// handle SIO_BSP_HANDLE_POLL better. Try that.
let result = unsafe { try_socket_ioctl(sock, SIO_BSP_HANDLE_POLL)? };
if result == sock {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
// Try `SIO_BASE_HANDLE` again, in case the LSP fixed itself.
unsafe { try_socket_ioctl(result, SIO_BASE_HANDLE) }
}
/// Run an IOCTL on a socket and return a socket.
///
/// # Safety
///
/// The `ioctl` parameter must be a valid I/O control that returns a valid socket.
unsafe fn try_socket_ioctl(sock: RawSocket, ioctl: u32) -> io::Result<RawSocket> {
let mut out = MaybeUninit::<RawSocket>::uninit();
let mut bytes = 0u32;
let result = WSAIoctl(
sock as _,
ioctl,
ptr::null_mut(),
0,
out.as_mut_ptr().cast(),
size_of::<RawSocket>() as u32,
&mut bytes,
ptr::null_mut(),
None,
);
if result == SOCKET_ERROR {
return Err(io::Error::last_os_error());
}
Ok(out.assume_init())
}

1412
vendor/polling/src/iocp/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

299
vendor/polling/src/iocp/port.rs vendored Normal file
View File

@@ -0,0 +1,299 @@
//! A safe wrapper around the Windows I/O API.
use super::dur2timeout;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::pin::Pin;
use std::ptr;
use std::sync::Arc;
use std::time::Duration;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::SetFileCompletionNotificationModes;
use windows_sys::Win32::System::Threading::INFINITE;
use windows_sys::Win32::System::WindowsProgramming::FILE_SKIP_SET_EVENT_ON_HANDLE;
use windows_sys::Win32::System::IO::{
CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, OVERLAPPED,
OVERLAPPED_ENTRY,
};
/// A completion block which can be used with I/O completion ports.
///
/// # Safety
///
/// This must be a valid completion block.
pub(super) unsafe trait Completion {
/// Signal to the completion block that we are about to start an operation.
fn try_lock(self: Pin<&Self>) -> bool;
/// Unlock the completion block.
unsafe fn unlock(self: Pin<&Self>);
}
/// The pointer to a completion block.
///
/// # Safety
///
/// This must be a valid completion block.
pub(super) unsafe trait CompletionHandle: Deref + Sized {
/// Type of the completion block.
type Completion: Completion;
/// Get a pointer to the completion block.
///
/// The pointer is pinned since the underlying object should not be moved
/// after creation. This prevents it from being invalidated while it's
/// used in an overlapped operation.
fn get(&self) -> Pin<&Self::Completion>;
/// Convert this block into a pointer that can be passed as `*mut OVERLAPPED`.
fn into_ptr(this: Self) -> *mut OVERLAPPED;
/// Convert a pointer that was passed as `*mut OVERLAPPED` into a pointer to this block.
///
/// # Safety
///
/// This must be a valid pointer to a completion block.
unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self;
/// Convert to a pointer without losing ownership.
fn as_ptr(&self) -> *mut OVERLAPPED;
}
unsafe impl<T: Completion> CompletionHandle for Pin<&T> {
type Completion = T;
fn get(&self) -> Pin<&Self::Completion> {
*self
}
fn into_ptr(this: Self) -> *mut OVERLAPPED {
unsafe { Pin::into_inner_unchecked(this) as *const T as *mut OVERLAPPED }
}
unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self {
Pin::new_unchecked(&*(ptr as *const T))
}
fn as_ptr(&self) -> *mut OVERLAPPED {
self.get_ref() as *const T as *mut OVERLAPPED
}
}
unsafe impl<T: Completion> CompletionHandle for Pin<Arc<T>> {
type Completion = T;
fn get(&self) -> Pin<&Self::Completion> {
self.as_ref()
}
fn into_ptr(this: Self) -> *mut OVERLAPPED {
unsafe { Arc::into_raw(Pin::into_inner_unchecked(this)) as *const T as *mut OVERLAPPED }
}
unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self {
Pin::new_unchecked(Arc::from_raw(ptr as *const T))
}
fn as_ptr(&self) -> *mut OVERLAPPED {
self.as_ref().get_ref() as *const T as *mut OVERLAPPED
}
}
/// A handle to the I/O completion port.
pub(super) struct IoCompletionPort<T> {
/// The underlying handle.
handle: HANDLE,
/// We own the status block.
_marker: PhantomData<T>,
}
impl<T> Drop for IoCompletionPort<T> {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
impl<T> AsRawHandle for IoCompletionPort<T> {
fn as_raw_handle(&self) -> RawHandle {
self.handle as _
}
}
impl<T> fmt::Debug for IoCompletionPort<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct WriteAsHex(HANDLE);
impl fmt::Debug for WriteAsHex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:010x}", self.0 as usize)
}
}
f.debug_struct("IoCompletionPort")
.field("handle", &WriteAsHex(self.handle))
.finish()
}
}
impl<T: CompletionHandle> IoCompletionPort<T> {
/// Create a new I/O completion port.
pub(super) fn new(threads: usize) -> io::Result<Self> {
let handle = unsafe {
CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
ptr::null_mut(),
0,
threads.try_into().expect("too many threads"),
)
};
if handle.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(Self {
handle,
_marker: PhantomData,
})
}
}
/// Register a handle with this I/O completion port.
pub(super) fn register(
&self,
handle: &impl AsRawHandle, // TODO change to AsHandle
skip_set_event_on_handle: bool,
) -> io::Result<()> {
let handle = handle.as_raw_handle();
let result =
unsafe { CreateIoCompletionPort(handle as _, self.handle, handle as usize, 0) };
if result.is_null() {
return Err(io::Error::last_os_error());
}
if skip_set_event_on_handle {
// Set the skip event on handle.
let result = unsafe {
SetFileCompletionNotificationModes(handle as _, FILE_SKIP_SET_EVENT_ON_HANDLE as _)
};
if result == 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
/// Post a completion packet to this port.
pub(super) fn post(&self, bytes_transferred: usize, id: usize, packet: T) -> io::Result<()> {
let result = unsafe {
PostQueuedCompletionStatus(
self.handle,
bytes_transferred
.try_into()
.expect("too many bytes transferred"),
id,
T::into_ptr(packet),
)
};
if result == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
/// Wait for completion packets to arrive.
pub(super) fn wait(
&self,
packets: &mut Vec<OverlappedEntry<T>>,
timeout: Option<Duration>,
) -> io::Result<usize> {
// Drop the current packets.
packets.clear();
let mut count = MaybeUninit::<u32>::uninit();
let timeout = timeout.map_or(INFINITE, dur2timeout);
let result = unsafe {
GetQueuedCompletionStatusEx(
self.handle,
packets.as_mut_ptr() as _,
packets.capacity().try_into().expect("too many packets"),
count.as_mut_ptr(),
timeout,
0,
)
};
if result == 0 {
let io_error = io::Error::last_os_error();
if io_error.kind() == io::ErrorKind::TimedOut {
Ok(0)
} else {
Err(io_error)
}
} else {
let count = unsafe { count.assume_init() };
unsafe {
packets.set_len(count as _);
}
Ok(count as _)
}
}
}
/// An `OVERLAPPED_ENTRY` resulting from an I/O completion port.
#[repr(transparent)]
pub(super) struct OverlappedEntry<T: CompletionHandle> {
/// The underlying entry.
entry: OVERLAPPED_ENTRY,
/// We own the status block.
_marker: PhantomData<T>,
}
impl<T: CompletionHandle> fmt::Debug for OverlappedEntry<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("OverlappedEntry { .. }")
}
}
impl<T: CompletionHandle> OverlappedEntry<T> {
/// Convert into the completion packet.
pub(super) fn into_packet(self) -> T {
let packet = unsafe { self.packet() };
std::mem::forget(self);
packet
}
/// Get the packet reference that this entry refers to.
///
/// # Safety
///
/// This function should only be called once, since it moves
/// out the `T` from the `OVERLAPPED_ENTRY`.
unsafe fn packet(&self) -> T {
let packet = T::from_ptr(self.entry.lpOverlapped);
packet.get().unlock();
packet
}
}
impl<T: CompletionHandle> Drop for OverlappedEntry<T> {
fn drop(&mut self) {
drop(unsafe { self.packet() });
}
}

596
vendor/polling/src/kqueue.rs vendored Normal file
View File

@@ -0,0 +1,596 @@
//! Bindings to kqueue (macOS, iOS, tvOS, watchOS, visionOS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD).
use std::collections::HashSet;
use std::io;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
use std::sync::RwLock;
use std::time::Instant;
use rustix::buffer::spare_capacity;
use rustix::event::{kqueue, Timespec};
use rustix::io::{fcntl_setfd, Errno, FdFlags};
use crate::{Event, PollMode};
/// Interface to kqueue.
#[derive(Debug)]
pub struct Poller {
/// File descriptor for the kqueue instance.
kqueue_fd: OwnedFd,
/// List of sources currently registered in this poller.
///
/// This is used to make sure the same source is not registered twice.
sources: RwLock<HashSet<SourceId>>,
/// Notification pipe for waking up the poller.
///
/// On platforms that support `EVFILT_USER`, this uses that to wake up the poller. Otherwise, it
/// uses a pipe.
notify: notify::Notify,
}
/// Identifier for a source.
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SourceId {
/// Registered file descriptor.
Fd(RawFd),
/// Signal.
Signal(std::os::raw::c_int),
/// Process ID.
Pid(rustix::process::Pid),
/// Timer ID.
Timer(usize),
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
// Create a kqueue instance.
let kqueue_fd = kqueue::kqueue()?;
fcntl_setfd(&kqueue_fd, FdFlags::CLOEXEC)?;
let poller = Poller {
kqueue_fd,
sources: RwLock::new(HashSet::new()),
notify: notify::Notify::new()?,
};
// Register the notification pipe.
poller.notify.register(&poller)?;
#[cfg(feature = "tracing")]
tracing::trace!(
kqueue_fd = ?poller.kqueue_fd.as_raw_fd(),
"new"
);
Ok(poller)
}
/// Whether this poller supports level-triggered events.
pub fn supports_level(&self) -> bool {
true
}
/// Whether this poller supports edge-triggered events.
pub fn supports_edge(&self) -> bool {
true
}
/// Adds a new file descriptor.
///
/// # Safety
///
/// The file descriptor must be valid and it must last until it is deleted.
pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
self.add_source(SourceId::Fd(fd))?;
// File descriptors don't need to be added explicitly, so just modify the interest.
self.modify(BorrowedFd::borrow_raw(fd), ev, mode)
}
/// Modifies an existing file descriptor.
pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = if !self.notify.has_fd(fd) {
let span = tracing::trace_span!(
"add",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
?fd,
?ev,
);
Some(span)
} else {
None
};
#[cfg(feature = "tracing")]
let _enter = span.as_ref().map(|s| s.enter());
self.has_source(SourceId::Fd(fd.as_raw_fd()))?;
let mode_flags = mode_to_flags(mode);
let read_flags = if ev.readable {
kqueue::EventFlags::ADD | mode_flags
} else {
kqueue::EventFlags::DELETE
};
let write_flags = if ev.writable {
kqueue::EventFlags::ADD | mode_flags
} else {
kqueue::EventFlags::DELETE
};
// A list of changes for kqueue.
let changelist = [
kqueue::Event::new(
kqueue::EventFilter::Read(fd.as_raw_fd()),
read_flags | kqueue::EventFlags::RECEIPT,
ev.key as _,
),
kqueue::Event::new(
kqueue::EventFilter::Write(fd.as_raw_fd()),
write_flags | kqueue::EventFlags::RECEIPT,
ev.key as _,
),
];
// Apply changes.
self.submit_changes(changelist)
}
/// Submit one or more changes to the kernel queue and check to see if they succeeded.
pub(crate) fn submit_changes<A>(&self, changelist: A) -> io::Result<()>
where
A: Copy + AsRef<[kqueue::Event]> + AsMut<[kqueue::Event]>,
{
let mut eventlist = Vec::with_capacity(changelist.as_ref().len());
// Apply changes.
{
let changelist = changelist.as_ref();
unsafe {
kqueue::kevent_timespec(
&self.kqueue_fd,
changelist,
spare_capacity(&mut eventlist),
None,
)?;
}
}
// Check for errors.
for &ev in &eventlist {
let data = ev.data();
// Explanation for ignoring EPIPE: https://github.com/tokio-rs/mio/issues/582
if (ev.flags().contains(kqueue::EventFlags::ERROR))
&& data != 0
&& data != Errno::NOENT.raw_os_error() as _
&& data != Errno::PIPE.raw_os_error() as _
{
return Err(io::Error::from_raw_os_error(data as _));
}
}
Ok(())
}
/// Add a source to the sources set.
#[inline]
pub(crate) fn add_source(&self, source: SourceId) -> io::Result<()> {
if self
.sources
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(source)
{
Ok(())
} else {
Err(io::Error::from(io::ErrorKind::AlreadyExists))
}
}
/// Tell if a source is currently inside the set.
#[inline]
pub(crate) fn has_source(&self, source: SourceId) -> io::Result<()> {
if self
.sources
.read()
.unwrap_or_else(|e| e.into_inner())
.contains(&source)
{
Ok(())
} else {
Err(io::Error::from(io::ErrorKind::NotFound))
}
}
/// Remove a source from the sources set.
#[inline]
pub(crate) fn remove_source(&self, source: SourceId) -> io::Result<()> {
if self
.sources
.write()
.unwrap_or_else(|e| e.into_inner())
.remove(&source)
{
Ok(())
} else {
Err(io::Error::from(io::ErrorKind::NotFound))
}
}
/// Deletes a file descriptor.
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
// Simply delete interest in the file descriptor.
self.modify(fd, Event::none(0), PollMode::Oneshot)?;
self.remove_source(SourceId::Fd(fd.as_raw_fd()))
}
/// Waits for I/O events with an optional deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Timeout for kevent. In case of overflow, use no timeout.
let timeout = match timeout {
Some(t) => Timespec::try_from(t).ok(),
None => None,
};
// Wait for I/O events.
let changelist = [];
let _res = unsafe {
kqueue::kevent_timespec(
&self.kqueue_fd,
&changelist,
spare_capacity(&mut events.list),
timeout.as_ref(),
)?
};
#[cfg(feature = "tracing")]
tracing::trace!(
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
res = ?_res,
"new events",
);
// Clear the notification (if received) and re-register interest in it.
self.notify.reregister(self)?;
Ok(())
}
/// Sends a notification to wake up the current or next `wait()` call.
pub fn notify(&self) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
self.notify.notify(self).ok();
Ok(())
}
}
impl AsRawFd for Poller {
fn as_raw_fd(&self) -> RawFd {
self.kqueue_fd.as_raw_fd()
}
}
impl AsFd for Poller {
fn as_fd(&self) -> BorrowedFd<'_> {
self.kqueue_fd.as_fd()
}
}
impl Drop for Poller {
fn drop(&mut self) {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"drop",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let _ = self.notify.deregister(self);
}
}
/// A list of reported I/O events.
pub struct Events {
list: Vec<kqueue::Event>,
}
unsafe impl Send for Events {}
impl Events {
/// Creates an empty list.
pub fn with_capacity(cap: usize) -> Events {
Events {
list: Vec::with_capacity(cap),
}
}
/// Iterates over I/O events.
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
// On some platforms, closing the read end of a pipe wakes up writers, but the
// event is reported as EVFILT_READ with the EV_EOF flag.
//
// https://github.com/golang/go/commit/23aad448b1e3f7c3b4ba2af90120bde91ac865b4
self.list.iter().map(|ev| Event {
key: ev.udata() as usize,
readable: matches!(
ev.filter(),
kqueue::EventFilter::Read(..)
| kqueue::EventFilter::Vnode { .. }
| kqueue::EventFilter::Proc { .. }
| kqueue::EventFilter::Signal { .. }
| kqueue::EventFilter::Timer { .. }
),
writable: matches!(ev.filter(), kqueue::EventFilter::Write(..))
|| (matches!(ev.filter(), kqueue::EventFilter::Read(..))
&& (ev.flags().intersects(kqueue::EventFlags::EOF))),
extra: EventExtra,
})
}
/// Clears the list.
pub fn clear(&mut self) {
self.list.clear();
}
/// Get the capacity of the list.
pub fn capacity(&self) -> usize {
self.list.capacity()
}
}
/// Extra information associated with an event.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct EventExtra;
impl EventExtra {
/// Create a new, empty version of this struct.
#[inline]
pub const fn empty() -> EventExtra {
EventExtra
}
/// Set the interrupt flag.
#[inline]
pub fn set_hup(&mut self, _value: bool) {
// No-op.
}
/// Set the priority flag.
#[inline]
pub fn set_pri(&mut self, _value: bool) {
// No-op.
}
/// Is the interrupt flag set?
#[inline]
pub fn is_hup(&self) -> bool {
false
}
/// Is the priority flag set?
#[inline]
pub fn is_pri(&self) -> bool {
false
}
#[inline]
pub fn is_connect_failed(&self) -> Option<bool> {
None
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
None
}
}
pub(crate) fn mode_to_flags(mode: PollMode) -> kqueue::EventFlags {
use kqueue::EventFlags as EV;
match mode {
PollMode::Oneshot => EV::ONESHOT,
PollMode::Level => EV::empty(),
PollMode::Edge => EV::CLEAR,
PollMode::EdgeOneshot => EV::ONESHOT | EV::CLEAR,
}
}
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_vendor = "apple",
))]
mod notify {
use super::Poller;
use rustix::event::kqueue;
use std::io;
#[cfg(feature = "tracing")]
use std::os::unix::io::BorrowedFd;
/// A notification pipe.
///
/// This implementation uses `EVFILT_USER` to avoid allocating a pipe.
#[derive(Debug)]
pub(super) struct Notify;
impl Notify {
/// Creates a new notification pipe.
pub(super) fn new() -> io::Result<Self> {
Ok(Self)
}
/// Registers this notification pipe in the `Poller`.
pub(super) fn register(&self, poller: &Poller) -> io::Result<()> {
// Register an EVFILT_USER event.
poller.submit_changes([kqueue::Event::new(
kqueue::EventFilter::User {
ident: 0,
flags: kqueue::UserFlags::empty(),
user_flags: kqueue::UserDefinedFlags::new(0),
},
kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT | kqueue::EventFlags::CLEAR,
crate::NOTIFY_KEY as _,
)])
}
/// Reregister this notification pipe in the `Poller`.
pub(super) fn reregister(&self, _poller: &Poller) -> io::Result<()> {
// We don't need to do anything, it's already registered as EV_CLEAR.
Ok(())
}
/// Notifies the `Poller`.
pub(super) fn notify(&self, poller: &Poller) -> io::Result<()> {
// Trigger the EVFILT_USER event.
poller.submit_changes([kqueue::Event::new(
kqueue::EventFilter::User {
ident: 0,
flags: kqueue::UserFlags::TRIGGER,
user_flags: kqueue::UserDefinedFlags::new(0),
},
kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT,
crate::NOTIFY_KEY as _,
)])?;
Ok(())
}
/// Deregisters this notification pipe from the `Poller`.
pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> {
// Deregister the EVFILT_USER event.
poller.submit_changes([kqueue::Event::new(
kqueue::EventFilter::User {
ident: 0,
flags: kqueue::UserFlags::empty(),
user_flags: kqueue::UserDefinedFlags::new(0),
},
kqueue::EventFlags::DELETE | kqueue::EventFlags::RECEIPT,
crate::NOTIFY_KEY as _,
)])
}
/// Whether this raw file descriptor is associated with this pipe.
#[cfg(feature = "tracing")]
pub(super) fn has_fd(&self, _fd: BorrowedFd<'_>) -> bool {
false
}
}
}
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_vendor = "apple",
)))]
mod notify {
use super::Poller;
use crate::{Event, PollMode, NOTIFY_KEY};
use std::io::{self, prelude::*};
#[cfg(feature = "tracing")]
use std::os::unix::io::BorrowedFd;
use std::os::unix::{
io::{AsFd, AsRawFd},
net::UnixStream,
};
/// A notification pipe.
///
/// This implementation uses a pipe to send notifications.
#[derive(Debug)]
pub(super) struct Notify {
/// The read end of the pipe.
read_stream: UnixStream,
/// The write end of the pipe.
write_stream: UnixStream,
}
impl Notify {
/// Creates a new notification pipe.
pub(super) fn new() -> io::Result<Self> {
let (read_stream, write_stream) = UnixStream::pair()?;
read_stream.set_nonblocking(true)?;
write_stream.set_nonblocking(true)?;
Ok(Self {
read_stream,
write_stream,
})
}
/// Registers this notification pipe in the `Poller`.
pub(super) fn register(&self, poller: &Poller) -> io::Result<()> {
// Register the read end of this pipe.
unsafe {
poller.add(
self.read_stream.as_raw_fd(),
Event::readable(NOTIFY_KEY),
PollMode::Oneshot,
)
}
}
/// Reregister this notification pipe in the `Poller`.
pub(super) fn reregister(&self, poller: &Poller) -> io::Result<()> {
// Clear out the notification.
while (&self.read_stream).read(&mut [0; 64]).is_ok() {}
// Reregister the read end of this pipe.
poller.modify(
self.read_stream.as_fd(),
Event::readable(NOTIFY_KEY),
PollMode::Oneshot,
)
}
/// Notifies the `Poller`.
#[allow(clippy::unused_io_amount)]
pub(super) fn notify(&self, _poller: &Poller) -> io::Result<()> {
// Write to the write end of the pipe
(&self.write_stream).write(&[1])?;
Ok(())
}
/// Deregisters this notification pipe from the `Poller`.
pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> {
// Deregister the read end of the pipe.
poller.delete(self.read_stream.as_fd())
}
/// Whether this raw file descriptor is associated with this pipe.
#[cfg(feature = "tracing")]
pub(super) fn has_fd(&self, fd: BorrowedFd<'_>) -> bool {
self.read_stream.as_raw_fd() == fd.as_raw_fd()
}
}
}

1131
vendor/polling/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

24
vendor/polling/src/os.rs vendored Normal file
View File

@@ -0,0 +1,24 @@
//! Platform-specific functionality.
#[cfg(all(
any(
target_vendor = "apple",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
),
not(polling_test_poll_backend),
))]
pub mod kqueue;
#[cfg(target_os = "windows")]
pub mod iocp;
mod __private {
#[doc(hidden)]
#[allow(dead_code)]
pub trait PollerSealed {}
impl PollerSealed for crate::Poller {}
}

253
vendor/polling/src/os/iocp.rs vendored Normal file
View File

@@ -0,0 +1,253 @@
//! Functionality that is only available for IOCP-based platforms.
pub use crate::sys::CompletionPacket;
use super::__private::PollerSealed;
use crate::{Event, PollMode, Poller};
use std::io;
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::os::windows::prelude::{AsHandle, BorrowedHandle};
/// Extension trait for the [`Poller`] type that provides functionality specific to IOCP-based
/// platforms.
///
/// [`Poller`]: crate::Poller
pub trait PollerIocpExt: PollerSealed {
/// Post a new [`Event`] to the poller.
///
/// # Examples
///
/// ```rust
/// use polling::{Poller, Event, Events};
/// use polling::os::iocp::{CompletionPacket, PollerIocpExt};
///
/// use std::thread;
/// use std::sync::Arc;
/// use std::time::Duration;
///
/// # fn main() -> std::io::Result<()> {
/// // Spawn a thread to wake us up after 100ms.
/// let poller = Arc::new(Poller::new()?);
/// thread::spawn({
/// let poller = poller.clone();
/// move || {
/// let packet = CompletionPacket::new(Event::readable(0));
/// thread::sleep(Duration::from_millis(100));
/// poller.post(packet).unwrap();
/// }
/// });
///
/// // Wait for the event.
/// let mut events = Events::new();
/// poller.wait(&mut events, None)?;
///
/// assert_eq!(events.len(), 1);
/// # Ok(()) }
/// ```
fn post(&self, packet: CompletionPacket) -> io::Result<()>;
/// Add a waitable handle to this poller.
///
/// Some handles in Windows are "waitable", which means that they emit a "readiness" signal
/// after some event occurs. This function can be used to wait for such events to occur
/// on a handle. This function can be used in addition to regular socket polling.
///
/// Waitable objects include the following:
///
/// - Console inputs
/// - Waitable events
/// - Mutexes
/// - Processes
/// - Semaphores
/// - Threads
/// - Timer
///
/// Once the object has been signalled, the poller will emit the `interest` event.
///
/// # Safety
///
/// The added handle must not be dropped before it is deleted.
///
/// # Examples
///
/// ```no_run
/// use polling::{Poller, Event, Events, PollMode};
/// use polling::os::iocp::PollerIocpExt;
///
/// use std::process::Command;
///
/// // Spawn a new process.
/// let mut child = Command::new("echo")
/// .arg("Hello, world!")
/// .spawn()
/// .unwrap();
///
/// // Create a new poller.
/// let poller = Poller::new().unwrap();
///
/// // Add the child process to the poller.
/// unsafe {
/// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap();
/// }
///
/// // Wait for the child process to exit.
/// let mut events = Events::new();
/// poller.wait(&mut events, None).unwrap();
///
/// assert_eq!(events.len(), 1);
/// assert_eq!(events.iter().next().unwrap(), Event::all(0));
/// ```
unsafe fn add_waitable(
&self,
handle: impl AsRawWaitable,
interest: Event,
mode: PollMode,
) -> io::Result<()>;
/// Modify an existing waitable handle.
///
/// This function can be used to change the emitted event and/or mode of an existing waitable
/// handle. The handle must have been previously added to the poller using [`add_waitable`].
///
/// [`add_waitable`]: Self::add_waitable
///
/// # Examples
///
/// ```no_run
/// use polling::{Poller, Event, Events, PollMode};
/// use polling::os::iocp::PollerIocpExt;
///
/// use std::process::Command;
///
/// // Spawn a new process.
/// let mut child = Command::new("echo")
/// .arg("Hello, world!")
/// .spawn()
/// .unwrap();
///
/// // Create a new poller.
/// let poller = Poller::new().unwrap();
///
/// // Add the child process to the poller.
/// unsafe {
/// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap();
/// }
///
/// // Wait for the child process to exit.
/// let mut events = Events::new();
/// poller.wait(&mut events, None).unwrap();
///
/// assert_eq!(events.len(), 1);
/// assert_eq!(events.iter().next().unwrap(), Event::all(0));
///
/// // Modify the waitable handle.
/// poller.modify_waitable(&child, Event::readable(0), PollMode::Oneshot).unwrap();
/// ```
fn modify_waitable(
&self,
handle: impl AsWaitable,
interest: Event,
mode: PollMode,
) -> io::Result<()>;
/// Remove a waitable handle from this poller.
///
/// This function can be used to remove a waitable handle from the poller. The handle must
/// have been previously added to the poller using [`add_waitable`].
///
/// [`add_waitable`]: Self::add_waitable
///
/// # Examples
///
/// ```no_run
/// use polling::{Poller, Event, Events, PollMode};
/// use polling::os::iocp::PollerIocpExt;
///
/// use std::process::Command;
///
/// // Spawn a new process.
/// let mut child = Command::new("echo")
/// .arg("Hello, world!")
/// .spawn()
/// .unwrap();
///
/// // Create a new poller.
/// let poller = Poller::new().unwrap();
///
/// // Add the child process to the poller.
/// unsafe {
/// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap();
/// }
///
/// // Wait for the child process to exit.
/// let mut events = Events::new();
/// poller.wait(&mut events, None).unwrap();
///
/// assert_eq!(events.len(), 1);
/// assert_eq!(events.iter().next().unwrap(), Event::all(0));
///
/// // Remove the waitable handle.
/// poller.remove_waitable(&child).unwrap();
/// ```
fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()>;
}
impl PollerIocpExt for Poller {
fn post(&self, packet: CompletionPacket) -> io::Result<()> {
self.poller.post(packet)
}
unsafe fn add_waitable(
&self,
handle: impl AsRawWaitable,
event: Event,
mode: PollMode,
) -> io::Result<()> {
self.poller
.add_waitable(handle.as_raw_handle(), event, mode)
}
fn modify_waitable(
&self,
handle: impl AsWaitable,
interest: Event,
mode: PollMode,
) -> io::Result<()> {
self.poller
.modify_waitable(handle.as_waitable().as_raw_handle(), interest, mode)
}
fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()> {
self.poller
.remove_waitable(handle.as_waitable().as_raw_handle())
}
}
/// A type that represents a waitable handle.
pub trait AsRawWaitable {
/// Returns the raw handle of this waitable.
fn as_raw_handle(&self) -> RawHandle;
}
impl AsRawWaitable for RawHandle {
fn as_raw_handle(&self) -> RawHandle {
*self
}
}
impl<T: AsRawHandle + ?Sized> AsRawWaitable for &T {
fn as_raw_handle(&self) -> RawHandle {
AsRawHandle::as_raw_handle(*self)
}
}
/// A type that represents a waitable handle.
pub trait AsWaitable: AsHandle {
/// Returns the raw handle of this waitable.
fn as_waitable(&self) -> BorrowedHandle<'_> {
self.as_handle()
}
}
impl<T: AsHandle + ?Sized> AsWaitable for T {}

304
vendor/polling/src/os/kqueue.rs vendored Normal file
View File

@@ -0,0 +1,304 @@
//! Functionality that is only available for `kqueue`-based platforms.
use crate::sys::{mode_to_flags, SourceId};
use crate::{PollMode, Poller};
use std::io;
use std::marker::PhantomData;
use std::process::Child;
use std::time::Duration;
use rustix::event::kqueue;
use super::__private::PollerSealed;
use __private::FilterSealed;
// TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current
// API makes it difficult to effectively express events from these filters. At the next breaking
// change, we should change `Event` to be a struct with private fields, and encode additional
// information in there.
/// Functionality that is only available for `kqueue`-based platforms.
///
/// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using
/// this extension trait, you can monitor for signals, process exits, and more. See the implementors
/// of the [`Filter`] trait for more information.
pub trait PollerKqueueExt<F: Filter>: PollerSealed {
/// Add a filter to the poller.
///
/// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of
/// a socket. See the implementors of the [`Filter`] trait for more information.
///
/// # Examples
///
/// ```no_run
/// use polling::{Events, Poller, PollMode};
/// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal};
///
/// let poller = Poller::new().unwrap();
///
/// // Register the SIGINT signal.
/// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap();
///
/// // Wait for the signal.
/// let mut events = Events::new();
/// poller.wait(&mut events, None).unwrap();
/// # let _ = events;
/// ```
fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>;
/// Modify a filter in the poller.
///
/// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter
/// instead of a socket. See the implementors of the [`Filter`] trait for more information.
///
/// # Examples
///
/// ```no_run
/// use polling::{Events, Poller, PollMode};
/// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal};
///
/// let poller = Poller::new().unwrap();
///
/// // Register the SIGINT signal.
/// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap();
///
/// // Re-register with a different key.
/// poller.modify_filter(Signal(rustix::process::Signal::INT.as_raw()), 1, PollMode::Oneshot).unwrap();
///
/// // Wait for the signal.
/// let mut events = Events::new();
/// poller.wait(&mut events, None).unwrap();
/// # let _ = events;
/// ```
fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>;
/// Remove a filter from the poller.
///
/// This is used to remove filters that were previously added with
/// [`add_filter`](PollerKqueueExt::add_filter).
///
/// # Examples
///
/// ```no_run
/// use polling::{Poller, PollMode};
/// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal};
///
/// let poller = Poller::new().unwrap();
///
/// // Register the SIGINT signal.
/// poller.add_filter(Signal(rustix::process::Signal::INT.as_raw()), 0, PollMode::Oneshot).unwrap();
///
/// // Remove the filter.
/// poller.delete_filter(Signal(rustix::process::Signal::INT.as_raw())).unwrap();
/// ```
fn delete_filter(&self, filter: F) -> io::Result<()>;
}
impl<F: Filter> PollerKqueueExt<F> for Poller {
#[inline(always)]
fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> {
// No difference between adding and modifying in kqueue.
self.poller.add_source(filter.source_id())?;
self.modify_filter(filter, key, mode)
}
fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> {
self.poller.has_source(filter.source_id())?;
// Convert the filter into a kevent.
let event = filter.filter(kqueue::EventFlags::ADD | mode_to_flags(mode), key);
// Modify the filter.
self.poller.submit_changes([event])
}
fn delete_filter(&self, filter: F) -> io::Result<()> {
// Convert the filter into a kevent.
let event = filter.filter(kqueue::EventFlags::DELETE, 0);
// Delete the filter.
self.poller.submit_changes([event])?;
self.poller.remove_source(filter.source_id())
}
}
/// A filter that can be registered into a `kqueue`.
pub trait Filter: FilterSealed {}
unsafe impl<T: FilterSealed + ?Sized> FilterSealed for &T {
#[inline(always)]
fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event {
(**self).filter(flags, key)
}
#[inline(always)]
fn source_id(&self) -> SourceId {
(**self).source_id()
}
}
impl<T: Filter + ?Sized> Filter for &T {}
/// Monitor this signal number.
///
/// No matter what `PollMode` is specified, this filter will always be
/// oneshot-only.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Signal(pub std::os::raw::c_int);
unsafe impl FilterSealed for Signal {
#[inline(always)]
fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event {
kqueue::Event::new(
kqueue::EventFilter::Signal {
signal: rustix::process::Signal::from_named_raw(self.0)
.expect("invalid signal number"),
times: 0,
},
flags | kqueue::EventFlags::RECEIPT,
key as _,
)
}
#[inline(always)]
fn source_id(&self) -> SourceId {
SourceId::Signal(self.0)
}
}
impl Filter for Signal {}
/// Monitor a child process.
#[derive(Debug)]
pub struct Process<'a> {
/// The process ID to monitor.
pid: rustix::process::Pid,
/// The operation to monitor.
ops: ProcessOps,
/// Lifetime of the underlying process.
_lt: PhantomData<&'a Child>,
}
/// The operations that a monitored process can perform.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum ProcessOps {
/// The process exited.
Exit,
/// The process was forked.
Fork,
/// The process executed a new process.
Exec,
}
impl<'a> Process<'a> {
/// Monitor a child process.
///
/// # Safety
///
/// Once registered into the `Poller`, the `Child` object must outlive this filter's
/// registration into the poller.
pub unsafe fn new(child: &'a Child, ops: ProcessOps) -> Self {
Self {
pid: rustix::process::Pid::from_child(child),
ops,
_lt: PhantomData,
}
}
/// Create a `Process` from a PID.
///
/// # Safety
///
/// The PID must be tied to an actual child process.
pub unsafe fn from_pid(pid: std::num::NonZeroI32, ops: ProcessOps) -> Self {
Self {
pid: unsafe { rustix::process::Pid::from_raw_unchecked(pid.get()) },
ops,
_lt: PhantomData,
}
}
}
unsafe impl FilterSealed for Process<'_> {
#[inline(always)]
fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event {
let events = match self.ops {
ProcessOps::Exit => kqueue::ProcessEvents::EXIT,
ProcessOps::Fork => kqueue::ProcessEvents::FORK,
ProcessOps::Exec => kqueue::ProcessEvents::EXEC,
};
kqueue::Event::new(
kqueue::EventFilter::Proc {
// SAFETY: We know that the PID is nonzero.
pid: self.pid,
flags: events,
},
flags | kqueue::EventFlags::RECEIPT,
key as _,
)
}
#[inline(always)]
fn source_id(&self) -> SourceId {
// SAFETY: We know that the PID is nonzero
SourceId::Pid(self.pid)
}
}
impl Filter for Process<'_> {}
/// Wait for a timeout to expire.
///
/// Modifying the timeout after it has been added to the poller will reset it.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timer {
/// Identifier for the timer.
pub id: usize,
/// The timeout to wait for.
pub timeout: Duration,
}
unsafe impl FilterSealed for Timer {
fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event {
kqueue::Event::new(
kqueue::EventFilter::Timer {
ident: self.id as _,
timer: Some(self.timeout),
},
flags | kqueue::EventFlags::RECEIPT,
key as _,
)
}
#[inline(always)]
fn source_id(&self) -> SourceId {
SourceId::Timer(self.id)
}
}
impl Filter for Timer {}
mod __private {
use crate::sys::SourceId;
use rustix::event::kqueue;
#[doc(hidden)]
pub unsafe trait FilterSealed {
/// Get the filter for the given event.
///
/// This filter's flags must have `EV_RECEIPT`.
fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event;
/// Get the source ID for this source.
fn source_id(&self) -> SourceId;
}
}

857
vendor/polling/src/poll.rs vendored Normal file
View File

@@ -0,0 +1,857 @@
//! Bindings to poll (VxWorks, Fuchsia, other Unix systems).
use std::collections::HashMap;
use std::io;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex};
use std::time::Instant;
#[cfg(not(target_os = "hermit"))]
use rustix::fd::{AsFd, AsRawFd, BorrowedFd};
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd};
use syscall::{poll, PollFd, PollFlags};
// std::os::unix doesn't exist on Fuchsia
type RawFd = std::os::raw::c_int;
use crate::{Event, PollMode};
/// Interface to poll.
#[derive(Debug)]
pub struct Poller {
/// File descriptors to poll.
fds: Mutex<Fds>,
/// Notification pipe for waking up the poller.
///
/// On all platforms except ESP IDF, the `pipe` syscall is used.
/// On ESP IDF, the `eventfd` syscall is used instead.
notify: notify::Notify,
/// 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,
/// Whether `wait` has been notified by the user.
notified: AtomicBool,
/// 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,
}
/// The file descriptors to poll in a `Poller`.
#[derive(Debug)]
struct Fds {
/// The list of `pollfds` taken by poll.
///
/// The first file descriptor is always present and is used to notify the poller. It is also
/// stored in `notify_read`.
poll_fds: Vec<PollFd<'static>>,
/// The map of each file descriptor to data associated with it. This does not include the file
/// descriptors `notify_read` or `notify_write`.
fd_data: HashMap<RawFd, FdData>,
}
/// Data associated with a file descriptor in a poller.
#[derive(Debug)]
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.
key: usize,
/// Whether to remove this file descriptor from the poller on the next call to `wait`.
remove: bool,
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
let notify = notify::Notify::new()?;
#[cfg(feature = "tracing")]
tracing::trace!(?notify, "new");
Ok(Self {
fds: Mutex::new(Fds {
poll_fds: vec![PollFd::from_borrowed_fd(
// SAFETY: `notify.fd()` will remain valid until we drop `self`.
unsafe { BorrowedFd::borrow_raw(notify.fd().as_raw_fd()) },
notify.poll_flags(),
)],
fd_data: HashMap::new(),
}),
notify,
waiting_operations: AtomicUsize::new(0),
operations_complete: Condvar::new(),
notified: AtomicBool::new(false),
})
}
/// Whether this poller supports level-triggered events.
pub fn supports_level(&self) -> bool {
true
}
/// Whether the poller supports edge-triggered events.
pub fn supports_edge(&self) -> bool {
false
}
/// Adds a new file descriptor.
pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
if self.notify.has_fd(fd) {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"add",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
self.modify_fds(|fds| {
if fds.fd_data.contains_key(&fd) {
return Err(io::Error::from(io::ErrorKind::AlreadyExists));
}
let poll_fds_index = fds.poll_fds.len();
fds.fd_data.insert(
fd,
FdData {
poll_fds_index,
key: ev.key,
remove: cvt_mode_as_remove(mode)?,
},
);
fds.poll_fds.push(PollFd::from_borrowed_fd(
// SAFETY: Until we have I/O safety, assume that `fd` is valid forever.
unsafe { BorrowedFd::borrow_raw(fd) },
poll_events(ev),
));
Ok(())
})
}
/// Modifies an existing file descriptor.
pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> {
if self.notify.has_fd(fd.as_raw_fd()) {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
self.modify_fds(|fds| {
let data = fds
.fd_data
.get_mut(&fd.as_raw_fd())
.ok_or(io::ErrorKind::NotFound)?;
data.key = ev.key;
let poll_fds_index = data.poll_fds_index;
// SAFETY: This is essentially transmuting a `PollFd<'a>` to a `PollFd<'static>`, which
// only works if it's removed in time with `delete()`.
fds.poll_fds[poll_fds_index] = PollFd::from_borrowed_fd(
unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) },
poll_events(ev),
);
data.remove = cvt_mode_as_remove(mode)?;
Ok(())
})
}
/// Deletes a file descriptor.
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
if self.notify.has_fd(fd.as_raw_fd()) {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
self.modify_fds(|fds| {
let data = fds
.fd_data
.remove(&fd.as_raw_fd())
.ok_or(io::ErrorKind::NotFound)?;
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.as_fd().as_raw_fd())
.unwrap()
.poll_fds_index = data.poll_fds_index;
}
Ok(())
})
}
/// Waits for I/O events with an optional deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
notify_read = ?self.notify.fd().as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let mut fds = self.fds.lock().unwrap();
loop {
// Complete all current operations.
loop {
if self.notified.swap(false, Ordering::SeqCst) {
// `notify` will have sent a notification in case we were polling. We weren't,
// so remove it.
return self.notify.pop_notification();
} else if self.waiting_operations.load(Ordering::SeqCst) == 0 {
break;
}
fds = self.operations_complete.wait(fds).unwrap();
}
let timeout =
deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Perform the poll.
let num_events = poll(&mut fds.poll_fds, timeout)?;
let notified = !fds.poll_fds[0].revents().is_empty();
let num_fd_events = if notified { num_events - 1 } else { num_events };
#[cfg(feature = "tracing")]
tracing::trace!(?num_events, ?notified, ?num_fd_events, "new events",);
// Read all notifications.
if notified {
self.notify.pop_all_notifications()?;
}
// If the only event that occurred during polling was notification and it wasn't to
// exit, another thread is trying to perform an operation on the fds. Continue the
// loop.
if !self.notified.swap(false, Ordering::SeqCst) && num_fd_events == 0 && notified {
continue;
}
// Store the events if there were any.
if num_fd_events > 0 {
let fds = &mut *fds;
events.inner.reserve(num_fd_events);
for fd_data in fds.fd_data.values_mut() {
let poll_fd = &mut fds.poll_fds[fd_data.poll_fds_index];
if !poll_fd.revents().is_empty() {
// Store event
let revents = poll_fd.revents();
events.inner.push(Event {
key: fd_data.key,
readable: revents.intersects(read_events()),
writable: revents.intersects(write_events()),
extra: EventExtra { flags: revents },
});
// Remove interest if necessary
if fd_data.remove {
*poll_fd = PollFd::from_borrowed_fd(
unsafe { BorrowedFd::borrow_raw(poll_fd.as_fd().as_raw_fd()) },
PollFlags::empty(),
);
}
if events.inner.len() == num_fd_events {
break;
}
}
}
}
break;
}
Ok(())
}
/// Sends a notification to wake up the current or next `wait()` call.
pub fn notify(&self) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
notify_read = ?self.notify.fd().as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
if !self.notified.swap(true, Ordering::SeqCst) {
self.notify.notify()?;
self.operations_complete.notify_one();
}
Ok(())
}
/// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running.
fn modify_fds(&self, f: impl FnOnce(&mut Fds) -> io::Result<()>) -> io::Result<()> {
self.waiting_operations.fetch_add(1, Ordering::SeqCst);
// Wake up the current caller of `wait` if there is one.
let sent_notification = self.notify.notify().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 {
let _ = self.notify.pop_notification();
}
let res = f(&mut fds);
if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 {
self.operations_complete.notify_one();
}
res
}
}
/// Get the input poll events for the given event.
fn poll_events(ev: Event) -> PollFlags {
(if ev.readable {
PollFlags::IN | PollFlags::PRI
} else {
PollFlags::empty()
}) | (if ev.writable {
PollFlags::OUT | PollFlags::WRBAND
} else {
PollFlags::empty()
})
}
/// Returned poll events for reading.
fn read_events() -> PollFlags {
PollFlags::IN | PollFlags::PRI | PollFlags::HUP | PollFlags::ERR
}
/// Returned poll events for writing.
fn write_events() -> PollFlags {
PollFlags::OUT | PollFlags::WRBAND | PollFlags::HUP | PollFlags::ERR
}
/// A list of reported I/O events.
pub struct Events {
inner: Vec<Event>,
}
impl Events {
/// Creates an empty list.
pub fn with_capacity(cap: usize) -> Events {
Self {
inner: Vec::with_capacity(cap),
}
}
/// Iterates over I/O events.
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.inner.iter().copied()
}
/// Clear the list.
pub fn clear(&mut self) {
self.inner.clear();
}
/// Get the capacity of the list.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
}
/// Extra information associated with an event.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct EventExtra {
/// Flags associated with this event.
flags: PollFlags,
}
impl EventExtra {
/// Creates an empty set of extra information.
#[inline]
pub const fn empty() -> Self {
Self {
flags: PollFlags::empty(),
}
}
/// Set the interrupt flag.
#[inline]
pub fn set_hup(&mut self, value: bool) {
self.flags.set(PollFlags::HUP, value);
}
/// Set the priority flag.
#[inline]
pub fn set_pri(&mut self, value: bool) {
self.flags.set(PollFlags::PRI, value);
}
/// Is this an interrupt event?
#[inline]
pub fn is_hup(&self) -> bool {
self.flags.contains(PollFlags::HUP)
}
/// Is this a priority event?
#[inline]
pub fn is_pri(&self) -> bool {
self.flags.contains(PollFlags::PRI)
}
#[inline]
pub fn is_connect_failed(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP))
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR))
}
}
fn cvt_mode_as_remove(mode: PollMode) -> io::Result<bool> {
match mode {
PollMode::Oneshot => Ok(true),
PollMode::Level => Ok(false),
_ => Err(crate::unsupported_error(
"edge-triggered I/O events are not supported in poll()",
)),
}
}
#[cfg(unix)]
mod syscall {
pub(super) use rustix::event::{PollFd, PollFlags};
pub(super) use rustix::event::Timespec;
#[cfg(target_os = "espidf")]
pub(super) use rustix::event::{eventfd, EventfdFlags};
#[cfg(target_os = "espidf")]
pub(super) use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(target_os = "espidf")]
pub(super) use rustix::io::{read, write};
use std::io;
use std::time::Duration;
/// Safe wrapper around the `poll` system call.
pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option<Duration>) -> io::Result<usize> {
// Timeout for `poll`. In case of overflow, use no timeout.
let timeout = match timeout {
Some(timeout) => Timespec::try_from(timeout).ok(),
None => None,
};
Ok(rustix::event::poll(fds, timeout.as_ref())?)
}
}
#[cfg(target_os = "hermit")]
mod syscall {
// TODO: Remove this shim once HermitOS is supported in Rustix.
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::ops::BitOr;
use std::time::Duration;
pub(super) use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
/// Create an eventfd.
pub(super) fn eventfd(count: u64, _flags: EventfdFlags) -> io::Result<OwnedFd> {
let fd = unsafe { hermit_abi::eventfd(count, 0) };
if fd < 0 {
Err(io::Error::from_raw_os_error(unsafe {
hermit_abi::get_errno()
}))
} else {
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
}
/// Read some bytes.
pub(super) fn read(fd: BorrowedFd<'_>, bytes: &mut [u8]) -> io::Result<usize> {
let count = unsafe { hermit_abi::read(fd.as_raw_fd(), bytes.as_mut_ptr(), bytes.len()) };
cvt(count)
}
/// Write some bytes.
pub(super) fn write(fd: BorrowedFd<'_>, bytes: &[u8]) -> io::Result<usize> {
let count = unsafe { hermit_abi::write(fd.as_raw_fd(), bytes.as_ptr(), bytes.len()) };
cvt(count)
}
/// Safe wrapper around the `poll` system call.
pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option<Duration>) -> io::Result<usize> {
// Timeout in milliseconds for epoll. In case of overflow, use no timeout.
let mut timeout_ms = -1;
if let Some(t) = timeout {
if let Ok(ms) = i32::try_from(t.as_millis()) {
// Round up to a whole millisecond.
if Duration::from_millis(ms as u64) < t {
if let Some(ms) = ms.checked_add(1) {
timeout_ms = ms;
}
} else {
timeout_ms = ms;
}
}
}
let call = unsafe {
hermit_abi::poll(
fds.as_mut_ptr() as *mut hermit_abi::pollfd,
fds.len(),
timeout_ms,
)
};
cvt(call as isize)
}
/// Safe wrapper around `pollfd`.
#[repr(transparent)]
pub(super) struct PollFd<'a> {
inner: hermit_abi::pollfd,
_lt: PhantomData<BorrowedFd<'a>>,
}
impl<'a> PollFd<'a> {
pub(super) fn from_borrowed_fd(fd: BorrowedFd<'a>, inflags: PollFlags) -> Self {
Self {
inner: hermit_abi::pollfd {
fd: fd.as_raw_fd(),
events: inflags.0,
revents: 0,
},
_lt: PhantomData,
}
}
pub(super) fn revents(&self) -> PollFlags {
PollFlags(self.inner.revents)
}
}
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.inner.fd) }
}
}
impl fmt::Debug for PollFd<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollFd")
.field("fd", &format_args!("0x{:x}", self.inner.fd))
.field("events", &PollFlags(self.inner.events))
.field("revents", &PollFlags(self.inner.revents))
.finish()
}
}
/// Wrapper around polling flags.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) struct PollFlags(i16);
impl PollFlags {
/// Empty set of flags.
pub(super) const fn empty() -> Self {
Self(0)
}
pub(super) const IN: PollFlags = PollFlags(hermit_abi::POLLIN);
pub(super) const OUT: PollFlags = PollFlags(hermit_abi::POLLOUT);
pub(super) const WRBAND: PollFlags = PollFlags(hermit_abi::POLLWRBAND);
pub(super) const ERR: PollFlags = PollFlags(hermit_abi::POLLERR);
pub(super) const HUP: PollFlags = PollFlags(hermit_abi::POLLHUP);
pub(super) const PRI: PollFlags = PollFlags(hermit_abi::POLLPRI);
/// Tell if this contains some flags.
pub(super) fn contains(self, flags: PollFlags) -> bool {
self.0 & flags.0 != 0
}
/// Set a flag.
pub(super) fn set(&mut self, flags: PollFlags, set: bool) {
if set {
self.0 |= flags.0;
} else {
self.0 &= !(flags.0);
}
}
/// Tell if this is empty.
pub(super) fn is_empty(self) -> bool {
self.0 == 0
}
/// Tell if this intersects with some flags.
pub(super) fn intersects(self, flags: PollFlags) -> bool {
self.contains(flags)
}
}
impl BitOr for PollFlags {
type Output = PollFlags;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
#[derive(Debug, Copy, Clone)]
pub(super) struct EventfdFlags;
impl EventfdFlags {
pub(super) fn empty() -> Self {
Self
}
}
/// Convert a number to an actual result.
#[inline]
fn cvt(len: isize) -> io::Result<usize> {
if len < 0 {
Err(io::Error::from_raw_os_error(unsafe {
hermit_abi::get_errno()
}))
} else {
Ok(len as usize)
}
}
}
#[cfg(not(any(target_os = "espidf", target_os = "hermit")))]
mod notify {
use std::io;
use rustix::event::PollFlags;
use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags};
#[cfg(not(any(target_os = "haiku", target_os = "nto")))]
use rustix::pipe::pipe_with;
use rustix::pipe::{pipe, PipeFlags};
/// A notification pipe.
///
/// This implementation uses a pipe to send notifications.
#[derive(Debug)]
pub(super) struct Notify {
/// The file descriptor of the read half of the notify pipe. This is also stored as the first
/// file descriptor in `fds.poll_fds`.
read_pipe: OwnedFd,
/// The file descriptor of the write half of the notify pipe.
///
/// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the
/// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs
/// to occur (in which case `Poller::waiting_operations` would have been incremented).
write_pipe: OwnedFd,
}
impl Notify {
/// Creates a new notification pipe.
pub(super) fn new() -> io::Result<Self> {
let fallback_pipe = |_| {
let (read_pipe, write_pipe) = pipe()?;
fcntl_setfd(&read_pipe, fcntl_getfd(&read_pipe)? | FdFlags::CLOEXEC)?;
fcntl_setfd(&write_pipe, fcntl_getfd(&write_pipe)? | FdFlags::CLOEXEC)?;
io::Result::Ok((read_pipe, write_pipe))
};
#[cfg(not(any(target_os = "haiku", target_os = "nto")))]
let (read_pipe, write_pipe) = pipe_with(PipeFlags::CLOEXEC).or_else(fallback_pipe)?;
#[cfg(any(target_os = "haiku", target_os = "nto"))]
let (read_pipe, write_pipe) = fallback_pipe(PipeFlags::CLOEXEC)?;
// Put the reading side into non-blocking mode.
fcntl_setfl(&read_pipe, fcntl_getfl(&read_pipe)? | OFlags::NONBLOCK)?;
Ok(Self {
read_pipe,
write_pipe,
})
}
/// Provides the file handle of the read half of the notify pipe that needs to be registered by the `Poller`.
pub(super) fn fd(&self) -> BorrowedFd<'_> {
self.read_pipe.as_fd()
}
/// Provides the poll flags to be used when registering the read half of the notify pipe with the `Poller`.
pub(super) fn poll_flags(&self) -> PollFlags {
PollFlags::RDNORM
}
/// Notifies the `Poller` instance via the write half of the notify pipe.
pub(super) fn notify(&self) -> Result<(), io::Error> {
write(&self.write_pipe, &[0; 1])?;
Ok(())
}
/// Pops a notification (if any) from the pipe.
pub(super) fn pop_notification(&self) -> Result<(), io::Error> {
// Pipes on Vita do not guarantee that after `write` call succeeds, the
// data becomes immediately available for reading on the other side of the pipe.
// To ensure that the notification is not lost, the read side of the pipe is temporarily
// switched to blocking for a single `read` call.
#[cfg(target_os = "vita")]
rustix::fs::fcntl_setfl(
&self.read_pipe,
rustix::fs::fcntl_getfl(&self.read_pipe)? & !rustix::fs::OFlags::NONBLOCK,
)?;
let result = read(&self.read_pipe, &mut [0; 1]);
#[cfg(target_os = "vita")]
rustix::fs::fcntl_setfl(
&self.read_pipe,
rustix::fs::fcntl_getfl(&self.read_pipe)? | rustix::fs::OFlags::NONBLOCK,
)?;
result?;
Ok(())
}
/// Pops all notifications from the pipe.
pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> {
while read(&self.read_pipe, &mut [0; 64]).is_ok() {}
Ok(())
}
/// Whether this raw file descriptor is associated with this notifier.
pub(super) fn has_fd(&self, fd: RawFd) -> bool {
self.read_pipe.as_raw_fd() == fd || self.write_pipe.as_raw_fd() == fd
}
}
}
#[cfg(any(target_os = "espidf", target_os = "hermit"))]
mod notify {
use std::io;
use std::mem;
use super::syscall::{
eventfd, read, write, AsFd, AsRawFd, BorrowedFd, EventfdFlags, OwnedFd, PollFlags, RawFd,
};
/// A notification pipe.
///
/// This implementation uses the `eventfd` syscall to send notifications.
#[derive(Debug)]
pub(super) struct Notify {
/// The file descriptor of the eventfd object. This is also stored as the first
/// file descriptor in `fds.poll_fds`.
///
/// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the
/// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs
/// to occur (in which case `Poller::waiting_operations` would have been incremented).
event_fd: OwnedFd,
}
impl Notify {
/// Creates a new notification pipe.
pub(super) fn new() -> io::Result<Self> {
// Note that the eventfd() implementation in ESP-IDF deviates from the specification in the following ways:
// 1) The file descriptor is always in a non-blocking mode, as if EFD_NONBLOCK was passed as a flag;
// passing EFD_NONBLOCK or calling fcntl(.., F_GETFL/F_SETFL) on the eventfd() file descriptor is not supported
// 2) It always returns the counter value, even if it is 0. This is contrary to the specification which mandates
// that it should instead fail with EAGAIN
//
// (1) is not a problem for us, as we want the eventfd() file descriptor to be in a non-blocking mode anyway
// (2) is also not a problem, as long as we don't try to read the counter value in an endless loop when we detect being notified
let flags = EventfdFlags::empty();
let event_fd = eventfd(0, flags).map_err(|err| {
match io::Error::from(err) {
err if err.kind() == io::ErrorKind::PermissionDenied => {
// EPERM can happen if the eventfd isn't initialized yet.
// Tell the user to call esp_vfs_eventfd_register.
io::Error::new(
io::ErrorKind::PermissionDenied,
"failed to initialize eventfd for polling, try calling `esp_vfs_eventfd_register`"
)
},
err => err,
}
})?;
Ok(Self { event_fd })
}
/// Provides the eventfd file handle that needs to be registered by the `Poller`.
pub(super) fn fd(&self) -> BorrowedFd<'_> {
self.event_fd.as_fd()
}
/// Provides the eventfd file handle poll flags to be used when registering it with the `Poller`.
pub(super) fn poll_flags(&self) -> PollFlags {
PollFlags::IN
}
/// Notifies the `Poller` instance via the eventfd file descriptor.
pub(super) fn notify(&self) -> Result<(), io::Error> {
write(self.event_fd.as_fd(), &1u64.to_ne_bytes())?;
Ok(())
}
/// Pops a notification (if any) from the eventfd file descriptor.
pub(super) fn pop_notification(&self) -> Result<(), io::Error> {
read(self.event_fd.as_fd(), &mut [0; mem::size_of::<u64>()])?;
Ok(())
}
/// Pops all notifications from the eventfd file descriptor.
/// Since the eventfd object accumulates all writes in a single 64 bit value,
/// this operation is - in fact - equivalent to `pop_notification`.
pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> {
let _ = self.pop_notification();
Ok(())
}
/// Whether this raw file descriptor is associated with this notifier.
pub(super) fn has_fd(&self, fd: RawFd) -> bool {
self.event_fd.as_raw_fd() == fd
}
}
}

287
vendor/polling/src/port.rs vendored Normal file
View File

@@ -0,0 +1,287 @@
//! Bindings to event port (illumos, Solaris).
use std::io;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::time::Instant;
use rustix::buffer::spare_capacity;
use rustix::event::{port, PollFlags, Timespec};
use rustix::fd::OwnedFd;
use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags};
use crate::{Event, PollMode};
/// Interface to event ports.
#[derive(Debug)]
pub struct Poller {
/// File descriptor for the port instance.
port_fd: OwnedFd,
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
let port_fd = port::create()?;
let flags = fcntl_getfd(&port_fd)?;
fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?;
#[cfg(feature = "tracing")]
tracing::trace!(
port_fd = ?port_fd.as_raw_fd(),
"new",
);
Ok(Poller { port_fd })
}
/// Whether this poller supports level-triggered events.
pub fn supports_level(&self) -> bool {
false
}
/// Whether this poller supports edge-triggered events.
pub fn supports_edge(&self) -> bool {
false
}
/// Adds a file descriptor.
///
/// # Safety
///
/// The `fd` must be a valid file descriptor and it must last until it is deleted.
pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
// File descriptors don't need to be added explicitly, so just modify the interest.
self.modify(BorrowedFd::borrow_raw(fd), ev, mode)
}
/// Modifies an existing file descriptor.
pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
port_fd = ?self.port_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let mut flags = PollFlags::empty();
if ev.readable {
flags |= read_flags();
}
if ev.writable {
flags |= write_flags();
}
if mode != PollMode::Oneshot {
return Err(crate::unsupported_error(
"this kind of event is not supported with event ports",
));
}
unsafe {
port::associate_fd(&self.port_fd, fd, flags, ev.key as _)?;
}
Ok(())
}
/// Deletes a file descriptor.
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
port_fd = ?self.port_fd.as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let result = unsafe { port::dissociate_fd(&self.port_fd, fd) };
if let Err(e) = result {
match e {
rustix::io::Errno::NOENT => return Ok(()),
_ => return Err(e.into()),
}
}
Ok(())
}
/// Waits for I/O events with an optional deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
port_fd = ?self.port_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Timeout for `port::getn`. In case of overflow, use no timeout.
let timeout = match timeout {
Some(t) => Timespec::try_from(t).ok(),
None => None,
};
// Wait for I/O events.
let res = port::getn(
&self.port_fd,
spare_capacity(&mut events.list),
1,
timeout.as_ref(),
);
#[cfg(feature = "tracing")]
tracing::trace!(
port_fd = ?self.port_fd,
res = ?events.list.len(),
"new events"
);
// Event ports sets the return value to -1 and returns ETIME on timer expire. The number of
// returned events is stored in nget, but in our case it should always be 0 since we set
// nget to 1 initially.
if let Err(e) = res {
match e {
rustix::io::Errno::TIME => {}
_ => return Err(e.into()),
}
}
Ok(())
}
/// Sends a notification to wake up the current or next `wait()` call.
pub fn notify(&self) -> io::Result<()> {
const PORT_SOURCE_USER: i32 = 3;
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
port_fd = ?self.port_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
// Use port_send to send a notification to the port.
port::send(&self.port_fd, PORT_SOURCE_USER, crate::NOTIFY_KEY as _)?;
Ok(())
}
}
impl AsRawFd for Poller {
fn as_raw_fd(&self) -> RawFd {
self.port_fd.as_raw_fd()
}
}
impl AsFd for Poller {
fn as_fd(&self) -> BorrowedFd<'_> {
self.port_fd.as_fd()
}
}
/// Poll flags for all possible readability events.
fn read_flags() -> PollFlags {
PollFlags::IN | PollFlags::HUP | PollFlags::ERR | PollFlags::PRI
}
/// Poll flags for all possible writability events.
fn write_flags() -> PollFlags {
PollFlags::OUT | PollFlags::HUP | PollFlags::ERR
}
/// A list of reported I/O events.
pub struct Events {
list: Vec<port::Event>,
}
unsafe impl Send for Events {}
impl Events {
/// Creates an empty list.
pub fn with_capacity(cap: usize) -> Events {
Events {
list: Vec::with_capacity(cap),
}
}
/// Iterates over I/O events.
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list.iter().map(|ev| {
let flags = PollFlags::from_bits_truncate(ev.events() as _);
Event {
key: ev.userdata() as usize,
readable: flags.intersects(read_flags()),
writable: flags.intersects(write_flags()),
extra: EventExtra { flags },
}
})
}
/// Clear the list.
pub fn clear(&mut self) {
self.list.clear();
}
/// Get the capacity of the list.
pub fn capacity(&self) -> usize {
self.list.capacity()
}
}
/// Extra information associated with an event.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct EventExtra {
/// Flags associated with this event.
flags: PollFlags,
}
impl EventExtra {
/// Create a new, empty version of this struct.
#[inline]
pub const fn empty() -> EventExtra {
EventExtra {
flags: PollFlags::empty(),
}
}
/// Set the interrupt flag.
#[inline]
pub fn set_hup(&mut self, value: bool) {
self.flags.set(PollFlags::HUP, value);
}
/// Set the priority flag.
#[inline]
pub fn set_pri(&mut self, value: bool) {
self.flags.set(PollFlags::PRI, value);
}
/// Is this an interrupt event?
#[inline]
pub fn is_hup(&self) -> bool {
self.flags.contains(PollFlags::HUP)
}
/// Is this a priority event?
#[inline]
pub fn is_pri(&self) -> bool {
self.flags.contains(PollFlags::PRI)
}
#[inline]
pub fn is_connect_failed(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP))
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR))
}
}