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
180        events
181    }
182
183    fn state_from_events(events: EpollEvents) -> FileState {
184        let mut state = FileState::empty();
185
186        if events.intersects(EpollEvents::EPOLLIN) {
187            state.insert(FileState::READABLE)
188        }
189        if events.intersects(EpollEvents::EPOLLOUT) {
190            state.insert(FileState::WRITABLE)
191        }
192
193        state
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    const DATA: u64 = 1234;
202
203    fn poll_init(init: FileState, interest: EpollEvents) {
204        let mut entry = Entry::new(interest, DATA, init);
205        assert!(entry.has_ready_events());
206
207        let (ev, data) = entry.collect_ready_events().unwrap();
208        assert!(interest.contains(ev));
209        assert_eq!(data, DATA);
210    }
211
212    #[test]
213    fn poll_init_r() {
214        let init = FileState::READABLE;
215        poll_init(init, EpollEvents::EPOLLIN);
216    }
217
218    #[test]
219    fn poll_init_w() {
220        let init = FileState::WRITABLE;
221        poll_init(init, EpollEvents::EPOLLOUT);
222    }
223
224    #[test]
225    fn poll_init_rw() {
226        let init = FileState::READABLE | FileState::WRITABLE;
227        poll_init(init, EpollEvents::EPOLLIN);
228        poll_init(init, EpollEvents::EPOLLOUT);
229        poll_init(init, EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT);
230    }
231
232    /// Checks that an entry starting in state `init` is only ready after `change` turns on when
233    /// waiting for `interest`.
234    fn poll_on_state(
235        init: FileState,
236        interest: EpollEvents,
237        change_on: FileState,
238        signals: FileSignals,
239    ) {
240        let mut entry = Entry::new(interest, DATA, init);
241        assert!(!entry.has_ready_events());
242
243        entry.notify(init.union(change_on), change_on, signals);
244        assert!(entry.has_ready_events());
245
246        let (ev, data) = entry.collect_ready_events().unwrap();
247        assert!(interest.contains(ev));
248        assert_eq!(data, DATA);
249    }
250
251    #[test]
252    fn poll_on_r() {
253        let on = FileState::READABLE;
254        poll_on_state(
255            FileState::empty(),
256            EpollEvents::EPOLLIN,
257            on,
258            FileSignals::empty(),
259        );
260        poll_on_state(
261            FileState::empty(),
262            EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
263            on,
264            FileSignals::empty(),
265        );
266        poll_on_state(
267            FileState::WRITABLE,
268            EpollEvents::EPOLLIN,
269            on,
270            FileSignals::empty(),
271        );
272    }
273
274    #[test]
275    fn poll_on_w() {
276        let on = FileState::WRITABLE;
277        poll_on_state(
278            FileState::empty(),
279            EpollEvents::EPOLLOUT,
280            on,
281            FileSignals::empty(),
282        );
283        poll_on_state(
284            FileState::empty(),
285            EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
286            on,
287            FileSignals::empty(),
288        );
289        poll_on_state(
290            FileState::READABLE,
291            EpollEvents::EPOLLOUT,
292            on,
293            FileSignals::empty(),
294        );
295    }
296
297    #[test]
298    fn poll_on_rw() {
299        let on = FileState::READABLE | FileState::WRITABLE;
300        poll_on_state(
301            FileState::empty(),
302            EpollEvents::EPOLLIN,
303            on,
304            FileSignals::empty(),
305        );
306        poll_on_state(
307            FileState::empty(),
308            EpollEvents::EPOLLOUT,
309            on,
310            FileSignals::empty(),
311        );
312        poll_on_state(
313            FileState::empty(),
314            EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
315            on,
316            FileSignals::empty(),
317        );
318    }
319
320    /// Checks that an entry starting in state `init` is only not ready after `change` turns off
321    /// when waiting for `interest`.
322    fn poll_off_state(
323        init: FileState,
324        interest: EpollEvents,
325        change_off: FileState,
326        signals: FileSignals,
327    ) {
328        let mut entry = Entry::new(interest, DATA, init);
329        assert!(entry.has_ready_events());
330
331        entry.notify(init.difference(change_off), change_off, signals);
332        assert!(!entry.has_ready_events());
333        assert!(entry.collect_ready_events().is_none());
334    }
335
336    #[test]
337    fn poll_off_r() {
338        let interest = EpollEvents::EPOLLIN;
339        let off = FileState::READABLE;
340        poll_off_state(off, interest, off, FileSignals::empty());
341        poll_off_state(
342            FileState::WRITABLE | off,
343            interest,
344            off,
345            FileSignals::empty(),
346        );
347    }
348
349    #[test]
350    fn poll_off_w() {
351        let interest = EpollEvents::EPOLLOUT;
352        let off = FileState::WRITABLE;
353        poll_off_state(off, interest, off, FileSignals::empty());
354        poll_off_state(
355            FileState::READABLE | off,
356            interest,
357            off,
358            FileSignals::empty(),
359        );
360    }
361
362    #[test]
363    fn poll_off_rw() {
364        let off = FileState::READABLE | FileState::WRITABLE;
365        poll_off_state(off, EpollEvents::EPOLLIN, off, FileSignals::empty());
366        poll_off_state(off, EpollEvents::EPOLLOUT, off, FileSignals::empty());
367        poll_off_state(
368            off,
369            EpollEvents::EPOLLIN | EpollEvents::EPOLLOUT,
370            off,
371            FileSignals::empty(),
372        );
373    }
374
375    #[test]
376    fn level_trigger() {
377        let in_lt = EpollEvents::EPOLLIN;
378        let mut entry = Entry::new(in_lt, DATA, FileState::empty());
379        assert!(!entry.has_ready_events());
380
381        entry.notify(
382            FileState::READABLE,
383            FileState::READABLE,
384            FileSignals::empty(),
385        );
386        assert!(entry.has_ready_events());
387
388        for _ in 0..3 {
389            assert_eq!(
390                entry.collect_ready_events(),
391                Some((EpollEvents::EPOLLIN, DATA))
392            );
393            assert!(entry.has_ready_events());
394        }
395
396        entry.notify(
397            FileState::empty(),
398            FileState::READABLE,
399            FileSignals::empty(),
400        );
401        assert!(!entry.has_ready_events());
402        entry.notify(
403            FileState::READABLE,
404            FileState::READABLE,
405            FileSignals::empty(),
406        );
407        assert!(entry.has_ready_events());
408
409        for _ in 0..3 {
410            assert_eq!(
411                entry.collect_ready_events(),
412                Some((EpollEvents::EPOLLIN, DATA))
413            );
414            assert!(entry.has_ready_events());
415        }
416    }
417
418    #[test]
419    fn edge_trigger() {
420        let in_et = EpollEvents::EPOLLIN | EpollEvents::EPOLLET;
421        let mut entry = Entry::new(in_et, DATA, FileState::empty());
422        assert!(!entry.has_ready_events());
423
424        entry.notify(
425            FileState::READABLE,
426            FileState::READABLE,
427            FileSignals::empty(),
428        );
429
430        assert!(entry.has_ready_events());
431        assert_eq!(
432            entry.collect_ready_events(),
433            Some((EpollEvents::EPOLLIN, DATA))
434        );
435
436        // Event was collected and should only be reported once.
437        assert!(!entry.has_ready_events());
438        assert_eq!(entry.collect_ready_events(), None);
439
440        // Nothing changed, so still no events.
441        entry.notify(
442            FileState::READABLE,
443            FileState::empty(),
444            FileSignals::empty(),
445        );
446        assert!(!entry.has_ready_events());
447
448        // Nothing changes, but this time we signal that the buffer is written more.
449        entry.notify(
450            FileState::READABLE,
451            FileState::empty(),
452            FileSignals::READ_BUFFER_GREW,
453        );
454        assert!(entry.has_ready_events());
455        assert_eq!(
456            entry.collect_ready_events(),
457            Some((EpollEvents::EPOLLIN, DATA))
458        );
459
460        // When the file is not readable but we receives `READ_BUFFER_GREW`, there should be no
461        // ready events.
462        entry.notify(
463            FileState::empty(),
464            FileState::empty(),
465            FileSignals::READ_BUFFER_GREW,
466        );
467        assert!(!entry.has_ready_events());
468
469        // State turns off.
470        entry.notify(
471            FileState::empty(),
472            FileState::READABLE,
473            FileSignals::empty(),
474        );
475        assert!(!entry.has_ready_events());
476
477        // State turns on again.
478        entry.notify(
479            FileState::READABLE,
480            FileState::READABLE,
481            FileSignals::empty(),
482        );
483        assert!(entry.has_ready_events());
484        assert_eq!(
485            entry.collect_ready_events(),
486            Some((EpollEvents::EPOLLIN, DATA))
487        );
488
489        assert!(!entry.has_ready_events());
490    }
491
492    #[test]
493    fn one_shot() {
494        let in_os = EpollEvents::EPOLLIN | EpollEvents::EPOLLONESHOT;
495        let mut entry = Entry::new(in_os, DATA, FileState::empty());
496        assert!(!entry.has_ready_events());
497
498        entry.notify(
499            FileState::READABLE,
500            FileState::READABLE,
501            FileSignals::empty(),
502        );
503
504        assert!(entry.has_ready_events());
505        assert_eq!(
506            entry.collect_ready_events(),
507            Some((EpollEvents::EPOLLIN, DATA))
508        );
509
510        // Should never report that event again until we reset.
511        assert!(!entry.has_ready_events());
512        assert_eq!(entry.collect_ready_events(), None);
513        entry.notify(
514            FileState::READABLE,
515            FileState::empty(),
516            FileSignals::empty(),
517        );
518        assert!(!entry.has_ready_events());
519        entry.notify(
520            FileState::empty(),
521            FileState::READABLE,
522            FileSignals::empty(),
523        );
524        assert!(!entry.has_ready_events());
525        entry.notify(
526            FileState::READABLE,
527            FileState::READABLE,
528            FileSignals::empty(),
529        );
530        assert!(!entry.has_ready_events());
531
532        entry.modify(in_os, DATA, FileState::READABLE);
533
534        assert!(entry.has_ready_events());
535        assert_eq!(
536            entry.collect_ready_events(),
537            Some((EpollEvents::EPOLLIN, DATA))
538        );
539
540        entry.notify(
541            FileState::empty(),
542            FileState::READABLE,
543            FileSignals::empty(),
544        );
545        entry.notify(
546            FileState::READABLE,
547            FileState::READABLE,
548            FileSignals::empty(),
549        );
550
551        assert!(!entry.has_ready_events());
552        assert_eq!(entry.collect_ready_events(), None);
553    }
554}