#![cfg_attr(feature = "nightly", feature(async_iterator, cfg_sanitize))]
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)))]
compile_error!("OS not supported");
use std::time::Duration;
pub mod fd;
mod asan;
mod config;
mod msan;
mod op;
#[cfg(unix)]
mod unix;
pub mod extract;
pub mod fs;
pub mod io;
pub mod mem;
pub mod net;
pub mod pipe;
pub mod process;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod io_uring;
#[cfg(any(target_os = "android", target_os = "linux"))]
use io_uring as sys;
#[cfg(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",
))]
mod kqueue;
#[cfg(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",
))]
use kqueue as sys;
#[doc(inline)]
pub use config::Config;
#[doc(no_inline)]
pub use extract::Extract;
#[doc(no_inline)]
pub use fd::AsyncFd;
#[derive(Debug)]
pub struct Ring {
cq: sys::Completions,
sq: sys::Submissions,
}
impl Ring {
pub const fn config<'r>() -> Config<'r> {
Config {
sys: crate::sys::Config::new(),
}
}
#[doc(alias = "io_uring_setup")]
#[doc(alias = "kqueue")]
pub fn new() -> io::Result<Ring> {
Ring::config().build()
}
pub fn sq(&self) -> SubmissionQueue {
SubmissionQueue(self.sq.clone())
}
#[doc(alias = "io_uring_enter")]
#[doc(alias = "kevent")]
pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<()> {
self.cq.poll(self.sq.shared(), timeout)
}
}
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct SubmissionQueue(sys::Submissions);
impl SubmissionQueue {
pub fn wake(&self) {
if let Err(err) = self.0.wake() {
log::warn!("failed to wake a10::Ring: {err}");
}
}
pub(crate) fn submissions(&self) -> &sys::Submissions {
&self.0
}
pub(crate) const fn sq(&self) -> &SubmissionQueue {
self
}
}
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)? ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($( $arg, )*) };
if res == -1 {
::std::result::Result::Err(::std::io::Error::last_os_error())
} else {
::std::result::Result::Ok(res)
}
}};
}
#[rustfmt::skip]
macro_rules! man_link {
($syscall: tt ( $section: tt ) ) => {
concat!(
"\n\nAdditional documentation can be found in the ",
"[`", stringify!($syscall), "(", stringify!($section), ")`]",
"(https://man7.org/linux/man-pages/man", stringify!($section), "/", stringify!($syscall), ".", stringify!($section), ".html)",
" manual.\n"
)
};
}
macro_rules! new_flag {
(
$(
$(#[$type_meta:meta])*
$type_vis: vis struct $type_name: ident ( $type_repr: ty ) $( impl BitOr $( $type_or: ty )* )? {
$(
$(#[$value_meta:meta])*
$value_name: ident = $libc: ident :: $value_type: ident,
)*
}
)+
) => {
$(
$(#[$type_meta])*
#[derive(Copy, Clone, Eq, PartialEq)]
$type_vis struct $type_name(pub(crate) $type_repr);
impl $type_name {
$(
$(#[$value_meta])*
#[allow(trivial_numeric_casts, clippy::cast_sign_loss)]
$type_vis const $value_name: $type_name = $type_name($libc::$value_type as $type_repr);
)*
#[allow(unused_doc_comments, dead_code)]
pub(crate) const ALL_VALUES: &[$type_name] = &[
$(
$(#[$value_meta])*
$type_name::$value_name,
)*
];
}
$crate::debug_detail!(impl match for $type_name($type_repr), $( $(#[$value_meta])* $libc::$value_type ),*);
$(
impl std::ops::BitOr for $type_name {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
$type_name(self.0 | rhs.0)
}
}
$(
impl std::ops::BitOr<$type_or> for $type_name {
type Output = Self;
#[allow(clippy::cast_sign_loss)]
fn bitor(self, rhs: $type_or) -> Self::Output {
$type_name(self.0 | rhs as $type_repr)
}
}
)*
)?
)+
};
}
macro_rules! debug_detail {
(
// Match a value exactly.
match $type: ident ($event_type: ty),
$( $( #[$meta: meta] )* $libc: ident :: $flag: ident ),+ $(,)?
) => {
struct $type($event_type);
$crate::debug_detail!(impl match for $type($event_type), $( $(#[$meta])* $libc::$flag ),*);
};
(
impl match for $type: ident ($type_repr: ty),
$( $( #[$meta: meta] )* $libc: ident :: $flag: ident ),* $(,)?
) => {
impl ::std::fmt::Debug for $type {
#[allow(trivial_numeric_casts, unreachable_patterns, unreachable_code, unused_doc_comments, clippy::bad_bit_mask)]
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
mod consts {
$(
$(#[$meta])*
pub(super) const $flag: $type_repr = $libc :: $flag as $type_repr;
)*
}
f.write_str(match self.0 {
$(
$(#[$meta])*
consts::$flag => stringify!($flag),
)*
value => return value.fmt(f),
})
}
}
};
(
bitset $type: ident ($event_type: ty),
$( $( #[$meta: meta] )* $libc: ident :: $flag: ident ),+ $(,)?
) => {
struct $type($event_type);
$crate::debug_detail!(impl bitset for $type($event_type), $( $(#[$meta])* $libc::$flag ),*);
};
(
impl bitset for $type: ident ($event_type: ty),
$( $( #[$meta: meta] )* $libc: ident :: $flag: ident ),+ $(,)?
) => {
impl ::std::fmt::Debug for $type {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let mut written_one = false;
$(
$(#[$meta])*
#[allow(clippy::bad_bit_mask)] {
if self.0 & $libc :: $flag != 0 {
if !written_one {
write!(f, "{}", stringify!($flag))?;
written_one = true;
} else {
write!(f, "|{}", stringify!($flag))?;
}
}
}
)+
if !written_one {
write!(f, "(empty)")
} else {
Ok(())
}
}
}
};
}
use {debug_detail, man_link, new_flag, syscall};
fn lock<'a, T>(mutex: &'a std::sync::Mutex<T>) -> std::sync::MutexGuard<'a, T> {
match mutex.lock() {
Ok(guard) => guard,
Err(err) => {
mutex.clear_poison();
err.into_inner()
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn try_lock<'a, T>(mutex: &'a std::sync::Mutex<T>) -> Option<std::sync::MutexGuard<'a, T>> {
match mutex.try_lock() {
Ok(guard) => Some(guard),
Err(std::sync::TryLockError::Poisoned(err)) => {
mutex.clear_poison();
Some(err.into_inner())
}
Err(std::sync::TryLockError::WouldBlock) => None,
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn get_mut<'a, T>(mutex: &'a mut std::sync::Mutex<T>) -> &'a mut T {
match mutex.get_mut() {
Ok(guard) => guard,
Err(err) => err.into_inner(),
}
}
#[allow(unused)]
trait OpPollResult<T> {
fn from_ok(ok: T) -> Self;
fn from_err(err: io::Error) -> Self;
fn from_res(res: io::Result<T>) -> Self;
fn done() -> Self;
}
impl<T> OpPollResult<T> for io::Result<T> {
fn from_ok(ok: T) -> Self {
Ok(ok)
}
fn from_err(err: io::Error) -> Self {
Err(err)
}
fn from_res(res: io::Result<T>) -> Self {
res
}
fn done() -> Self {
unreachable!()
}
}
impl<T> OpPollResult<T> for Option<io::Result<T>> {
fn from_ok(ok: T) -> Self {
Some(Ok(ok))
}
fn from_err(err: io::Error) -> Self {
Some(Err(err))
}
fn from_res(res: io::Result<T>) -> Self {
Some(res)
}
fn done() -> Self {
None
}
}