Skip to main content

starnix_core/vfs/
epoll.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::power::WakeupSourceOrigin;
6use crate::task::{
7    CurrentTask, EventHandler, ReadyItem, ReadyItemKey, WaitCanceler, WaitQueue, Waiter,
8};
9use crate::vfs::{
10    Anon, FileHandle, FileObject, FileObjectState, FileOps, WeakFileHandle, fileops_impl_dataless,
11    fileops_impl_nonseekable, fileops_impl_noop_sync,
12};
13use itertools::Itertools;
14use starnix_logging::log_warn;
15use starnix_sync::{
16    EpollStateLock, EpollWaitableStateLock, FileOpsCore, LockDepMutex, LockEqualOrBefore, Locked,
17    allow_subclass,
18};
19use starnix_uapi::error;
20use starnix_uapi::errors::{EINTR, ETIMEDOUT, Errno};
21use starnix_uapi::open_flags::OpenFlags;
22use starnix_uapi::vfs::{EpollEvent, FdEvents};
23use std::collections::hash_map::Entry;
24use std::collections::{HashMap, VecDeque};
25use std::sync::Arc;
26
27/// Maximum depth of epoll instances monitoring one another.
28/// From https://man7.org/linux/man-pages/man2/epoll_ctl.2.html
29const MAX_NESTED_DEPTH: u32 = 5;
30
31/// WaitObject represents a FileHandle that is being waited upon.
32/// The `data` field is a user defined quantity passed in
33/// via `sys_epoll_ctl`. Typically C programs could use this
34/// to store a pointer to the data that needs to be processed
35/// after an event.
36struct WaitObject {
37    target: WeakFileHandle,
38    events: FdEvents,
39    data: u64,
40    wait_canceler: Option<WaitCanceler>,
41    active_wakeup_source: Option<WakeupSourceOrigin>,
42}
43
44impl WaitObject {
45    /// Returns the target `FileHandle` of the `WaitObject`, or `None` if the file has been closed.
46    ///
47    /// It is fine for the `FileHandle` to be closed after being added to an epoll, and subsequent
48    /// epoll_waits end up timing out (importantly not returning EBADF).
49    fn target(&self) -> Option<FileHandle> {
50        self.target.upgrade()
51    }
52
53    fn deactivate_wakeup_source(&mut self, current_task: &CurrentTask) {
54        if let Some(origin) = self.active_wakeup_source.take() {
55            current_task.kernel().suspend_resume_manager.deactivate_wakeup_source(&origin);
56        }
57    }
58}
59
60/// EpollKey acts as an key to a map of WaitObject.
61/// In reality it is a pointer to a FileHandle object.
62pub type EpollKey = usize;
63
64/// EpollFileObject represents the FileObject used to
65/// implement epoll_create1/epoll_ctl/epoll_pwait.
66#[derive(Default)]
67pub struct EpollFileObject {
68    waiter: Waiter,
69    /// Mutable state of this epoll object.
70    state: LockDepMutex<EpollState, EpollStateLock>,
71    waitable_state: Arc<LockDepMutex<EpollWaitableState, EpollWaitableStateLock>>,
72    /// A list of waiters waiting for events from this
73    /// epoll instance.
74    waiters: Arc<WaitQueue>,
75}
76
77#[derive(Default)]
78struct EpollState {
79    /// Any file tracked by this epoll instance
80    /// will exist as a key in `wait_objects`.
81    wait_objects: HashMap<ReadyItemKey, WaitObject>,
82    /// processing_list is a FIFO of events that are being
83    /// processed.
84    ///
85    /// Objects from the `EpollFileObject`'s `trigger_list` are moved into this
86    /// list so that we can handle triggered events without holding its lock
87    /// longer than we need to. This reduces contention with waited-on objects
88    /// that tries to notify this epoll object on subscribed events.
89    processing_list: VecDeque<ReadyItem>,
90    /// recheck_list is the list of items that need to have query_events checked
91    /// at the start of the next EpollFileObject::wait call. This is only items
92    /// that were returned from the last wait call, because those are the only
93    /// ones that might need to be returned even if no events come in between
94    /// wait calls.
95    recheck_list: Vec<ReadyItemKey>,
96}
97
98#[derive(Default)]
99struct EpollWaitableState {
100    /// trigger_list is a FIFO of events that have
101    /// happened, but have not yet been processed.
102    trigger_list: VecDeque<ReadyItem>,
103}
104
105impl EpollFileObject {
106    /// Allocate a new, empty epoll object.
107    pub fn new_file<L>(locked: &mut Locked<L>, current_task: &CurrentTask) -> FileHandle
108    where
109        L: LockEqualOrBefore<FileOpsCore>,
110    {
111        let epoll = Box::new(EpollFileObject::default());
112
113        #[cfg(any(test, debug_assertions))]
114        {
115            let _l1 = epoll.state.lock();
116            let _l2 = epoll.waitable_state.lock();
117        }
118
119        Anon::new_private_file(locked, current_task, epoll, OpenFlags::RDWR, "[eventpoll]")
120    }
121
122    fn wait_on_file<L>(
123        &self,
124        locked: &mut Locked<L>,
125        current_task: &CurrentTask,
126        key: ReadyItemKey,
127        wait_object: &mut WaitObject,
128    ) -> Result<(), Errno>
129    where
130        L: LockEqualOrBefore<FileOpsCore>,
131    {
132        // First start the wait. If an event happens after this, we'll get it.
133        self.wait_on_file_edge_triggered(locked, current_task, key, wait_object)?;
134
135        self.do_recheck(locked, current_task, wait_object, key)?;
136
137        Ok(())
138    }
139
140    fn do_recheck<L>(
141        &self,
142        locked: &mut Locked<L>,
143        current_task: &CurrentTask,
144        wait_object: &mut WaitObject,
145        key: ReadyItemKey,
146    ) -> Result<(), Errno>
147    where
148        L: LockEqualOrBefore<FileOpsCore>,
149    {
150        let Some(target) = wait_object.target() else { return Ok(()) };
151        let events = {
152            // Target might be itself an epoll object. Because there is no loop,
153            // this allow_subclass is safe.
154            let _token = allow_subclass();
155            target.query_events(locked, current_task)?
156        };
157        if !(events & wait_object.events).is_empty() {
158            self.waiter.wake_immediately(events, self.new_wait_handler(key));
159            if let Some(wait_canceler) = wait_object.wait_canceler.take() {
160                wait_canceler.cancel();
161            } else {
162                log_warn!("wait canceler should have been set by `wait_on_file_edge_triggered`");
163            }
164        }
165        Ok(())
166    }
167
168    fn wait_on_file_edge_triggered<L>(
169        &self,
170        locked: &mut Locked<L>,
171        current_task: &CurrentTask,
172        key: ReadyItemKey,
173        wait_object: &mut WaitObject,
174    ) -> Result<(), Errno>
175    where
176        L: LockEqualOrBefore<FileOpsCore>,
177    {
178        let Some(target) = wait_object.target() else {
179            return Ok(());
180        };
181
182        wait_object.wait_canceler = target.wait_async(
183            locked,
184            current_task,
185            &self.waiter,
186            wait_object.events,
187            self.new_wait_handler(key),
188        );
189        if wait_object.wait_canceler.is_none() {
190            return error!(EPERM);
191        }
192        Ok(())
193    }
194
195    /// Checks if adding self to the `epoll_file_object` at `epoll_file_handle` would cause a loop
196    /// or exceed max depth.
197    fn check_eloop(&self, parent: &FileHandle, depth_left: u32) -> Result<(), Errno> {
198        if depth_left == 0 {
199            return error!(ELOOP);
200        }
201
202        let state = self.state.lock();
203        for nested_object in state.wait_objects.values() {
204            let Some(child) = nested_object.target() else {
205                continue;
206            };
207            let Some(child_file) = child.downcast_file::<EpollFileObject>() else {
208                continue;
209            };
210
211            if Arc::ptr_eq(&child, parent) {
212                return error!(ELOOP);
213            }
214            // Child is not part of a loop, so subclassing is safe.
215            let _token = allow_subclass();
216            child_file.check_eloop(parent, depth_left - 1)?;
217        }
218
219        Ok(())
220    }
221
222    /// Asynchronously wait on certain events happening on a FileHandle.
223    pub fn add<L>(
224        &self,
225        locked: &mut Locked<L>,
226        current_task: &CurrentTask,
227        file: &FileHandle,
228        epoll_file_handle: &FileHandle,
229        epoll_event: EpollEvent,
230    ) -> Result<(), Errno>
231    where
232        L: LockEqualOrBefore<FileOpsCore>,
233    {
234        // Check if adding this file would cause a cycle at a max depth of 5.
235        if let Some(epoll_to_add) = file.downcast_file::<EpollFileObject>() {
236            // We need to check for `MAX_NESTED_DEPTH - 1` because adding `epoll_to_add` to self
237            // would result in a total depth of one more.
238            epoll_to_add.check_eloop(epoll_file_handle, MAX_NESTED_DEPTH - 1)?;
239        }
240
241        let mut state = self.state.lock();
242        let key = file.id.as_epoll_key().into();
243        match state.wait_objects.entry(key) {
244            Entry::Occupied(_) => error!(EEXIST),
245            Entry::Vacant(entry) => {
246                let wait_object = entry.insert(WaitObject {
247                    target: Arc::downgrade(file),
248                    events: epoll_event.events() | FdEvents::POLLHUP | FdEvents::POLLERR,
249                    data: epoll_event.data(),
250                    wait_canceler: None,
251                    active_wakeup_source: None,
252                });
253                self.wait_on_file(locked, current_task, key, wait_object)
254            }
255        }
256    }
257
258    /// Modify the events we are looking for on a Filehandle.
259    pub fn modify<L>(
260        &self,
261        locked: &mut Locked<L>,
262        current_task: &CurrentTask,
263        file: &FileHandle,
264        epoll_event: EpollEvent,
265    ) -> Result<(), Errno>
266    where
267        L: LockEqualOrBefore<FileOpsCore>,
268    {
269        let mut state = self.state.lock();
270        let key = file.id.as_epoll_key();
271        state.recheck_list.retain(|x| *x != key.into());
272        let Some(wait_object) = state.wait_objects.get_mut(&key.into()) else {
273            return error!(ENOENT);
274        };
275        if let Some(wait_canceler) = wait_object.wait_canceler.take() {
276            wait_canceler.cancel();
277        }
278        wait_object.events = epoll_event.events() | FdEvents::POLLHUP | FdEvents::POLLERR;
279        wait_object.data = epoll_event.data();
280        // If the new epoll event doesn't include EPOLLWAKEUP, we need to take down the
281        // wake lease. This ensures that the system doesn't stay awake unnecessarily when
282        // the event no longer requires it to be awake.
283        if wait_object.events.contains(FdEvents::EPOLLWAKEUP)
284            && !epoll_event.events().contains(FdEvents::EPOLLWAKEUP)
285        {
286            wait_object.deactivate_wakeup_source(current_task);
287        }
288        self.wait_on_file(locked, current_task, key.into(), wait_object)
289    }
290
291    /// Cancel an asynchronous wait on an object. Events triggered before
292    /// calling this will still be delivered.
293    pub fn delete(&self, current_task: &CurrentTask, file: &FileObject) -> Result<(), Errno> {
294        let mut state = self.state.lock();
295        let key = file.id.as_epoll_key().into();
296        if let Some(mut wait_object) = state.wait_objects.remove(&key) {
297            if let Some(wait_canceler) = wait_object.wait_canceler.take() {
298                wait_canceler.cancel();
299            }
300            state.recheck_list.retain(|x| *x != key);
301            // Deactivate the wake lock if it was active.
302            wait_object.deactivate_wakeup_source(current_task);
303            Ok(())
304        } else {
305            error!(ENOENT)
306        }
307    }
308
309    /// Stores events from the Epoll's trigger list to the parameter `pending_list`. This does not
310    /// actually invoke the waiter which is how items are added to the trigger list. The caller
311    /// will have to do that before calling if needed.
312    ///
313    /// If an event in the trigger list is stale, the event will be re-added to the waiter.
314    ///
315    /// Returns true if any events were added. False means there was nothing in the trigger list.
316    fn process_triggered_events<L>(
317        &self,
318        locked: &mut Locked<L>,
319        current_task: &CurrentTask,
320        pending_list: &mut Vec<ReadyItem>,
321        max_events: usize,
322    ) -> Result<(), Errno>
323    where
324        L: LockEqualOrBefore<FileOpsCore>,
325    {
326        let mut state = self.state.lock();
327        // Move all the elements from `self.trigger_list` to this intermediary
328        // queue that we handle events from. This reduces the time spent holding
329        // `self.trigger_list`'s lock which reduces contention with objects that
330        // this epoll object has subscribed for notifications from.
331        state.processing_list.append(&mut self.waitable_state.lock().trigger_list);
332        while pending_list.len() < max_events && !state.processing_list.is_empty() {
333            if let Some(pending) = state.processing_list.pop_front() {
334                if let Some(wait) = state.wait_objects.get_mut(&pending.key) {
335                    // The weak pointer to the FileObject target can be gone if the file was closed
336                    // out from under us. If this happens it is not an error: ignore it and
337                    // continue.
338                    if let Some(target) = wait.target.upgrade() {
339                        let events = {
340                            // Target might be itself an epoll object. Because there is no loop,
341                            // this allow_subclass is safe.
342                            let _token = allow_subclass();
343                            target.query_events(locked, current_task)?
344                        };
345                        let ready = ReadyItem { key: pending.key, events };
346                        if ready.events.intersects(wait.events) {
347                            pending_list.push(ready);
348                        } else {
349                            // Another thread already handled this event, wait for another one.
350                            self.wait_on_file(locked, current_task, pending.key, wait)?;
351                        }
352                    }
353                }
354            }
355        }
356        Ok(())
357    }
358
359    /// Waits until an event exists in `pending_list` or until `timeout` has
360    /// been reached.
361    fn wait_until_pending_event<L>(
362        &self,
363        locked: &mut Locked<L>,
364        current_task: &CurrentTask,
365        max_events: usize,
366        mut wait_deadline: zx::MonotonicInstant,
367    ) -> Result<Vec<ReadyItem>, Errno>
368    where
369        L: LockEqualOrBefore<FileOpsCore>,
370    {
371        let mut pending_list = Vec::new();
372
373        loop {
374            self.process_triggered_events(locked, current_task, &mut pending_list, max_events)?;
375
376            if pending_list.len() == max_events {
377                break; // No input events or output list full, nothing more we can do.
378            }
379
380            if !pending_list.is_empty() {
381                // We now know we have at least one event to return. We shouldn't return
382                // immediately, in case there are more events available, but the next loop should
383                // wait with a 0 timeout to prevent further blocking.
384                wait_deadline = zx::MonotonicInstant::ZERO;
385            }
386
387            // Loop back to check if there are more items in the Waiter's queue. Every wait_until()
388            // call will process a single event. In order to drain as many events as we can that
389            // are synchronously available, keep trying until it reports empty.
390            //
391            // The handlers in the waits cause items to be appended to trigger_list. See the closure
392            // in `wait_on_file` to see how this happens.
393            //
394            // This wait may return EINTR for nonzero timeouts which is not an error. We must be
395            // careful not to lose events if this happens.
396            //
397            // The first time through this loop we'll use the timeout passed into this function so
398            // can get EINTR. But since we haven't done anything or accumulated any results yet it's
399            // OK to immediately return and no information will be lost.
400            match self.waiter.wait_until(locked, current_task, wait_deadline) {
401                Err(err) if err == ETIMEDOUT => break,
402                Err(err) if err == EINTR => {
403                    // Terminating early will lose any events in the pending_list so that should
404                    // only be for unrecoverable errors (not EINTR). The only time there should be a
405                    // nonzero wait_deadline (and hence the ability to encounter EINTR) is when the
406                    // pending list is empty.
407                    debug_assert!(
408                        pending_list.is_empty(),
409                        "Got EINTR from wait of {}ns with {} items pending.",
410                        wait_deadline.into_nanos(),
411                        pending_list.len()
412                    );
413                    return Err(err);
414                }
415                // TODO check if this is supposed to actually fail!
416                result => result?,
417            }
418        }
419
420        Ok(pending_list)
421    }
422
423    /// Blocking wait on all waited upon events with a timeout.
424    pub fn wait<L>(
425        &self,
426        locked: &mut Locked<L>,
427        current_task: &CurrentTask,
428        max_events: usize,
429        deadline: zx::MonotonicInstant,
430    ) -> Result<Vec<EpollEvent>, Errno>
431    where
432        L: LockEqualOrBefore<FileOpsCore>,
433    {
434        {
435            let mut state = self.state.lock();
436            let recheck_list = std::mem::take(&mut state.recheck_list);
437            for key in recheck_list {
438                let wait_object = state.wait_objects.get_mut(&key).unwrap();
439                wait_object.deactivate_wakeup_source(current_task);
440                // TODO(https://fxbug.dev/530545712): If `do_recheck` fails, we exit the loop and do
441                // not deactivate the remaining wakeup sources.
442                self.do_recheck(locked, current_task, wait_object, key)?;
443            }
444        }
445
446        let pending_list =
447            self.wait_until_pending_event(locked, current_task, max_events, deadline)?;
448
449        // Process the pending list and add processed ReadyItem
450        // entries to the rearm_list for the next wait.
451        let mut result = vec![];
452        let mut state = self.state.lock();
453        let state = &mut *state;
454        for pending_event in pending_list.iter().unique_by(|e| e.key) {
455            // The wait could have been deleted by here,
456            // so ignore the None case.
457            let Some(wait) = state.wait_objects.get_mut(&pending_event.key) else { continue };
458
459            let reported_events = pending_event.events & wait.events;
460            result.push(EpollEvent::new(reported_events, wait.data));
461
462            // Files marked with `EPOLLONESHOT` should only notify
463            // once and need to be rearmed manually with epoll_ctl_mod().
464            if wait.events.contains(FdEvents::EPOLLONESHOT) {
465                continue;
466            }
467
468            self.wait_on_file_edge_triggered(locked, current_task, pending_event.key, wait)?;
469
470            if !wait.events.contains(FdEvents::EPOLLET) {
471                state.recheck_list.push(pending_event.key);
472            }
473
474            // TODO: is this really only supposed to happen for level-triggered events?
475            if !wait.events.contains(FdEvents::EPOLLET) {
476                // When this is the first time epoll_wait on this epoll fd, create and
477                // hold a wake lease until the next epoll_wait.
478                if wait.events.contains(FdEvents::EPOLLWAKEUP) {
479                    if let ReadyItemKey::Usize(key) = pending_event.key {
480                        let origin = WakeupSourceOrigin::Epoll(current_task.command(), key);
481                        current_task
482                            .kernel()
483                            .suspend_resume_manager
484                            .activate_wakeup_source(origin.clone());
485                        wait.active_wakeup_source = Some(origin);
486                    }
487                }
488            }
489        }
490
491        Ok(result)
492    }
493}
494
495impl FileOps for EpollFileObject {
496    fileops_impl_nonseekable!();
497    fileops_impl_noop_sync!();
498    fileops_impl_dataless!();
499
500    fn wait_async(
501        &self,
502        _locked: &mut Locked<FileOpsCore>,
503        _file: &FileObject,
504        _current_task: &CurrentTask,
505        waiter: &Waiter,
506        events: FdEvents,
507        handler: EventHandler,
508    ) -> Option<WaitCanceler> {
509        Some(self.waiters.wait_async_fd_events(waiter, events, handler))
510    }
511
512    fn query_events(
513        &self,
514        locked: &mut Locked<FileOpsCore>,
515        _file: &FileObject,
516        current_task: &CurrentTask,
517    ) -> Result<FdEvents, Errno> {
518        let mut events = FdEvents::empty();
519        let state = self.state.lock();
520        if !state.processing_list.is_empty() || !self.waitable_state.lock().trigger_list.is_empty()
521        {
522            events |= FdEvents::POLLIN;
523        } else {
524            for key in &state.recheck_list {
525                let wait_object = state.wait_objects.get(key).unwrap();
526                let Some(target) = wait_object.target() else { continue };
527                // Target might be itself an epoll object. Because there is no loop,
528                // this allow_subclass is safe.
529                let _token = allow_subclass();
530                if !(target.query_events(locked, current_task)? & wait_object.events).is_empty() {
531                    events |= FdEvents::POLLIN;
532                    break;
533                }
534            }
535        }
536        Ok(events)
537    }
538
539    fn close(
540        self: Box<Self>,
541        _locked: &mut Locked<FileOpsCore>,
542        _file: &FileObjectState,
543        current_task: &CurrentTask,
544    ) {
545        let mut guard = self.state.lock();
546        for wait_object in guard.wait_objects.values_mut() {
547            wait_object.deactivate_wakeup_source(current_task);
548        }
549    }
550}
551
552#[derive(Clone)]
553pub struct EpollEventHandler {
554    key: ReadyItemKey,
555    waitable_state: Arc<LockDepMutex<EpollWaitableState, EpollWaitableStateLock>>,
556    waiters: Arc<WaitQueue>,
557}
558
559impl EpollEventHandler {
560    pub fn handle(self, events: FdEvents) {
561        {
562            let mut waitable_state = self.waitable_state.lock();
563            waitable_state.trigger_list.push_back(ReadyItem { key: self.key, events });
564        }
565        self.waiters.notify_fd_events(FdEvents::POLLIN);
566    }
567}
568
569impl EpollFileObject {
570    fn new_wait_handler(&self, key: ReadyItemKey) -> EventHandler {
571        EventHandler::Epoll(EpollEventHandler {
572            key,
573            waitable_state: Arc::clone(&self.waitable_state),
574            waiters: Arc::clone(&self.waiters),
575        })
576    }
577}
578
579#[cfg(test)]
580mod tests {
581    use super::*;
582    use crate::fs::fuchsia::create_fuchsia_pipe;
583    use crate::task::Waiter;
584    use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
585    use crate::testing::spawn_kernel_and_run;
586    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
587    use crate::vfs::eventfd::{EventFdType, new_eventfd};
588    use crate::vfs::fs_registry::FsRegistry;
589    use crate::vfs::pipe::{new_pipe, register_pipe_fs};
590    use crate::vfs::socket::{SocketDomain, SocketType, UnixSocket};
591    use starnix_lifecycle::AtomicCounter;
592    use starnix_sync::Unlocked;
593    use starnix_uapi::vfs::{EpollEvent, FdEvents};
594    use syncio::Zxio;
595
596    #[::fuchsia::test]
597    async fn test_epoll_read_ready() {
598        static WRITE_COUNT: AtomicCounter<usize> = AtomicCounter::<usize>::new_const(0);
599        const EVENT_DATA: u64 = 42;
600
601        spawn_kernel_and_run(async |locked, current_task| {
602            let kernel = current_task.kernel();
603            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
604
605            let (pipe_out, pipe_in) = new_pipe(locked, &current_task).unwrap();
606
607            let test_string = "hello starnix".to_string();
608            let test_len = test_string.len();
609
610            let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
611            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
612            epoll_file
613                .add(
614                    locked,
615                    &current_task,
616                    &pipe_out,
617                    &epoll_file_handle,
618                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
619                )
620                .unwrap();
621
622            let (sender, receiver) = std::sync::mpsc::channel();
623            let value = test_string.clone();
624            let closure = move |locked: &mut Locked<Unlocked>, task: &CurrentTask| {
625                let bytes_written = pipe_in
626                    .write(locked, &task, &mut VecInputBuffer::new(value.as_bytes()))
627                    .unwrap();
628                assert_eq!(bytes_written, test_len);
629                WRITE_COUNT.add(bytes_written);
630                sender.send(()).unwrap();
631            };
632            let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
633            kernel.kthreads.spawner().spawn_from_request(req);
634            let events =
635                epoll_file.wait(locked, &current_task, 10, zx::MonotonicInstant::INFINITE).unwrap();
636            receiver.recv().unwrap();
637            assert_eq!(1, events.len());
638            let event = &events[0];
639            assert!(event.events().contains(FdEvents::POLLIN));
640            assert_eq!(event.data(), EVENT_DATA);
641
642            let mut buffer = VecOutputBuffer::new(test_len);
643            let bytes_read = pipe_out.read(locked, &current_task, &mut buffer).unwrap();
644            assert_eq!(bytes_read, WRITE_COUNT.get());
645            assert_eq!(bytes_read, test_len);
646            assert_eq!(buffer.data(), test_string.as_bytes());
647        })
648        .await;
649    }
650
651    #[::fuchsia::test]
652    async fn test_epoll_ready_then_wait() {
653        const EVENT_DATA: u64 = 42;
654
655        spawn_kernel_and_run(async |locked, current_task| {
656            let kernel = current_task.kernel();
657            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
658
659            let (pipe_out, pipe_in) = new_pipe(locked, &current_task).unwrap();
660
661            let test_string = "hello starnix".to_string();
662            let test_bytes = test_string.as_bytes();
663            let test_len = test_bytes.len();
664
665            assert_eq!(
666                pipe_in.write(locked, &current_task, &mut VecInputBuffer::new(test_bytes)).unwrap(),
667                test_bytes.len()
668            );
669
670            let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
671            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
672            epoll_file
673                .add(
674                    locked,
675                    &current_task,
676                    &pipe_out,
677                    &epoll_file_handle,
678                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
679                )
680                .unwrap();
681
682            let events =
683                epoll_file.wait(locked, &current_task, 10, zx::MonotonicInstant::INFINITE).unwrap();
684            assert_eq!(1, events.len());
685            let event = &events[0];
686            assert!(event.events().contains(FdEvents::POLLIN));
687            assert_eq!(event.data(), EVENT_DATA);
688
689            let mut buffer = VecOutputBuffer::new(test_len);
690            let bytes_read = pipe_out.read(locked, &current_task, &mut buffer).unwrap();
691            assert_eq!(bytes_read, test_len);
692            assert_eq!(buffer.data(), test_bytes);
693        })
694        .await;
695    }
696
697    #[::fuchsia::test]
698    async fn test_epoll_ctl_cancel() {
699        spawn_kernel_and_run(async |locked, current_task| {
700            for do_cancel in [true, false] {
701                let event = new_eventfd(locked, &current_task, 0, EventFdType::Counter, true);
702                let waiter = Waiter::new();
703
704                let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
705                let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
706                const EVENT_DATA: u64 = 42;
707                epoll_file
708                    .add(
709                        locked,
710                        &current_task,
711                        &event,
712                        &epoll_file_handle,
713                        EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
714                    )
715                    .unwrap();
716
717                if do_cancel {
718                    epoll_file.delete(&current_task, &event).unwrap();
719                }
720
721                let wait_canceler = event
722                    .wait_async(
723                        locked,
724                        &current_task,
725                        &waiter,
726                        FdEvents::POLLIN,
727                        EventHandler::None,
728                    )
729                    .expect("wait_async");
730                if do_cancel {
731                    wait_canceler.cancel();
732                }
733
734                let add_val = 1u64;
735
736                assert_eq!(
737                    event
738                        .write(
739                            locked,
740                            &current_task,
741                            &mut VecInputBuffer::new(&add_val.to_ne_bytes())
742                        )
743                        .unwrap(),
744                    std::mem::size_of::<u64>()
745                );
746
747                let events =
748                    epoll_file.wait(locked, &current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
749
750                if do_cancel {
751                    assert_eq!(0, events.len());
752                } else {
753                    assert_eq!(1, events.len());
754                    let event = &events[0];
755                    assert!(event.events().contains(FdEvents::POLLIN));
756                    assert_eq!(event.data(), EVENT_DATA);
757                }
758            }
759        })
760        .await;
761    }
762
763    #[::fuchsia::test]
764    async fn test_multiple_events() {
765        spawn_kernel_and_run(async |locked, current_task| {
766            let (client1, server1) = zx::Socket::create_stream();
767            let (client2, server2) = zx::Socket::create_stream();
768            let pipe1 = create_fuchsia_pipe(locked, &current_task, client1, OpenFlags::RDWR)
769                .expect("create_fuchsia_pipe");
770            let pipe2 = create_fuchsia_pipe(locked, &current_task, client2, OpenFlags::RDWR)
771                .expect("create_fuchsia_pipe");
772            let server1_zxio = Zxio::create(server1.into_handle()).expect("Zxio::create");
773            let server2_zxio = Zxio::create(server2.into_handle()).expect("Zxio::create");
774
775            let poll = |locked: &mut Locked<Unlocked>| {
776                let epoll_object = EpollFileObject::new_file(locked, &current_task);
777                let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
778                epoll_file
779                    .add(
780                        locked,
781                        &current_task,
782                        &pipe1,
783                        &epoll_object,
784                        EpollEvent::new(FdEvents::POLLIN, 1),
785                    )
786                    .expect("epoll_file.add");
787                epoll_file
788                    .add(
789                        locked,
790                        &current_task,
791                        &pipe2,
792                        &epoll_object,
793                        EpollEvent::new(FdEvents::POLLIN, 2),
794                    )
795                    .expect("epoll_file.add");
796                epoll_file.wait(locked, &current_task, 2, zx::MonotonicInstant::ZERO).expect("wait")
797            };
798
799            let fds = poll(locked);
800            assert!(fds.is_empty());
801
802            assert_eq!(server1_zxio.write(&[0]).expect("write"), 1);
803
804            let fds = poll(locked);
805            assert_eq!(fds.len(), 1);
806            assert_eq!(fds[0].events(), FdEvents::POLLIN);
807            assert_eq!(fds[0].data(), 1);
808            assert_eq!(
809                pipe1.read(locked, &current_task, &mut VecOutputBuffer::new(64)).expect("read"),
810                1
811            );
812
813            let fds = poll(locked);
814            assert!(fds.is_empty());
815
816            assert_eq!(server2_zxio.write(&[0]).expect("write"), 1);
817
818            let fds = poll(locked);
819            assert_eq!(fds.len(), 1);
820            assert_eq!(fds[0].events(), FdEvents::POLLIN);
821            assert_eq!(fds[0].data(), 2);
822            assert_eq!(
823                pipe2.read(locked, &current_task, &mut VecOutputBuffer::new(64)).expect("read"),
824                1
825            );
826
827            let fds = poll(locked);
828            assert!(fds.is_empty());
829        })
830        .await;
831    }
832
833    #[::fuchsia::test]
834    async fn test_cancel_after_notify() {
835        spawn_kernel_and_run(async |locked, current_task| {
836            let event = new_eventfd(locked, &current_task, 0, EventFdType::Counter, true);
837            let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
838            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
839
840            // Add a thing
841            const EVENT_DATA: u64 = 42;
842            epoll_file
843                .add(
844                    locked,
845                    &current_task,
846                    &event,
847                    &epoll_file_handle,
848                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
849                )
850                .unwrap();
851
852            // Make the thing send a notification, wait for it
853            let add_val = 1u64;
854            assert_eq!(
855                event
856                    .write(locked, &current_task, &mut VecInputBuffer::new(&add_val.to_ne_bytes()))
857                    .unwrap(),
858                std::mem::size_of::<u64>()
859            );
860
861            assert_eq!(
862                epoll_file
863                    .wait(locked, &current_task, 10, zx::MonotonicInstant::ZERO)
864                    .unwrap()
865                    .len(),
866                1
867            );
868
869            // Remove the thing
870            epoll_file.delete(&current_task, &event).unwrap();
871
872            // Wait for new notifications
873            assert_eq!(
874                epoll_file
875                    .wait(locked, &current_task, 10, zx::MonotonicInstant::ZERO)
876                    .unwrap()
877                    .len(),
878                0
879            );
880            // That shouldn't crash
881        })
882        .await;
883    }
884
885    #[::fuchsia::test]
886    async fn test_add_then_modify() {
887        spawn_kernel_and_run(async |locked, current_task| {
888            let (socket1, _socket2) = UnixSocket::new_pair(
889                locked,
890                &current_task,
891                SocketDomain::Unix,
892                SocketType::Stream,
893                OpenFlags::RDWR,
894            )
895            .expect("Failed to create socket pair.");
896
897            let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
898            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
899
900            const EVENT_DATA: u64 = 42;
901            epoll_file
902                .add(
903                    locked,
904                    &current_task,
905                    &socket1,
906                    &epoll_file_handle,
907                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
908                )
909                .unwrap();
910            assert_eq!(
911                epoll_file
912                    .wait(locked, &current_task, 10, zx::MonotonicInstant::ZERO)
913                    .unwrap()
914                    .len(),
915                0
916            );
917
918            let read_write_event = FdEvents::POLLIN | FdEvents::POLLOUT;
919            epoll_file
920                .modify(
921                    locked,
922                    &current_task,
923                    &socket1,
924                    EpollEvent::new(read_write_event, EVENT_DATA),
925                )
926                .unwrap();
927            let triggered_events =
928                epoll_file.wait(locked, &current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
929            assert_eq!(1, triggered_events.len());
930            let event = &triggered_events[0];
931            assert_eq!(event.events(), FdEvents::POLLOUT);
932            assert_eq!(event.data(), EVENT_DATA);
933        })
934        .await;
935    }
936
937    #[::fuchsia::test]
938    async fn test_waiter_removal() {
939        spawn_kernel_and_run(async |locked, current_task| {
940            let event = new_eventfd(locked, &current_task, 0, EventFdType::Counter, true);
941            let epoll_file_handle = EpollFileObject::new_file(locked, &current_task);
942            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
943
944            const EVENT_DATA: u64 = 42;
945            epoll_file
946                .add(
947                    locked,
948                    &current_task,
949                    &event,
950                    &epoll_file_handle,
951                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
952                )
953                .unwrap();
954
955            std::mem::drop(event);
956
957            assert!(epoll_file.waiters.is_empty());
958        })
959        .await;
960    }
961}