From 511ed2673095e1a1bbab067ba834e518bb546cef Mon Sep 17 00:00:00 2001 From: Folkert <folkert@folkertdev.nl> Date: Mon, 10 Jul 2023 22:39:42 +0200 Subject: [PATCH] Interest as usize --- tokio/src/io/interest.rs | 233 +++++++++++++++++++++++++-------------- 1 file changed, 151 insertions(+), 82 deletions(-) diff --git a/tokio/src/io/interest.rs b/tokio/src/io/interest.rs index b7659c076a5..c6de37b886e 100644 --- a/tokio/src/io/interest.rs +++ b/tokio/src/io/interest.rs @@ -5,65 +5,70 @@ use crate::io::ready::Ready; use std::fmt; use std::ops; +// These must be unique. +// same as mio +const READABLE: usize = 0b0001; +const WRITABLE: usize = 0b0010; +// The following are not available on all platforms. +#[cfg(target_os = "freebsd")] +const AIO: usize = 0b0100; +#[cfg(target_os = "freebsd")] +const LIO: usize = 0b1000; +#[cfg(any(target_os = "linux", target_os = "android"))] +const PRIORITY: usize = 0b0001_0000; +// error is available on all platforms, but behavior is platform-specific +// mio does not have this interest +const ERROR: usize = 0b0010_0000; + /// Readiness event interest. /// /// Specifies the readiness events the caller is interested in when awaiting on /// I/O resource readiness states. #[cfg_attr(docsrs, doc(cfg(feature = "net")))] #[derive(Clone, Copy, Eq, PartialEq)] -pub struct Interest { - mio: Option<mio::Interest>, - has_error_interest: bool, -} +pub struct Interest(usize); impl Interest { - const fn new(mio: mio::Interest) -> Self { - Self { - mio: Some(mio), - has_error_interest: false, - } - } - // The non-FreeBSD definitions in this block are active only when // building documentation. cfg_aio! { /// Interest for POSIX AIO. #[cfg(target_os = "freebsd")] - pub const AIO: Interest = Interest::new(mio::Interest::AIO); + pub const AIO: Interest = Interest(AIO); /// Interest for POSIX AIO. #[cfg(not(target_os = "freebsd"))] - pub const AIO: Interest = Interest::new(mio::Interest::READABLE); + pub const AIO: Interest = Interest(READABLE); /// Interest for POSIX AIO lio_listio events. #[cfg(target_os = "freebsd")] - pub const LIO: Interest = Interest::new(mio::Interest::LIO); + pub const LIO: Interest = Interest(LIO); /// Interest for POSIX AIO lio_listio events. #[cfg(not(target_os = "freebsd"))] - pub const LIO: Interest = Interest::new(mio::Interest::READABLE); + pub const LIO: Interest = Interest(READABLE); } /// Interest in all readable events. /// /// Readable interest includes read-closed events. - pub const READABLE: Interest = Interest::new(mio::Interest::READABLE); + pub const READABLE: Interest = Interest(READABLE); /// Interest in all writable events. /// /// Writable interest includes write-closed events. - pub const WRITABLE: Interest = Interest::new(mio::Interest::WRITABLE); + pub const WRITABLE: Interest = Interest(WRITABLE); /// Interest in error events. - pub const ERROR: Interest = Interest { - mio: None, - has_error_interest: true, - }; + /// + /// Passes error interest to the underlying OS selector. + /// Behavior is platform-specific, read your platform's documentation. + pub const ERROR: Interest = Interest(ERROR); /// Returns a `Interest` set representing priority completion interests. #[cfg(any(target_os = "linux", target_os = "android"))] #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))] - pub const PRIORITY: Interest = Interest::new(mio::Interest::PRIORITY); + pub const PRIORITY: Interest = Interest(PRIORITY); /// Returns true if the value includes readable interest. /// @@ -79,10 +84,7 @@ impl Interest { /// assert!(both.is_readable()); /// ``` pub const fn is_readable(self) -> bool { - match self.mio { - Some(mio) => mio.is_readable(), - None => false, - } + self.0 & READABLE != 0 } /// Returns true if the value includes writable interest. @@ -99,10 +101,34 @@ impl Interest { /// assert!(both.is_writable()); /// ``` pub const fn is_writable(self) -> bool { - match self.mio { - Some(mio) => mio.is_writable(), - None => false, - } + self.0 & WRITABLE != 0 + } + + /// Returns true if the value includes error interest. + /// + /// # Examples + /// + /// ``` + /// use tokio::io::Interest; + /// + /// assert!(Interest::ERROR.is_error()); + /// assert!(!Interest::WRITABLE.is_error()); + /// + /// let combined = Interest::READABLE | Interest::ERROR; + /// assert!(combined.is_error()); + /// ``` + pub const fn is_error(self) -> bool { + self.0 & ERROR != 0 + } + + #[cfg(target_os = "freebsd")] + const fn is_aio(self) -> bool { + self.0 & AIO != 0 + } + + #[cfg(target_os = "freebsd")] + const fn is_lio(self) -> bool { + self.0 & LIO != 0 } /// Returns true if the value includes priority interest. @@ -121,10 +147,7 @@ impl Interest { #[cfg(any(target_os = "linux", target_os = "android"))] #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))] pub const fn is_priority(self) -> bool { - match self.mio { - Some(mio) => mio.is_priority(), - None => false, - } + self.0 & PRIORITY != 0 } /// Add together two `Interest` values. @@ -141,44 +164,51 @@ impl Interest { /// assert!(BOTH.is_readable()); /// assert!(BOTH.is_writable()); pub const fn add(self, other: Interest) -> Interest { - Interest { - mio: match (self.mio, other.mio) { - (Some(a), Some(b)) => Some(a.add(b)), - (Some(c), None) | (None, Some(c)) => Some(c), - (None, None) => None, - }, - has_error_interest: self.has_error_interest || other.has_error_interest, - } - } - - /// Returns true if the value includes error interest. - /// - /// # Examples - /// - /// ``` - /// use tokio::io::Interest; - /// - /// assert!(Interest::ERROR.is_error()); - /// assert!(!Interest::WRITABLE.is_error()); - /// - /// let combined = Interest::READABLE | Interest::ERROR; - /// assert!(combined.is_error()); - /// ``` - pub const fn is_error(self) -> bool { - self.has_error_interest + Self(self.0 | other.0) } // This function must be crate-private to avoid exposing a `mio` dependency. - pub(crate) const fn to_mio(self) -> mio::Interest { - match self.mio { - Some(mio) => mio, - None => { - // error interest is implicit, and mio does not have a specific Interest::ERROR - // so we provide an arbitrary interest here, and filter out the this readiness - // in `Interest::mask` and `Ready::from_interest`. - mio::Interest::READABLE - } + pub(crate) fn to_mio(self) -> mio::Interest { + // mio does not allow and empty interest, so pick an arbitrary starting value + let mut mio = mio::Interest::READABLE; + + if self.is_readable() { + mio |= mio::Interest::READABLE; + } + + if self.is_writable() { + mio |= mio::Interest::WRITABLE; + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + if self.is_priority() { + mio |= mio::Interest::PRIORITY; + } + + #[cfg(target_os = "freebsd")] + if self.is_aio() { + mio |= mio::Interest::AIO; + } + + #[cfg(target_os = "freebsd")] + if self.is_lio() { + mio |= mio::Interest::LIO; + } + + // Finally remove the starting value unless either: + // + // - the readable interest is specified + // - the error interest is specified. There is no error interest in mio, because error + // events are always reported. But mio interests cannot be empty and one is needed + // just for the registeration. + // + // read readiness is filtered out in `Interest::mask` or `Ready::from_interest` if + // needed + if !self.is_readable() && !self.is_error() { + mio.remove(mio::Interest::READABLE); } + + mio } pub(crate) fn mask(self) -> Ready { @@ -211,22 +241,61 @@ impl ops::BitOrAssign for Interest { impl fmt::Debug for Interest { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.mio { - None => { - if self.has_error_interest { - write!(fmt, "ERROR") - } else { - Ok(()) - } + let mut separator = false; + + if self.is_readable() { + if separator { + write!(fmt, " | ")?; + } + write!(fmt, "READABLE")?; + separator = true; + } + + if self.is_writable() { + if separator { + write!(fmt, " | ")?; + } + write!(fmt, "WRITABLE")?; + separator = true; + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + if self.is_priority() { + if separator { + write!(fmt, " | ")?; + } + write!(fmt, "PRIORITY")?; + separator = true; + } + + #[cfg(target_os = "freebsd")] + if self.is_aio() { + if separator { + write!(fmt, " | ")?; + } + write!(fmt, "AIO")?; + separator = true; + } + + #[cfg(target_os = "freebsd")] + if self.is_lio() { + if separator { + write!(fmt, " | ")?; } - Some(mio) => { - mio.fmt(fmt)?; - if self.has_error_interest { - write!(fmt, " | ERROR") - } else { - Ok(()) - } + write!(fmt, "LIO")?; + separator = true; + } + + if self.is_error() { + if separator { + write!(fmt, " | ")?; } + write!(fmt, "ERROR")?; + separator = true; } + + let _ = separator; + + Ok(()) } }