1use linux_api::epoll::EpollEvents;
2
3use crate::host::descriptor::listener::StateListenHandle;
4use crate::host::descriptor::{FileSignals, FileState};
5
6pub(super) struct Entry {
10 priority: Option<u64>,
12 interest: EpollEvents,
14 data: u64,
16 listener_handle: Option<StateListenHandle>,
18 state: FileState,
20 collected: FileState,
23 is_legacy: bool,
25}
26
27impl Entry {
28 pub fn new(interest: EpollEvents, data: u64, state: FileState) -> Self {
29 Self {
30 priority: None,
31 interest,
32 data,
33 listener_handle: None,
34 state,
35 collected: FileState::empty(),
36 is_legacy: false,
37 }
38 }
39
40 pub fn set_legacy(&mut self) {
42 self.is_legacy = true;
43 }
44
45 pub fn modify(&mut self, interest: EpollEvents, data: u64, state: FileState) {
52 log::trace!("Reset old state {:?}, new state {:?}", self.state, state);
53 self.interest = interest;
54 self.data = data;
55 self.state = state;
56 self.collected = FileState::empty();
57 }
58
59 pub fn set_priority(&mut self, priority: Option<u64>) {
60 self.priority = priority;
61 }
62
63 pub fn priority(&self) -> Option<u64> {
64 self.priority
65 }
66
67 pub fn notify(&mut self, new_state: FileState, changed: FileState, signals: FileSignals) {
68 log::trace!(
69 "Notify old state {:?}, new state {:?}, changed {:?}, signals {:?}",
70 self.state,
71 new_state,
72 changed,
73 signals,
74 );
75 self.state = new_state;
76 self.collected.remove(changed);
77
78 if signals.contains(FileSignals::READ_BUFFER_GREW) {
80 debug_assert!(self.interest.intersects(EpollEvents::EPOLLET));
82
83 if new_state.contains(FileState::READABLE) {
85 self.collected.remove(FileState::READABLE);
86 } else {
87 warn_once_then_debug!("Epoll received READ_BUFFER_GREW but state is not READABLE");
90 }
91 }
92 }
93
94 pub fn get_listener_state(&self) -> FileState {
95 if self.is_legacy {
97 return FileState::all();
98 }
99
100 Self::state_from_events(self.interest).union(FileState::CLOSED)
102 }
103
104 pub fn get_listener_signals(&self) -> FileSignals {
105 let mut signals = FileSignals::empty();
106
107 if self.interest.intersects(EpollEvents::EPOLLET) {
108 signals.insert(FileSignals::READ_BUFFER_GREW);
109 }
110
111 signals
112 }
113
114 pub fn set_listener_handle(&mut self, handle: Option<StateListenHandle>) {
115 self.listener_handle = handle;
116 }
117
118 pub fn has_ready_events(&self) -> bool {
119 if self.is_legacy {
121 if self.state.contains(FileState::CLOSED) {
122 return false;
123 } else if self.state.contains(FileState::ACTIVE) {
124 return !self.get_ready_events().is_empty();
125 } else {
126 return false;
127 }
128 }
129
130 !self.state.contains(FileState::CLOSED) && !self.get_ready_events().is_empty()
131 }
132
133 pub fn collect_ready_events(&mut self) -> Option<(EpollEvents, u64)> {
134 let events = self.get_ready_events();
135
136 if events.is_empty() {
137 return None;
138 }
139
140 self.collected.insert(Self::state_from_events(events));
141
142 if self.interest.contains(EpollEvents::EPOLLONESHOT) {
143 self.interest.remove(events)
144 }
145
146 log::trace!(
147 "Collected ready events {events:?} interest {:?} state {:?}",
148 self.interest,
149 self.state
150 );
151
152 Some((events, self.data))
153 }
154
155 fn get_ready_events(&self) -> EpollEvents {
156 let events = Self::events_from_state(self.get_ready_state());
157 self.interest.intersection(events)
158 }
159
160 fn get_ready_state(&self) -> FileState {
161 if self.interest.contains(EpollEvents::EPOLLET) {
162 self.state.difference(self.collected)
164 } else {
165 self.state
167 }
168 }
169
170 fn events_from_state(state: FileState) -> EpollEvents {
171 let mut events = EpollEvents::empty();
172
173 if state.intersects(FileState::READABLE) {
174 events.insert(EpollEvents::EPOLLIN);
175 }
176 if state.intersects(FileState::WRITABLE) {
177 events.insert(EpollEvents::EPOLLOUT);
178 }
179 if state.intersects(FileState::RDHUP) {
180 events.insert(EpollEvents::EPOLLRDHUP);
181 }
182
183 events
184 }
185
186 fn state_from_events(events: EpollEvents) -> FileState {
187 let mut state = FileState::empty();
188
189 if events.intersects(EpollEvents::EPOLLIN) {
190 state.insert(FileState::READABLE)
191 }
192 if events.intersects(EpollEvents::EPOLLOUT) {
193 state.insert(FileState::WRITABLE)
194 }
195 if events.intersects(EpollEvents::EPOLLRDHUP) {
196 state.insert(FileState::RDHUP)
197 }
198
199 state
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 const DATA: u64 = 1234;
208
209 fn poll_init(init: FileState, interest: EpollEvents) {
210 let mut entry = Entry::new(interest, DATA, init);
211 assert!(entry.has_ready_events());
212
213 let (ev, data) = entry.collect_ready_events().unwrap();
214 assert!(interest.contains(ev));
215 assert_eq!(data, DATA);
216 }
217
218 #[test]
219 fn poll_init_r() {
220 let init = FileState::READABLE;
221 poll_init(init, EpollEvents::EPOLLIN);
222 }
223
224 #[test]
225 fn poll_init_w() {
226 let init = FileState::WRITABLE;
227 poll_init(init, EpollEvents::EPOLLOUT);
228 }
229
230 #[test]
231 fn poll_init_rw() {
232 let init = FileState::READABLE | FileState::WRITABLE;
233 poll_init(init, EpollEvents::EPOLLIN);
234 poll_init(init, EpollEvents::EPOLLOUT);
235 poll_init(init, EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT);
236 }
237
238 fn poll_on_state(
241 init: FileState,
242 interest: EpollEvents,
243 change_on: FileState,
244 signals: FileSignals,
245 ) {
246 let mut entry = Entry::new(interest, DATA, init);
247 assert!(!entry.has_ready_events());
248
249 entry.notify(init.union(change_on), change_on, signals);
250 assert!(entry.has_ready_events());
251
252 let (ev, data) = entry.collect_ready_events().unwrap();
253 assert!(interest.contains(ev));
254 assert_eq!(data, DATA);
255 }
256
257 #[test]
258 fn poll_on_r() {
259 let on = FileState::READABLE;
260 poll_on_state(
261 FileState::empty(),
262 EpollEvents::EPOLLIN,
263 on,
264 FileSignals::empty(),
265 );
266 poll_on_state(
267 FileState::empty(),
268 EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
269 on,
270 FileSignals::empty(),
271 );
272 poll_on_state(
273 FileState::WRITABLE,
274 EpollEvents::EPOLLIN,
275 on,
276 FileSignals::empty(),
277 );
278 }
279
280 #[test]
281 fn poll_on_w() {
282 let on = FileState::WRITABLE;
283 poll_on_state(
284 FileState::empty(),
285 EpollEvents::EPOLLOUT,
286 on,
287 FileSignals::empty(),
288 );
289 poll_on_state(
290 FileState::empty(),
291 EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
292 on,
293 FileSignals::empty(),
294 );
295 poll_on_state(
296 FileState::READABLE,
297 EpollEvents::EPOLLOUT,
298 on,
299 FileSignals::empty(),
300 );
301 }
302
303 #[test]
304 fn poll_on_rw() {
305 let on = FileState::READABLE | FileState::WRITABLE;
306 poll_on_state(
307 FileState::empty(),
308 EpollEvents::EPOLLIN,
309 on,
310 FileSignals::empty(),
311 );
312 poll_on_state(
313 FileState::empty(),
314 EpollEvents::EPOLLOUT,
315 on,
316 FileSignals::empty(),
317 );
318 poll_on_state(
319 FileState::empty(),
320 EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
321 on,
322 FileSignals::empty(),
323 );
324 }
325
326 fn poll_off_state(
329 init: FileState,
330 interest: EpollEvents,
331 change_off: FileState,
332 signals: FileSignals,
333 ) {
334 let mut entry = Entry::new(interest, DATA, init);
335 assert!(entry.has_ready_events());
336
337 entry.notify(init.difference(change_off), change_off, signals);
338 assert!(!entry.has_ready_events());
339 assert!(entry.collect_ready_events().is_none());
340 }
341
342 #[test]
343 fn poll_off_r() {
344 let interest = EpollEvents::EPOLLIN;
345 let off = FileState::READABLE;
346 poll_off_state(off, interest, off, FileSignals::empty());
347 poll_off_state(
348 FileState::WRITABLE | off,
349 interest,
350 off,
351 FileSignals::empty(),
352 );
353 }
354
355 #[test]
356 fn poll_off_w() {
357 let interest = EpollEvents::EPOLLOUT;
358 let off = FileState::WRITABLE;
359 poll_off_state(off, interest, off, FileSignals::empty());
360 poll_off_state(
361 FileState::READABLE | off,
362 interest,
363 off,
364 FileSignals::empty(),
365 );
366 }
367
368 #[test]
369 fn poll_off_rw() {
370 let off = FileState::READABLE | FileState::WRITABLE;
371 poll_off_state(off, EpollEvents::EPOLLIN, off, FileSignals::empty());
372 poll_off_state(off, EpollEvents::EPOLLOUT, off, FileSignals::empty());
373 poll_off_state(
374 off,
375 EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
376 off,
377 FileSignals::empty(),
378 );
379 }
380
381 #[test]
382 fn level_trigger() {
383 let in_lt = EpollEvents::EPOLLIN;
384 let mut entry = Entry::new(in_lt, DATA, FileState::empty());
385 assert!(!entry.has_ready_events());
386
387 entry.notify(
388 FileState::READABLE,
389 FileState::READABLE,
390 FileSignals::empty(),
391 );
392 assert!(entry.has_ready_events());
393
394 for _ in 0..3 {
395 assert_eq!(
396 entry.collect_ready_events(),
397 Some((EpollEvents::EPOLLIN, DATA))
398 );
399 assert!(entry.has_ready_events());
400 }
401
402 entry.notify(
403 FileState::empty(),
404 FileState::READABLE,
405 FileSignals::empty(),
406 );
407 assert!(!entry.has_ready_events());
408 entry.notify(
409 FileState::READABLE,
410 FileState::READABLE,
411 FileSignals::empty(),
412 );
413 assert!(entry.has_ready_events());
414
415 for _ in 0..3 {
416 assert_eq!(
417 entry.collect_ready_events(),
418 Some((EpollEvents::EPOLLIN, DATA))
419 );
420 assert!(entry.has_ready_events());
421 }
422 }
423
424 #[test]
425 fn edge_trigger() {
426 let in_et = EpollEvents::EPOLLIN | EpollEvents::EPOLLET;
427 let mut entry = Entry::new(in_et, DATA, FileState::empty());
428 assert!(!entry.has_ready_events());
429
430 entry.notify(
431 FileState::READABLE,
432 FileState::READABLE,
433 FileSignals::empty(),
434 );
435
436 assert!(entry.has_ready_events());
437 assert_eq!(
438 entry.collect_ready_events(),
439 Some((EpollEvents::EPOLLIN, DATA))
440 );
441
442 assert!(!entry.has_ready_events());
444 assert_eq!(entry.collect_ready_events(), None);
445
446 entry.notify(
448 FileState::READABLE,
449 FileState::empty(),
450 FileSignals::empty(),
451 );
452 assert!(!entry.has_ready_events());
453
454 entry.notify(
456 FileState::READABLE,
457 FileState::empty(),
458 FileSignals::READ_BUFFER_GREW,
459 );
460 assert!(entry.has_ready_events());
461 assert_eq!(
462 entry.collect_ready_events(),
463 Some((EpollEvents::EPOLLIN, DATA))
464 );
465
466 entry.notify(
469 FileState::empty(),
470 FileState::empty(),
471 FileSignals::READ_BUFFER_GREW,
472 );
473 assert!(!entry.has_ready_events());
474
475 entry.notify(
477 FileState::empty(),
478 FileState::READABLE,
479 FileSignals::empty(),
480 );
481 assert!(!entry.has_ready_events());
482
483 entry.notify(
485 FileState::READABLE,
486 FileState::READABLE,
487 FileSignals::empty(),
488 );
489 assert!(entry.has_ready_events());
490 assert_eq!(
491 entry.collect_ready_events(),
492 Some((EpollEvents::EPOLLIN, DATA))
493 );
494
495 assert!(!entry.has_ready_events());
496 }
497
498 #[test]
499 fn one_shot() {
500 let in_os = EpollEvents::EPOLLIN | EpollEvents::EPOLLONESHOT;
501 let mut entry = Entry::new(in_os, DATA, FileState::empty());
502 assert!(!entry.has_ready_events());
503
504 entry.notify(
505 FileState::READABLE,
506 FileState::READABLE,
507 FileSignals::empty(),
508 );
509
510 assert!(entry.has_ready_events());
511 assert_eq!(
512 entry.collect_ready_events(),
513 Some((EpollEvents::EPOLLIN, DATA))
514 );
515
516 assert!(!entry.has_ready_events());
518 assert_eq!(entry.collect_ready_events(), None);
519 entry.notify(
520 FileState::READABLE,
521 FileState::empty(),
522 FileSignals::empty(),
523 );
524 assert!(!entry.has_ready_events());
525 entry.notify(
526 FileState::empty(),
527 FileState::READABLE,
528 FileSignals::empty(),
529 );
530 assert!(!entry.has_ready_events());
531 entry.notify(
532 FileState::READABLE,
533 FileState::READABLE,
534 FileSignals::empty(),
535 );
536 assert!(!entry.has_ready_events());
537
538 entry.modify(in_os, DATA, FileState::READABLE);
539
540 assert!(entry.has_ready_events());
541 assert_eq!(
542 entry.collect_ready_events(),
543 Some((EpollEvents::EPOLLIN, DATA))
544 );
545
546 entry.notify(
547 FileState::empty(),
548 FileState::READABLE,
549 FileSignals::empty(),
550 );
551 entry.notify(
552 FileState::READABLE,
553 FileState::READABLE,
554 FileSignals::empty(),
555 );
556
557 assert!(!entry.has_ready_events());
558 assert_eq!(entry.collect_ready_events(), None);
559 }
560}