Skip to main content

gaea/os/
option.rs

1use std::fmt;
2use std::ops::BitOr;
3
4/// Option supplied when [registering] an [`Evented`] handle with [`OsQueue`].
5///
6/// For high level documentation on registering see [`OsQueue`].
7///
8/// [registering]: crate::os::OsQueue::register
9/// [`OsQueue`]: crate::os::OsQueue
10///
11/// # Difference
12///
13/// An [`Evented`] registration may request [edge-triggered], [level-triggered]
14/// or [oneshot] events. `Evented` handles registered with oneshot polling
15/// option will only receive a single event and then have to [reregister] too
16/// receive more events.
17///
18/// The difference between the edge-triggered and level-trigger can be described
19/// as follows. Supposed that the following scenario happens:
20///
21/// 1. A [`TcpStream`] is registered with `OsQueue`.
22/// 2. The socket receives 2kb of data.
23/// 3. A call to [`poll`] returns the id associated with the socket indicating
24///    readable readiness.
25/// 4. 1kb is read from the socket.
26/// 5. Another call to [`poll`] is made.
27///
28/// If when the socket was registered with `OsQueue`, and *edge*-triggered
29/// events were requested, then the call to [`poll`] done in step **5** will
30/// (probably) block despite there being another 1kb still present in the socket
31/// read buffer. The reason for this is that edge-triggered mode delivers events
32/// only when changes occur on the monitored [`Evented`]. So, in step *5* the
33/// caller might end up waiting for some data that is already present inside the
34/// socket buffer.
35///
36/// With edge-triggered events, operations **must** be performed on the
37/// `Evented` type until [`WouldBlock`] is returned. In other words, after
38/// receiving an event indicating readiness for a certain operation, one should
39/// assume that [`poll`] may never return another event for the same id and
40/// readiness until the operation returns [`WouldBlock`].
41///
42/// By contrast, when *level*-triggered notifications was requested, each call
43/// to [`poll`] will return an event for the socket as long as data remains in
44/// the socket buffer. Though generally, level-triggered events should be
45/// avoided if high performance is a concern.
46///
47/// Since even with edge-triggered events, multiple events can be generated upon
48/// receipt of multiple chunks of data, the caller has the option to set the
49/// oneshot flag. This tells `OsQueue` to disable the associated [`Evented`]
50/// after the event is returned from [`poll`], note that *disabled* and
51/// *deregistered* are not the same thing. Subsequent calls to [`poll`] will no
52/// longer include events for [`Evented`] handles that are disabled even if the
53/// readiness state changes. The handle can be re-enabled by calling
54/// [`reregister`]. When handles are disabled, internal resources used to
55/// monitor the handle are maintained until the handle is deregistered. This
56/// makes re-registering the handle a fast operation.
57///
58/// For example, in the following scenario:
59///
60/// 1. A [`TcpStream`] is registered with `OsQueue`.
61/// 2. The socket receives 2kb of data.
62/// 3. A call to [`poll`] returns the id associated with the socket indicating
63///    readable readiness.
64/// 4. 2kb is read from the socket.
65/// 5. Another call to read is issued and [`WouldBlock`] is returned
66/// 6. The socket receives another 2kb of data.
67/// 7. Another call to [`poll`] is made.
68///
69/// Assuming the socket was registered with `OsQueue` with the *oneshot* option,
70/// then the call to [`poll`] in step 7 would block. This is because, oneshot
71/// tells `OsQueue` to disable events for the socket after returning an event.
72///
73/// In order to receive the event for the data received in step 6, the socket
74/// would need to be reregistered using [`reregister`].
75///
76/// [`Evented`]: crate::os::Evented
77/// [edge-triggered]: crate::os::RegisterOption::EDGE
78/// [level-triggered]: crate::os::RegisterOption::LEVEL
79/// [oneshot]: crate::os::RegisterOption::ONESHOT
80/// [reregister]: crate::os::OsQueue::reregister
81/// [`TcpStream`]: crate::net::TcpStream
82/// [`poll`]: crate::poll
83/// [`WouldBlock`]: std::io::ErrorKind::WouldBlock
84/// [`reregister`]: crate::os::OsQueue::reregister
85///
86/// # Notes
87///
88/// It is not possible to combine edge and level triggers.
89#[derive(Copy, Clone, Eq, PartialEq)]
90#[repr(transparent)]
91pub struct RegisterOption(u8);
92
93// Level trigger is 0.
94const EDGE: u8    = 1;
95const ONESHOT: u8 = 1 << 1;
96
97impl RegisterOption {
98    /// Level-triggered notifications.
99    pub const LEVEL: RegisterOption = RegisterOption(0);
100
101    /// Edge-triggered notifications.
102    pub const EDGE: RegisterOption = RegisterOption(EDGE);
103
104    /// Oneshot notifications.
105    pub const ONESHOT: RegisterOption = RegisterOption(ONESHOT);
106
107    /// Returns true if the value includes level trigger.
108    #[inline]
109    pub const fn is_level(self) -> bool {
110        !self.is_edge()
111    }
112
113    /// Returns true if the value includes edge trigger.
114    #[inline]
115    pub const fn is_edge(self) -> bool {
116        self.0 & EDGE != 0
117    }
118
119    /// Returns true if the value includes oneshot notification.
120    #[inline]
121    pub const fn is_oneshot(self) -> bool {
122        self.0 & ONESHOT != 0
123    }
124}
125
126impl BitOr for RegisterOption {
127    type Output = Self;
128
129    fn bitor(self, rhs: Self) -> Self {
130        RegisterOption(self.0 | rhs.0)
131    }
132}
133
134impl fmt::Debug for RegisterOption {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        f.pad(match (self.is_edge(), self.is_oneshot()) {
137            (false, false) => "LEVEL",
138            (true, false) => "EDGE",
139            (false, true) => "LEVEL | ONESHOT",
140            (true, true) => "EDGE | ONESHOT",
141        })
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::os::RegisterOption;
148
149    #[test]
150    fn is_tests() {
151        assert!(RegisterOption::LEVEL.is_level());
152        assert!(!RegisterOption::LEVEL.is_edge());
153        assert!(!RegisterOption::LEVEL.is_oneshot());
154
155        assert!(!RegisterOption::EDGE.is_level());
156        assert!(RegisterOption::EDGE.is_edge());
157        assert!(!RegisterOption::EDGE.is_oneshot());
158
159        assert!(RegisterOption::ONESHOT.is_level());
160        assert!(!RegisterOption::ONESHOT.is_edge());
161        assert!(RegisterOption::ONESHOT.is_oneshot());
162    }
163
164    #[test]
165    fn bit_or() {
166        let opt = RegisterOption::LEVEL | RegisterOption::ONESHOT;
167        assert!(opt.is_level());
168        assert!(!opt.is_edge());
169        assert!(opt.is_oneshot());
170
171        let opt = RegisterOption::EDGE | RegisterOption::ONESHOT;
172        assert!(!opt.is_level());
173        assert!(opt.is_edge());
174        assert!(opt.is_oneshot());
175    }
176
177    #[test]
178    fn fmt_debug() {
179        assert_eq!(format!("{:?}", RegisterOption::LEVEL), "LEVEL");
180        assert_eq!(format!("{:?}", RegisterOption::EDGE), "EDGE");
181        assert_eq!(format!("{:?}", RegisterOption::ONESHOT), "LEVEL | ONESHOT");
182        assert_eq!(format!("{:?}", RegisterOption::LEVEL | RegisterOption::ONESHOT), "LEVEL | ONESHOT");
183        assert_eq!(format!("{:?}", RegisterOption::EDGE | RegisterOption::ONESHOT), "EDGE | ONESHOT");
184    }
185}