shadow_rs/host/descriptor/epoll/
entry.rs

1use linux_api::epoll::EpollEvents;
2
3use crate::host::descriptor::listener::StateListenHandle;
4use crate::host::descriptor::{FileSignals, FileState};
5
6/// Used to track the status of a file we are monitoring for events. Any complicated logic for
7/// deciding when a file has events that epoll should report should be specified in this object's
8/// implementation.
9pub(super) struct Entry {
10    /// Priority value among other ready entries.
11    priority: Option<u64>,
12    /// The events of interest registered by the managed process.
13    interest: EpollEvents,
14    /// The data registered by the managed process, to be returned upon event notification.
15    data: u64,
16    /// The handle to the currently registered file status listener.
17    listener_handle: Option<StateListenHandle>,
18    /// The current state of the file.
19    state: FileState,
20    /// The file state changes we have already reported since the state last changed. When a state
21    /// changes, that event becomes uncollected until `collect_ready_events` is called.
22    collected: FileState,
23    /// TODO remove when legacy tcp is removed.
24    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    // TODO remove when legacy tcp is removed.
41    pub fn set_legacy(&mut self) {
42        self.is_legacy = true;
43    }
44
45    /// Updates the events that should be tracked in this entry, and the data that should be
46    /// returned to the managed process when those events occur.
47    ///
48    /// Note that this operation causes us to store the given current file state so that future
49    /// changes are tracked from the state at the time `modify()` was called, and internal state for
50    /// tracking which events have been collected by the managed process are updated accordingly.
51    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 the file is written again, let the epoll waiter collect the events again.
79        if signals.contains(FileSignals::READ_BUFFER_GREW) {
80            // We only subscribe to `READ_BUFFER_GREW` signals for edge-triggered entries.
81            debug_assert!(self.interest.intersects(EpollEvents::EPOLLET));
82
83            // Ignore the `READ_BUFFER_GREW` if the file isn't READABLE.
84            if new_state.contains(FileState::READABLE) {
85                self.collected.remove(FileState::READABLE);
86            } else {
87                // If this occurs, we probably want to fix whatever file is broadcasting `READ_BUFFER_GREW`
88                // when not readable.
89                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        // TODO remove this if block when legacy tcp is removed.
96        if self.is_legacy {
97            return FileState::all();
98        }
99
100        // Return the file state changes that we want to be notified about.
101        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        // TODO remove this if block when legacy tcp is removed.
120        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            // Edge-triggered: report event, then don't report again until that state changes.
163            self.state.difference(self.collected)
164        } else {
165            // Level-triggered: report event, keep reporting until state turns off.
166            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    /// Checks that an entry starting in state `init` is only ready after `change` turns on when
239    /// waiting for `interest`.
240    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    /// Checks that an entry starting in state `init` is only not ready after `change` turns off
327    /// when waiting for `interest`.
328    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        // Event was collected and should only be reported once.
443        assert!(!entry.has_ready_events());
444        assert_eq!(entry.collect_ready_events(), None);
445
446        // Nothing changed, so still no events.
447        entry.notify(
448            FileState::READABLE,
449            FileState::empty(),
450            FileSignals::empty(),
451        );
452        assert!(!entry.has_ready_events());
453
454        // Nothing changes, but this time we signal that the buffer is written more.
455        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        // When the file is not readable but we receives `READ_BUFFER_GREW`, there should be no
467        // ready events.
468        entry.notify(
469            FileState::empty(),
470            FileState::empty(),
471            FileSignals::READ_BUFFER_GREW,
472        );
473        assert!(!entry.has_ready_events());
474
475        // State turns off.
476        entry.notify(
477            FileState::empty(),
478            FileState::READABLE,
479            FileSignals::empty(),
480        );
481        assert!(!entry.has_ready_events());
482
483        // State turns on again.
484        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        // Should never report that event again until we reset.
517        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}