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}