#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))] 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(usize); impl Interest { // 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(AIO); /// Interest for POSIX AIO. #[cfg(not(target_os = "freebsd"))] pub const AIO: Interest = Interest(READABLE); /// Interest for POSIX AIO `lio_listio` events. #[cfg(target_os = "freebsd")] pub const LIO: Interest = Interest(LIO); /// Interest for POSIX AIO `lio_listio` events. #[cfg(not(target_os = "freebsd"))] pub const LIO: Interest = Interest(READABLE); } /// Interest in all readable events. /// /// Readable interest includes read-closed events. pub const READABLE: Interest = Interest(READABLE); /// Interest in all writable events. /// /// Writable interest includes write-closed events. pub const WRITABLE: Interest = Interest(WRITABLE); /// Interest in error events. /// /// 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(PRIORITY); /// Returns true if the value includes readable interest. /// /// # Examples /// /// ``` /// use tokio::io::Interest; /// /// assert!(Interest::READABLE.is_readable()); /// assert!(!Interest::WRITABLE.is_readable()); /// /// let both = Interest::READABLE | Interest::WRITABLE; /// assert!(both.is_readable()); /// ``` pub const fn is_readable(self) -> bool { self.0 & READABLE != 0 } /// Returns true if the value includes writable interest. /// /// # Examples /// /// ``` /// use tokio::io::Interest; /// /// assert!(!Interest::READABLE.is_writable()); /// assert!(Interest::WRITABLE.is_writable()); /// /// let both = Interest::READABLE | Interest::WRITABLE; /// assert!(both.is_writable()); /// ``` pub const fn is_writable(self) -> bool { 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. /// /// # Examples /// /// ``` /// use tokio::io::Interest; /// /// assert!(!Interest::READABLE.is_priority()); /// assert!(Interest::PRIORITY.is_priority()); /// /// let both = Interest::READABLE | Interest::PRIORITY; /// assert!(both.is_priority()); /// ``` #[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 { self.0 & PRIORITY != 0 } /// Add together two `Interest` values. /// /// This function works from a `const` context. /// /// # Examples /// /// ``` /// use tokio::io::Interest; /// /// const BOTH: Interest = Interest::READABLE.add(Interest::WRITABLE); /// /// assert!(BOTH.is_readable()); /// assert!(BOTH.is_writable()); #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn add(self, other: Interest) -> Interest { Self(self.0 | other.0) } /// Remove `Interest` from `self`. /// /// Interests present in `other` but *not* in `self` are ignored. /// /// Returns `None` if the set would be empty after removing `Interest`. /// /// # Examples /// /// ``` /// use tokio::io::Interest; /// /// const RW_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE); /// /// let w_interest = RW_INTEREST.remove(Interest::READABLE).unwrap(); /// assert!(!w_interest.is_readable()); /// assert!(w_interest.is_writable()); /// /// // Removing all interests from the set returns `None`. /// assert_eq!(w_interest.remove(Interest::WRITABLE), None); /// /// // Remove all interests at once. /// assert_eq!(RW_INTEREST.remove(RW_INTEREST), None); /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] pub fn remove(self, other: Interest) -> Option { let value = self.0 & !other.0; if value != 0 { Some(Self(value)) } else { None } } // This function must be crate-private to avoid exposing a `mio` dependency. pub(crate) fn to_mio(self) -> mio::Interest { fn mio_add(wrapped: &mut Option, add: mio::Interest) { match wrapped { Some(inner) => *inner |= add, None => *wrapped = Some(add), } } // mio does not allow and empty interest, so use None for empty let mut mio = None; if self.is_readable() { mio_add(&mut mio, mio::Interest::READABLE); } if self.is_writable() { mio_add(&mut mio, mio::Interest::WRITABLE); } #[cfg(any(target_os = "linux", target_os = "android"))] if self.is_priority() { mio_add(&mut mio, mio::Interest::PRIORITY); } #[cfg(target_os = "freebsd")] if self.is_aio() { mio_add(&mut mio, mio::Interest::AIO); } #[cfg(target_os = "freebsd")] if self.is_lio() { mio_add(&mut mio, mio::Interest::LIO); } if self.is_error() { // There is no error interest in mio, because error events are always reported. // But mio interests cannot be empty and an interest is needed just for the registration. // // read readiness is filtered out in `Interest::mask` or `Ready::from_interest` if // the read interest was not specified by the user. mio_add(&mut mio, mio::Interest::READABLE); } // the default `mio::Interest::READABLE` should never be used in practice. Either // // - at least one tokio interest with a mio counterpart was used // - only the error tokio interest was specified // // in both cases, `mio` is Some already mio.unwrap_or(mio::Interest::READABLE) } pub(crate) fn mask(self) -> Ready { match self { Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED, Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_CLOSED, #[cfg(any(target_os = "linux", target_os = "android"))] Interest::PRIORITY => Ready::PRIORITY | Ready::READ_CLOSED, Interest::ERROR => Ready::ERROR, _ => Ready::EMPTY, } } } impl ops::BitOr for Interest { type Output = Self; #[inline] fn bitor(self, other: Self) -> Self { self.add(other) } } impl ops::BitOrAssign for Interest { #[inline] fn bitor_assign(&mut self, other: Self) { *self = *self | other; } } impl fmt::Debug for Interest { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 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, " | ")?; } write!(fmt, "LIO")?; separator = true; } if self.is_error() { if separator { write!(fmt, " | ")?; } write!(fmt, "ERROR")?; separator = true; } let _ = separator; Ok(()) } }