tokio/io/
interest.rs

1#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))]
2
3use crate::io::ready::Ready;
4
5use std::fmt;
6use std::ops;
7
8// These must be unique.
9// same as mio
10const READABLE: usize = 0b0001;
11const WRITABLE: usize = 0b0010;
12// The following are not available on all platforms.
13#[cfg(target_os = "freebsd")]
14const AIO: usize = 0b0100;
15#[cfg(target_os = "freebsd")]
16const LIO: usize = 0b1000;
17#[cfg(any(target_os = "linux", target_os = "android"))]
18const PRIORITY: usize = 0b0001_0000;
19// error is available on all platforms, but behavior is platform-specific
20// mio does not have this interest
21const ERROR: usize = 0b0010_0000;
22
23/// Readiness event interest.
24///
25/// Specifies the readiness events the caller is interested in when awaiting on
26/// I/O resource readiness states.
27#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
28#[derive(Clone, Copy, Eq, PartialEq)]
29pub struct Interest(usize);
30
31impl Interest {
32    // The non-FreeBSD definitions in this block are active only when
33    // building documentation.
34    cfg_aio! {
35        /// Interest for POSIX AIO.
36        #[cfg(target_os = "freebsd")]
37        pub const AIO: Interest = Interest(AIO);
38
39        /// Interest for POSIX AIO.
40        #[cfg(not(target_os = "freebsd"))]
41        pub const AIO: Interest = Interest(READABLE);
42
43        /// Interest for POSIX AIO `lio_listio` events.
44        #[cfg(target_os = "freebsd")]
45        pub const LIO: Interest = Interest(LIO);
46
47        /// Interest for POSIX AIO `lio_listio` events.
48        #[cfg(not(target_os = "freebsd"))]
49        pub const LIO: Interest = Interest(READABLE);
50    }
51
52    /// Interest in all readable events.
53    ///
54    /// Readable interest includes read-closed events.
55    pub const READABLE: Interest = Interest(READABLE);
56
57    /// Interest in all writable events.
58    ///
59    /// Writable interest includes write-closed events.
60    pub const WRITABLE: Interest = Interest(WRITABLE);
61
62    /// Interest in error events.
63    ///
64    /// Passes error interest to the underlying OS selector.
65    /// Behavior is platform-specific, read your platform's documentation.
66    pub const ERROR: Interest = Interest(ERROR);
67
68    /// Returns a `Interest` set representing priority completion interests.
69    #[cfg(any(target_os = "linux", target_os = "android"))]
70    #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
71    pub const PRIORITY: Interest = Interest(PRIORITY);
72
73    /// Returns true if the value includes readable interest.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use tokio::io::Interest;
79    ///
80    /// assert!(Interest::READABLE.is_readable());
81    /// assert!(!Interest::WRITABLE.is_readable());
82    ///
83    /// let both = Interest::READABLE | Interest::WRITABLE;
84    /// assert!(both.is_readable());
85    /// ```
86    pub const fn is_readable(self) -> bool {
87        self.0 & READABLE != 0
88    }
89
90    /// Returns true if the value includes writable interest.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use tokio::io::Interest;
96    ///
97    /// assert!(!Interest::READABLE.is_writable());
98    /// assert!(Interest::WRITABLE.is_writable());
99    ///
100    /// let both = Interest::READABLE | Interest::WRITABLE;
101    /// assert!(both.is_writable());
102    /// ```
103    pub const fn is_writable(self) -> bool {
104        self.0 & WRITABLE != 0
105    }
106
107    /// Returns true if the value includes error interest.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use tokio::io::Interest;
113    ///
114    /// assert!(Interest::ERROR.is_error());
115    /// assert!(!Interest::WRITABLE.is_error());
116    ///
117    /// let combined = Interest::READABLE | Interest::ERROR;
118    /// assert!(combined.is_error());
119    /// ```
120    pub const fn is_error(self) -> bool {
121        self.0 & ERROR != 0
122    }
123
124    #[cfg(target_os = "freebsd")]
125    const fn is_aio(self) -> bool {
126        self.0 & AIO != 0
127    }
128
129    #[cfg(target_os = "freebsd")]
130    const fn is_lio(self) -> bool {
131        self.0 & LIO != 0
132    }
133
134    /// Returns true if the value includes priority interest.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use tokio::io::Interest;
140    ///
141    /// assert!(!Interest::READABLE.is_priority());
142    /// assert!(Interest::PRIORITY.is_priority());
143    ///
144    /// let both = Interest::READABLE | Interest::PRIORITY;
145    /// assert!(both.is_priority());
146    /// ```
147    #[cfg(any(target_os = "linux", target_os = "android"))]
148    #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))]
149    pub const fn is_priority(self) -> bool {
150        self.0 & PRIORITY != 0
151    }
152
153    /// Add together two `Interest` values.
154    ///
155    /// This function works from a `const` context.
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// use tokio::io::Interest;
161    ///
162    /// const BOTH: Interest = Interest::READABLE.add(Interest::WRITABLE);
163    ///
164    /// assert!(BOTH.is_readable());
165    /// assert!(BOTH.is_writable());
166    #[must_use = "this returns the result of the operation, without modifying the original"]
167    pub const fn add(self, other: Interest) -> Interest {
168        Self(self.0 | other.0)
169    }
170
171    /// Remove `Interest` from `self`.
172    ///
173    /// Interests present in `other` but *not* in `self` are ignored.
174    ///
175    /// Returns `None` if the set would be empty after removing `Interest`.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use tokio::io::Interest;
181    ///
182    /// const RW_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
183    ///
184    /// let w_interest = RW_INTEREST.remove(Interest::READABLE).unwrap();
185    /// assert!(!w_interest.is_readable());
186    /// assert!(w_interest.is_writable());
187    ///
188    /// // Removing all interests from the set returns `None`.
189    /// assert_eq!(w_interest.remove(Interest::WRITABLE), None);
190    ///
191    /// // Remove all interests at once.
192    /// assert_eq!(RW_INTEREST.remove(RW_INTEREST), None);
193    /// ```
194    #[must_use = "this returns the result of the operation, without modifying the original"]
195    pub fn remove(self, other: Interest) -> Option<Interest> {
196        let value = self.0 & !other.0;
197
198        if value != 0 {
199            Some(Self(value))
200        } else {
201            None
202        }
203    }
204
205    // This function must be crate-private to avoid exposing a `mio` dependency.
206    pub(crate) fn to_mio(self) -> mio::Interest {
207        fn mio_add(wrapped: &mut Option<mio::Interest>, add: mio::Interest) {
208            match wrapped {
209                Some(inner) => *inner |= add,
210                None => *wrapped = Some(add),
211            }
212        }
213
214        // mio does not allow and empty interest, so use None for empty
215        let mut mio = None;
216
217        if self.is_readable() {
218            mio_add(&mut mio, mio::Interest::READABLE);
219        }
220
221        if self.is_writable() {
222            mio_add(&mut mio, mio::Interest::WRITABLE);
223        }
224
225        #[cfg(any(target_os = "linux", target_os = "android"))]
226        if self.is_priority() {
227            mio_add(&mut mio, mio::Interest::PRIORITY);
228        }
229
230        #[cfg(target_os = "freebsd")]
231        if self.is_aio() {
232            mio_add(&mut mio, mio::Interest::AIO);
233        }
234
235        #[cfg(target_os = "freebsd")]
236        if self.is_lio() {
237            mio_add(&mut mio, mio::Interest::LIO);
238        }
239
240        if self.is_error() {
241            // There is no error interest in mio, because error events are always reported.
242            // But mio interests cannot be empty and an interest is needed just for the registration.
243            //
244            // read readiness is filtered out in `Interest::mask` or `Ready::from_interest` if
245            // the read interest was not specified by the user.
246            mio_add(&mut mio, mio::Interest::READABLE);
247        }
248
249        // the default `mio::Interest::READABLE` should never be used in practice. Either
250        //
251        // - at least one tokio interest with a mio counterpart was used
252        // - only the error tokio interest was specified
253        //
254        // in both cases, `mio` is Some already
255        mio.unwrap_or(mio::Interest::READABLE)
256    }
257
258    pub(crate) fn mask(self) -> Ready {
259        match self {
260            Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED,
261            Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_CLOSED,
262            #[cfg(any(target_os = "linux", target_os = "android"))]
263            Interest::PRIORITY => Ready::PRIORITY | Ready::READ_CLOSED,
264            Interest::ERROR => Ready::ERROR,
265            _ => Ready::EMPTY,
266        }
267    }
268}
269
270impl ops::BitOr for Interest {
271    type Output = Self;
272
273    #[inline]
274    fn bitor(self, other: Self) -> Self {
275        self.add(other)
276    }
277}
278
279impl ops::BitOrAssign for Interest {
280    #[inline]
281    fn bitor_assign(&mut self, other: Self) {
282        *self = *self | other;
283    }
284}
285
286impl fmt::Debug for Interest {
287    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
288        let mut separator = false;
289
290        if self.is_readable() {
291            if separator {
292                write!(fmt, " | ")?;
293            }
294            write!(fmt, "READABLE")?;
295            separator = true;
296        }
297
298        if self.is_writable() {
299            if separator {
300                write!(fmt, " | ")?;
301            }
302            write!(fmt, "WRITABLE")?;
303            separator = true;
304        }
305
306        #[cfg(any(target_os = "linux", target_os = "android"))]
307        if self.is_priority() {
308            if separator {
309                write!(fmt, " | ")?;
310            }
311            write!(fmt, "PRIORITY")?;
312            separator = true;
313        }
314
315        #[cfg(target_os = "freebsd")]
316        if self.is_aio() {
317            if separator {
318                write!(fmt, " | ")?;
319            }
320            write!(fmt, "AIO")?;
321            separator = true;
322        }
323
324        #[cfg(target_os = "freebsd")]
325        if self.is_lio() {
326            if separator {
327                write!(fmt, " | ")?;
328            }
329            write!(fmt, "LIO")?;
330            separator = true;
331        }
332
333        if self.is_error() {
334            if separator {
335                write!(fmt, " | ")?;
336            }
337            write!(fmt, "ERROR")?;
338            separator = true;
339        }
340
341        let _ = separator;
342
343        Ok(())
344    }
345}