retty_io/sys/unix/
ready.rs

1use event_imp::{ready_as_usize, ready_from_usize, Ready};
2
3use std::fmt;
4use std::ops;
5
6/// Unix specific extensions to `Ready`
7///
8/// Provides additional readiness event kinds that are available on unix
9/// platforms. Unix platforms are able to provide readiness events for
10/// additional socket events, such as HUP and error.
11///
12/// HUP events occur when the remote end of a socket hangs up. In the TCP case,
13/// this occurs when the remote end of a TCP socket shuts down writes.
14///
15/// Error events occur when the socket enters an error state. In this case, the
16/// socket will also receive a readable or writable event. Reading or writing to
17/// the socket will result in an error.
18///
19/// Conversion traits are implemented between `Ready` and `UnixReady`. See the
20/// examples.
21///
22/// For high level documentation on polling and readiness, see [`Poll`].
23///
24/// # Examples
25///
26/// Most of the time, all that is needed is using bit operations
27///
28/// ```
29/// use retty_io::Ready;
30/// use retty_io::unix::UnixReady;
31///
32/// let ready = Ready::readable() | UnixReady::hup();
33///
34/// assert!(ready.is_readable());
35/// assert!(UnixReady::from(ready).is_hup());
36/// ```
37///
38/// Basic conversion between ready types.
39///
40/// ```
41/// use retty_io::Ready;
42/// use retty_io::unix::UnixReady;
43///
44/// // Start with a portable ready
45/// let ready = Ready::readable();
46///
47/// // Convert to a unix ready, adding HUP
48/// let mut unix_ready = UnixReady::from(ready) | UnixReady::hup();
49///
50/// unix_ready.insert(UnixReady::error());
51///
52/// // `unix_ready` maintains readable interest
53/// assert!(unix_ready.is_readable());
54/// assert!(unix_ready.is_hup());
55/// assert!(unix_ready.is_error());
56///
57/// // Convert back to `Ready`
58/// let ready = Ready::from(unix_ready);
59///
60/// // Readable is maintained
61/// assert!(ready.is_readable());
62/// ```
63///
64/// Registering readable and error interest on a socket
65///
66/// ```
67/// # use std::error::Error;
68/// # fn try_main() -> Result<(), Box<Error>> {
69/// use retty_io::{Ready, Poll, PollOpt, Token};
70/// use retty_io::net::TcpStream;
71/// use retty_io::unix::UnixReady;
72///
73/// let addr = "216.58.193.68:80".parse()?;
74/// let socket = TcpStream::connect(&addr)?;
75///
76/// let poll = Poll::new()?;
77///
78/// poll.register(&socket,
79///               Token(0),
80///               Ready::readable() | UnixReady::error(),
81///               PollOpt::edge())?;
82/// #     Ok(())
83/// # }
84/// #
85/// # fn main() {
86/// #     try_main().unwrap();
87/// # }
88/// ```
89///
90/// [`Poll`]: ../struct.Poll.html
91/// [readiness]: struct.Poll.html#readiness-operations
92#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
93pub struct UnixReady(Ready);
94
95const ERROR: usize = 0b00_0100;
96const HUP: usize = 0b00_1000;
97
98#[cfg(any(
99    target_os = "dragonfly",
100    target_os = "freebsd",
101    target_os = "ios",
102    target_os = "macos"
103))]
104const AIO: usize = 0b01_0000;
105
106#[cfg(not(any(
107    target_os = "dragonfly",
108    target_os = "freebsd",
109    target_os = "ios",
110    target_os = "macos"
111)))]
112const AIO: usize = 0b00_0000;
113
114#[cfg(any(target_os = "freebsd"))]
115const LIO: usize = 0b10_0000;
116
117#[cfg(not(any(target_os = "freebsd")))]
118const LIO: usize = 0b00_0000;
119
120#[cfg(any(
121    target_os = "android",
122    target_os = "illumos",
123    target_os = "linux",
124    target_os = "solaris"
125))]
126const PRI: usize = 0b100_0000;
127
128#[cfg(not(any(
129    target_os = "android",
130    target_os = "illumos",
131    target_os = "linux",
132    target_os = "solaris"
133)))]
134const PRI: usize = 0;
135
136// Export to support `Ready::all`
137pub const READY_ALL: usize = ERROR | HUP | AIO | LIO | PRI;
138
139#[test]
140fn test_ready_all() {
141    let readable = Ready::readable().as_usize();
142    let writable = Ready::writable().as_usize();
143
144    assert_eq!(
145        READY_ALL | readable | writable,
146        ERROR + HUP + AIO + LIO + PRI + readable + writable
147    );
148
149    // Issue #896.
150    #[cfg(any(
151        target_os = "android",
152        target_os = "illumos",
153        target_os = "linux",
154        target_os = "solaris"
155    ))]
156    assert!(!Ready::from(UnixReady::priority()).is_writable());
157}
158
159impl UnixReady {
160    /// Returns a `Ready` representing AIO completion readiness
161    ///
162    /// See [`Poll`] for more documentation on polling.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// use retty_io::unix::UnixReady;
168    ///
169    /// let ready = UnixReady::aio();
170    ///
171    /// assert!(ready.is_aio());
172    /// ```
173    ///
174    /// [`Poll`]: ../struct.Poll.html
175    #[inline]
176    #[cfg(any(
177        target_os = "dragonfly",
178        target_os = "freebsd",
179        target_os = "ios",
180        target_os = "macos"
181    ))]
182    pub fn aio() -> UnixReady {
183        UnixReady(ready_from_usize(AIO))
184    }
185
186    #[cfg(not(any(
187        target_os = "dragonfly",
188        target_os = "freebsd",
189        target_os = "ios",
190        target_os = "macos"
191    )))]
192    #[deprecated(since = "0.6.12", note = "this function is now platform specific")]
193    #[doc(hidden)]
194    pub fn aio() -> UnixReady {
195        UnixReady(Ready::empty())
196    }
197
198    /// Returns a `Ready` representing error readiness.
199    ///
200    /// **Note that only readable and writable readiness is guaranteed to be
201    /// supported on all platforms**. This means that `error` readiness
202    /// should be treated as a hint. For more details, see [readiness] in the
203    /// poll documentation.
204    ///
205    /// See [`Poll`] for more documentation on polling.
206    ///
207    /// # Examples
208    ///
209    /// ```
210    /// use retty_io::unix::UnixReady;
211    ///
212    /// let ready = UnixReady::error();
213    ///
214    /// assert!(ready.is_error());
215    /// ```
216    ///
217    /// [`Poll`]: ../struct.Poll.html
218    /// [readiness]: ../struct.Poll.html#readiness-operations
219    #[inline]
220    pub fn error() -> UnixReady {
221        UnixReady(ready_from_usize(ERROR))
222    }
223
224    /// Returns a `Ready` representing HUP readiness.
225    ///
226    /// A HUP (or hang-up) signifies that a stream socket **peer** closed the
227    /// connection, or shut down the writing half of the connection.
228    ///
229    /// **Note that only readable and writable readiness is guaranteed to be
230    /// supported on all platforms**. This means that `hup` readiness
231    /// should be treated as a hint. For more details, see [readiness] in the
232    /// poll documentation. It is also unclear if HUP readiness will remain in 0.7. See
233    /// [here][issue-941].
234    ///
235    /// See [`Poll`] for more documentation on polling.
236    ///
237    /// # Examples
238    ///
239    /// ```
240    /// use retty_io::unix::UnixReady;
241    ///
242    /// let ready = UnixReady::hup();
243    ///
244    /// assert!(ready.is_hup());
245    /// ```
246    ///
247    /// [`Poll`]: ../struct.Poll.html
248    /// [readiness]: ../struct.Poll.html#readiness-operations
249    /// [issue-941]: https://github.com/tokio-rs/retty-io/issues/941
250    #[inline]
251    pub fn hup() -> UnixReady {
252        UnixReady(ready_from_usize(HUP))
253    }
254
255    /// Returns a `Ready` representing LIO completion readiness
256    ///
257    /// See [`Poll`] for more documentation on polling.
258    ///
259    /// # Examples
260    ///
261    /// ```
262    /// use retty_io::unix::UnixReady;
263    ///
264    /// let ready = UnixReady::lio();
265    ///
266    /// assert!(ready.is_lio());
267    /// ```
268    ///
269    /// [`Poll`]: struct.Poll.html
270    #[inline]
271    #[cfg(any(target_os = "freebsd"))]
272    pub fn lio() -> UnixReady {
273        UnixReady(ready_from_usize(LIO))
274    }
275
276    /// Returns a `Ready` representing priority (`EPOLLPRI`) readiness
277    ///
278    /// See [`Poll`] for more documentation on polling.
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use retty_io::unix::UnixReady;
284    ///
285    /// let ready = UnixReady::priority();
286    ///
287    /// assert!(ready.is_priority());
288    /// ```
289    ///
290    /// [`Poll`]: struct.Poll.html
291    #[inline]
292    #[cfg(any(
293        target_os = "android",
294        target_os = "illumos",
295        target_os = "linux",
296        target_os = "solaris"
297    ))]
298    pub fn priority() -> UnixReady {
299        UnixReady(ready_from_usize(PRI))
300    }
301
302    /// Returns true if `Ready` contains AIO readiness
303    ///
304    /// See [`Poll`] for more documentation on polling.
305    ///
306    /// # Examples
307    ///
308    /// ```
309    /// use retty_io::unix::UnixReady;
310    ///
311    /// let ready = UnixReady::aio();
312    ///
313    /// assert!(ready.is_aio());
314    /// ```
315    ///
316    /// [`Poll`]: ../struct.Poll.html
317    #[inline]
318    #[cfg(any(
319        target_os = "dragonfly",
320        target_os = "freebsd",
321        target_os = "ios",
322        target_os = "macos"
323    ))]
324    pub fn is_aio(&self) -> bool {
325        self.contains(ready_from_usize(AIO))
326    }
327
328    #[deprecated(since = "0.6.12", note = "this function is now platform specific")]
329    #[cfg(feature = "with-deprecated")]
330    #[cfg(not(any(
331        target_os = "dragonfly",
332        target_os = "freebsd",
333        target_os = "ios",
334        target_os = "macos"
335    )))]
336    #[doc(hidden)]
337    pub fn is_aio(&self) -> bool {
338        false
339    }
340
341    /// Returns true if the value includes error readiness
342    ///
343    /// **Note that only readable and writable readiness is guaranteed to be
344    /// supported on all platforms**. This means that `error` readiness should
345    /// be treated as a hint. For more details, see [readiness] in the poll
346    /// documentation.
347    ///
348    /// See [`Poll`] for more documentation on polling.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use retty_io::unix::UnixReady;
354    ///
355    /// let ready = UnixReady::error();
356    ///
357    /// assert!(ready.is_error());
358    /// ```
359    ///
360    /// [`Poll`]: ../struct.Poll.html
361    /// [readiness]: ../struct.Poll.html#readiness-operations
362    #[inline]
363    pub fn is_error(&self) -> bool {
364        self.contains(ready_from_usize(ERROR))
365    }
366
367    /// Returns true if the value includes HUP readiness
368    ///
369    /// A HUP (or hang-up) signifies that a stream socket **peer** closed the
370    /// connection, or shut down the writing half of the connection.
371    ///
372    /// **Note that only readable and writable readiness is guaranteed to be
373    /// supported on all platforms**. This means that `hup` readiness
374    /// should be treated as a hint. For more details, see [readiness] in the
375    /// poll documentation.
376    ///
377    /// See [`Poll`] for more documentation on polling.
378    ///
379    /// # Examples
380    ///
381    /// ```
382    /// use retty_io::unix::UnixReady;
383    ///
384    /// let ready = UnixReady::hup();
385    ///
386    /// assert!(ready.is_hup());
387    /// ```
388    ///
389    /// [`Poll`]: ../struct.Poll.html
390    /// [readiness]: ../struct.Poll.html#readiness-operations
391    #[inline]
392    pub fn is_hup(&self) -> bool {
393        self.contains(ready_from_usize(HUP))
394    }
395
396    /// Returns true if `Ready` contains LIO readiness
397    ///
398    /// See [`Poll`] for more documentation on polling.
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// use retty_io::unix::UnixReady;
404    ///
405    /// let ready = UnixReady::lio();
406    ///
407    /// assert!(ready.is_lio());
408    /// ```
409    #[inline]
410    #[cfg(any(target_os = "freebsd"))]
411    pub fn is_lio(&self) -> bool {
412        self.contains(ready_from_usize(LIO))
413    }
414
415    /// Returns true if `Ready` contains priority (`EPOLLPRI`) readiness
416    ///
417    /// See [`Poll`] for more documentation on polling.
418    ///
419    /// # Examples
420    ///
421    /// ```
422    /// use retty_io::unix::UnixReady;
423    ///
424    /// let ready = UnixReady::priority();
425    ///
426    /// assert!(ready.is_priority());
427    /// ```
428    ///
429    /// [`Poll`]: struct.Poll.html
430    #[inline]
431    #[cfg(any(
432        target_os = "android",
433        target_os = "illumos",
434        target_os = "linux",
435        target_os = "solaris"
436    ))]
437    pub fn is_priority(&self) -> bool {
438        self.contains(ready_from_usize(PRI))
439    }
440}
441
442impl From<Ready> for UnixReady {
443    fn from(src: Ready) -> UnixReady {
444        UnixReady(src)
445    }
446}
447
448impl From<UnixReady> for Ready {
449    fn from(src: UnixReady) -> Ready {
450        src.0
451    }
452}
453
454impl ops::Deref for UnixReady {
455    type Target = Ready;
456
457    fn deref(&self) -> &Ready {
458        &self.0
459    }
460}
461
462impl ops::DerefMut for UnixReady {
463    fn deref_mut(&mut self) -> &mut Ready {
464        &mut self.0
465    }
466}
467
468impl ops::BitOr for UnixReady {
469    type Output = UnixReady;
470
471    #[inline]
472    fn bitor(self, other: UnixReady) -> UnixReady {
473        (self.0 | other.0).into()
474    }
475}
476
477impl ops::BitXor for UnixReady {
478    type Output = UnixReady;
479
480    #[inline]
481    fn bitxor(self, other: UnixReady) -> UnixReady {
482        (self.0 ^ other.0).into()
483    }
484}
485
486impl ops::BitAnd for UnixReady {
487    type Output = UnixReady;
488
489    #[inline]
490    fn bitand(self, other: UnixReady) -> UnixReady {
491        (self.0 & other.0).into()
492    }
493}
494
495impl ops::Sub for UnixReady {
496    type Output = UnixReady;
497
498    #[inline]
499    fn sub(self, other: UnixReady) -> UnixReady {
500        ready_from_usize(ready_as_usize(self.0) & !ready_as_usize(other.0)).into()
501    }
502}
503
504#[cfg(feature = "with-deprecated")]
505#[doc(hidden)]
506impl ops::Not for UnixReady {
507    type Output = UnixReady;
508
509    #[inline]
510    fn not(self) -> UnixReady {
511        (!self.0).into()
512    }
513}
514
515impl fmt::Debug for UnixReady {
516    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
517        let mut one = false;
518        let flags = [
519            (UnixReady(Ready::readable()), "Readable"),
520            (UnixReady(Ready::writable()), "Writable"),
521            (UnixReady::error(), "Error"),
522            (UnixReady::hup(), "Hup"),
523            #[allow(deprecated)]
524            (UnixReady::aio(), "Aio"),
525            #[cfg(any(
526                target_os = "android",
527                target_os = "illumos",
528                target_os = "linux",
529                target_os = "solaris"
530            ))]
531            (UnixReady::priority(), "Priority"),
532        ];
533
534        for &(flag, msg) in &flags {
535            if self.contains(flag) {
536                if one {
537                    write!(fmt, " | ")?
538                }
539                write!(fmt, "{}", msg)?;
540
541                one = true
542            }
543        }
544
545        if !one {
546            fmt.write_str("(empty)")?;
547        }
548
549        Ok(())
550    }
551}