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 rustc_hash::{FxHashMap, FxHashSet};
14use starnix_logging::log_warn;
15use starnix_sync::{EpollStateLock, EpollWaitableStateLock, LockDepMutex, allow_subclass};
16use starnix_uapi::error;
17use starnix_uapi::errors::{EINTR, ETIMEDOUT, Errno};
18use starnix_uapi::open_flags::OpenFlags;
19use starnix_uapi::vfs::{EpollEvent, FdEvents};
20use std::collections::VecDeque;
21use std::collections::hash_map::Entry;
22use std::sync::Arc;
23
24/// Maximum depth of epoll instances monitoring one another.
25/// From https://man7.org/linux/man-pages/man2/epoll_ctl.2.html
26const MAX_NESTED_DEPTH: u32 = 5;
27
28/// WaitObject represents a FileHandle that is being waited upon.
29/// The `data` field is a user defined quantity passed in
30/// via `sys_epoll_ctl`. Typically C programs could use this
31/// to store a pointer to the data that needs to be processed
32/// after an event.
33struct WaitObject {
34    target: WeakFileHandle,
35    events: FdEvents,
36    data: u64,
37    wait_canceler: Option<WaitCanceler>,
38    active_wakeup_source: Option<WakeupSourceOrigin>,
39}
40
41impl WaitObject {
42    /// Returns the target `FileHandle` of the `WaitObject`, or `None` if the file has been closed.
43    ///
44    /// It is fine for the `FileHandle` to be closed after being added to an epoll, and subsequent
45    /// epoll_waits end up timing out (importantly not returning EBADF).
46    fn target(&self) -> Option<FileHandle> {
47        self.target.upgrade()
48    }
49
50    fn deactivate_wakeup_source(&mut self, current_task: &CurrentTask) {
51        if let Some(origin) = self.active_wakeup_source.take() {
52            current_task.kernel().suspend_resume_manager.deactivate_wakeup_source(&origin);
53        }
54    }
55}
56
57/// EpollKey acts as an key to a map of WaitObject.
58/// In reality it is a pointer to a FileHandle object.
59pub type EpollKey = usize;
60
61/// EpollFileObject represents the FileObject used to
62/// implement epoll_create1/epoll_ctl/epoll_pwait.
63#[derive(Default)]
64pub struct EpollFileObject {
65    waiter: Waiter,
66    /// Mutable state of this epoll object.
67    state: LockDepMutex<EpollState, EpollStateLock>,
68    waitable_state: Arc<LockDepMutex<EpollWaitableState, EpollWaitableStateLock>>,
69    /// A list of waiters waiting for events from this
70    /// epoll instance.
71    waiters: Arc<WaitQueue>,
72}
73
74#[derive(Default)]
75struct EpollState {
76    /// Any file tracked by this epoll instance
77    /// will exist as a key in `wait_objects`.
78    ///
79    /// The keys are kernel-assigned file object IDs, so the faster non-DoS-resistant
80    /// `FxHashMap` is safe here.
81    wait_objects: FxHashMap<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(current_task: &CurrentTask) -> FileHandle {
108        let epoll = Box::new(EpollFileObject::default());
109
110        #[cfg(any(test, debug_assertions))]
111        {
112            let _l1 = epoll.state.lock();
113            let _l2 = epoll.waitable_state.lock();
114        }
115
116        Anon::new_private_file(current_task, epoll, OpenFlags::RDWR, "[eventpoll]")
117    }
118
119    fn wait_on_file(
120        &self,
121        current_task: &CurrentTask,
122        key: ReadyItemKey,
123        wait_object: &mut WaitObject,
124    ) -> Result<(), Errno> {
125        // First start the wait. If an event happens after this, we'll get it.
126        self.wait_on_file_edge_triggered(current_task, key, wait_object)?;
127
128        self.do_recheck(current_task, wait_object, key)?;
129
130        Ok(())
131    }
132
133    fn do_recheck(
134        &self,
135        current_task: &CurrentTask,
136        wait_object: &mut WaitObject,
137        key: ReadyItemKey,
138    ) -> Result<(), Errno> {
139        let Some(target) = wait_object.target() else { return Ok(()) };
140        let events = {
141            // Target might be itself an epoll object. Because there is no loop,
142            // this allow_subclass is safe.
143            let _token = allow_subclass();
144            target.query_events(current_task)?
145        };
146        if !(events & wait_object.events).is_empty() {
147            self.waiter.wake_immediately(events, self.new_wait_handler(key));
148            if let Some(wait_canceler) = wait_object.wait_canceler.take() {
149                wait_canceler.cancel();
150            } else {
151                log_warn!("wait canceler should have been set by `wait_on_file_edge_triggered`");
152            }
153        }
154        Ok(())
155    }
156
157    fn wait_on_file_edge_triggered(
158        &self,
159        current_task: &CurrentTask,
160        key: ReadyItemKey,
161        wait_object: &mut WaitObject,
162    ) -> Result<(), Errno> {
163        let Some(target) = wait_object.target() else {
164            return Ok(());
165        };
166
167        wait_object.wait_canceler = target.wait_async(
168            current_task,
169            &self.waiter,
170            wait_object.events,
171            self.new_wait_handler(key),
172        );
173        if wait_object.wait_canceler.is_none() {
174            return error!(EPERM);
175        }
176        Ok(())
177    }
178
179    /// Checks if adding self to the `epoll_file_object` at `epoll_file_handle` would cause a loop
180    /// or exceed max depth.
181    fn check_eloop(&self, parent: &FileHandle, depth_left: u32) -> Result<(), Errno> {
182        if depth_left == 0 {
183            return error!(ELOOP);
184        }
185
186        let state = self.state.lock();
187        for nested_object in state.wait_objects.values() {
188            let Some(child) = nested_object.target() else {
189                continue;
190            };
191            let Some(child_file) = child.downcast_file::<EpollFileObject>() else {
192                continue;
193            };
194
195            if Arc::ptr_eq(&child, parent) {
196                return error!(ELOOP);
197            }
198            // Child is not part of a loop, so subclassing is safe.
199            let _token = allow_subclass();
200            child_file.check_eloop(parent, depth_left - 1)?;
201        }
202
203        Ok(())
204    }
205
206    /// Asynchronously wait on certain events happening on a FileHandle.
207    pub fn add(
208        &self,
209        current_task: &CurrentTask,
210        file: &FileHandle,
211        epoll_file_handle: &FileHandle,
212        epoll_event: EpollEvent,
213    ) -> Result<(), Errno> {
214        // Check if adding this file would cause a cycle at a max depth of 5.
215        if let Some(epoll_to_add) = file.downcast_file::<EpollFileObject>() {
216            // We need to check for `MAX_NESTED_DEPTH - 1` because adding `epoll_to_add` to self
217            // would result in a total depth of one more.
218            epoll_to_add.check_eloop(epoll_file_handle, MAX_NESTED_DEPTH - 1)?;
219        }
220
221        let mut state = self.state.lock();
222        let key = file.id.as_epoll_key().into();
223        match state.wait_objects.entry(key) {
224            Entry::Occupied(_) => error!(EEXIST),
225            Entry::Vacant(entry) => {
226                let wait_object = entry.insert(WaitObject {
227                    target: Arc::downgrade(file),
228                    events: epoll_event.events() | FdEvents::POLLHUP | FdEvents::POLLERR,
229                    data: epoll_event.data(),
230                    wait_canceler: None,
231                    active_wakeup_source: None,
232                });
233                self.wait_on_file(current_task, key, wait_object)
234            }
235        }
236    }
237
238    /// Modify the events we are looking for on a Filehandle.
239    pub fn modify(
240        &self,
241        current_task: &CurrentTask,
242        file: &FileHandle,
243        epoll_event: EpollEvent,
244    ) -> Result<(), Errno> {
245        let mut state = self.state.lock();
246        let key = file.id.as_epoll_key();
247        state.recheck_list.retain(|x| *x != key.into());
248        let Some(wait_object) = state.wait_objects.get_mut(&key.into()) else {
249            return error!(ENOENT);
250        };
251        if let Some(wait_canceler) = wait_object.wait_canceler.take() {
252            wait_canceler.cancel();
253        }
254        wait_object.events = epoll_event.events() | FdEvents::POLLHUP | FdEvents::POLLERR;
255        wait_object.data = epoll_event.data();
256        // If the new epoll event doesn't include EPOLLWAKEUP, we need to take down the
257        // wake lease. This ensures that the system doesn't stay awake unnecessarily when
258        // the event no longer requires it to be awake.
259        if wait_object.events.contains(FdEvents::EPOLLWAKEUP)
260            && !epoll_event.events().contains(FdEvents::EPOLLWAKEUP)
261        {
262            wait_object.deactivate_wakeup_source(current_task);
263        }
264        self.wait_on_file(current_task, key.into(), wait_object)
265    }
266
267    /// Cancel an asynchronous wait on an object. Events triggered before
268    /// calling this will still be delivered.
269    pub fn delete(&self, current_task: &CurrentTask, file: &FileObject) -> Result<(), Errno> {
270        let mut state = self.state.lock();
271        let key = file.id.as_epoll_key().into();
272        if let Some(mut wait_object) = state.wait_objects.remove(&key) {
273            if let Some(wait_canceler) = wait_object.wait_canceler.take() {
274                wait_canceler.cancel();
275            }
276            state.recheck_list.retain(|x| *x != key);
277            // Deactivate the wake lock if it was active.
278            wait_object.deactivate_wakeup_source(current_task);
279            Ok(())
280        } else {
281            error!(ENOENT)
282        }
283    }
284
285    /// Stores events from the Epoll's trigger list to the parameter `pending_list`. This does not
286    /// actually invoke the waiter which is how items are added to the trigger list. The caller
287    /// will have to do that before calling if needed.
288    ///
289    /// If an event in the trigger list is stale, the event will be re-added to the waiter.
290    ///
291    /// Returns true if any events were added. False means there was nothing in the trigger list.
292    fn process_triggered_events(
293        &self,
294        current_task: &CurrentTask,
295        pending_list: &mut Vec<ReadyItem>,
296        max_events: usize,
297    ) -> Result<(), Errno> {
298        let mut state = self.state.lock();
299        // Move all the elements from `self.trigger_list` to this intermediary
300        // queue that we handle events from. This reduces the time spent holding
301        // `self.trigger_list`'s lock which reduces contention with objects that
302        // this epoll object has subscribed for notifications from.
303        state.processing_list.append(&mut self.waitable_state.lock().trigger_list);
304        while pending_list.len() < max_events && !state.processing_list.is_empty() {
305            if let Some(pending) = state.processing_list.pop_front() {
306                if let Some(wait) = state.wait_objects.get_mut(&pending.key) {
307                    // The weak pointer to the FileObject target can be gone if the file was closed
308                    // out from under us. If this happens it is not an error: ignore it and
309                    // continue.
310                    if let Some(target) = wait.target.upgrade() {
311                        let events = {
312                            // Target might be itself an epoll object. Because there is no loop,
313                            // this allow_subclass is safe.
314                            let _token = allow_subclass();
315                            target.query_events(current_task)?
316                        };
317                        let ready = ReadyItem { key: pending.key, events };
318                        if ready.events.intersects(wait.events) {
319                            pending_list.push(ready);
320                        } else {
321                            // Another thread already handled this event, wait for another one.
322                            self.wait_on_file(current_task, pending.key, wait)?;
323                        }
324                    }
325                }
326            }
327        }
328        Ok(())
329    }
330
331    /// Waits until an event exists in `pending_list` or until `timeout` has
332    /// been reached.
333    fn wait_until_pending_event(
334        &self,
335        current_task: &CurrentTask,
336        max_events: usize,
337        mut wait_deadline: zx::MonotonicInstant,
338    ) -> Result<Vec<ReadyItem>, Errno> {
339        let mut pending_list = Vec::new();
340
341        loop {
342            self.process_triggered_events(current_task, &mut pending_list, max_events)?;
343
344            if pending_list.len() == max_events {
345                break; // No input events or output list full, nothing more we can do.
346            }
347
348            if !pending_list.is_empty() {
349                // We now know we have at least one event to return. We shouldn't return
350                // immediately, in case there are more events available, but the next loop should
351                // wait with a 0 timeout to prevent further blocking.
352                wait_deadline = zx::MonotonicInstant::ZERO;
353            }
354
355            // Loop back to check if there are more items in the Waiter's queue. Every wait_until()
356            // call will process a single event. In order to drain as many events as we can that
357            // are synchronously available, keep trying until it reports empty.
358            //
359            // The handlers in the waits cause items to be appended to trigger_list. See the closure
360            // in `wait_on_file` to see how this happens.
361            //
362            // This wait may return EINTR for nonzero timeouts which is not an error. We must be
363            // careful not to lose events if this happens.
364            //
365            // The first time through this loop we'll use the timeout passed into this function so
366            // can get EINTR. But since we haven't done anything or accumulated any results yet it's
367            // OK to immediately return and no information will be lost.
368            match self.waiter.wait_until(current_task, wait_deadline) {
369                Err(err) if err == ETIMEDOUT => break,
370                Err(err) if err == EINTR => {
371                    // Terminating early will lose any events in the pending_list so that should
372                    // only be for unrecoverable errors (not EINTR). The only time there should be a
373                    // nonzero wait_deadline (and hence the ability to encounter EINTR) is when the
374                    // pending list is empty.
375                    debug_assert!(
376                        pending_list.is_empty(),
377                        "Got EINTR from wait of {}ns with {} items pending.",
378                        wait_deadline.into_nanos(),
379                        pending_list.len()
380                    );
381                    return Err(err);
382                }
383                // TODO check if this is supposed to actually fail!
384                result => result?,
385            }
386        }
387
388        Ok(pending_list)
389    }
390
391    /// Blocking wait on all waited upon events with a timeout.
392    pub fn wait(
393        &self,
394        current_task: &CurrentTask,
395        max_events: usize,
396        deadline: zx::MonotonicInstant,
397    ) -> Result<Vec<EpollEvent>, Errno> {
398        {
399            let mut state = self.state.lock();
400            let recheck_list = std::mem::take(&mut state.recheck_list);
401            for key in &recheck_list {
402                let wait_object = state.wait_objects.get_mut(key).unwrap();
403                wait_object.deactivate_wakeup_source(current_task);
404            }
405            for (idx, key) in recheck_list.iter().enumerate() {
406                let wait_object = state.wait_objects.get_mut(key).unwrap();
407                if let Err(err) = self.do_recheck(current_task, wait_object, *key) {
408                    // Put remaining unprocessed entries (including the failing one) back
409                    // onto `recheck_list` so they aren't lost and can be rechecked on the next wait.
410                    state.recheck_list.extend(recheck_list[idx..].iter().copied());
411                    return Err(err);
412                }
413            }
414        }
415
416        let pending_list = self.wait_until_pending_event(current_task, max_events, deadline)?;
417
418        // Process the pending list and add processed ReadyItem
419        // entries to the rearm_list for the next wait.
420        let mut result = vec![];
421        let mut state = self.state.lock();
422        let state = &mut *state;
423        let mut seen = FxHashSet::with_capacity_and_hasher(pending_list.len(), Default::default());
424        for pending_event in pending_list.iter().filter(|e| seen.insert(e.key)) {
425            // The wait could have been deleted by here,
426            // so ignore the None case.
427            let Some(wait) = state.wait_objects.get_mut(&pending_event.key) else { continue };
428
429            let reported_events = pending_event.events & wait.events;
430            result.push(EpollEvent::new(reported_events, wait.data));
431
432            // Files marked with `EPOLLONESHOT` should only notify
433            // once and need to be rearmed manually with epoll_ctl_mod().
434            if wait.events.contains(FdEvents::EPOLLONESHOT) {
435                continue;
436            }
437
438            self.wait_on_file_edge_triggered(current_task, pending_event.key, wait)?;
439
440            if !wait.events.contains(FdEvents::EPOLLET) {
441                state.recheck_list.push(pending_event.key);
442            }
443
444            // TODO: is this really only supposed to happen for level-triggered events?
445            if !wait.events.contains(FdEvents::EPOLLET) {
446                // When this is the first time epoll_wait on this epoll fd, create and
447                // hold a wake lease until the next epoll_wait.
448                if wait.events.contains(FdEvents::EPOLLWAKEUP) {
449                    if let ReadyItemKey::Usize(key) = pending_event.key {
450                        let origin = WakeupSourceOrigin::Epoll(key);
451                        current_task
452                            .kernel()
453                            .suspend_resume_manager
454                            .activate_wakeup_source_with_actor(
455                                origin.clone(),
456                                Some(current_task.command()),
457                            );
458                        wait.active_wakeup_source = Some(origin);
459                    }
460                }
461            }
462        }
463
464        Ok(result)
465    }
466}
467
468impl FileOps for EpollFileObject {
469    fileops_impl_nonseekable!();
470    fileops_impl_noop_sync!();
471    fileops_impl_dataless!();
472
473    fn wait_async(
474        &self,
475        _file: &FileObject,
476        _current_task: &CurrentTask,
477        waiter: &Waiter,
478        events: FdEvents,
479        handler: EventHandler,
480    ) -> Option<WaitCanceler> {
481        Some(self.waiters.wait_async_fd_events(waiter, events, handler))
482    }
483
484    fn query_events(
485        &self,
486        _file: &FileObject,
487        current_task: &CurrentTask,
488    ) -> Result<FdEvents, Errno> {
489        let mut events = FdEvents::empty();
490        let state = self.state.lock();
491        if !state.processing_list.is_empty() || !self.waitable_state.lock().trigger_list.is_empty()
492        {
493            events |= FdEvents::POLLIN;
494        } else {
495            for key in &state.recheck_list {
496                let wait_object = state.wait_objects.get(key).unwrap();
497                let Some(target) = wait_object.target() else { continue };
498                // Target might be itself an epoll object. Because there is no loop,
499                // this allow_subclass is safe.
500                let _token = allow_subclass();
501                if !(target.query_events(current_task)? & wait_object.events).is_empty() {
502                    events |= FdEvents::POLLIN;
503                    break;
504                }
505            }
506        }
507        Ok(events)
508    }
509
510    fn close(self: Box<Self>, _file: &FileObjectState, current_task: &CurrentTask) {
511        let mut guard = self.state.lock();
512        for wait_object in guard.wait_objects.values_mut() {
513            wait_object.deactivate_wakeup_source(current_task);
514        }
515    }
516}
517
518#[derive(Clone)]
519pub struct EpollEventHandler {
520    key: ReadyItemKey,
521    waitable_state: Arc<LockDepMutex<EpollWaitableState, EpollWaitableStateLock>>,
522    waiters: Arc<WaitQueue>,
523}
524
525impl EpollEventHandler {
526    pub fn handle(self, events: FdEvents) {
527        {
528            let mut waitable_state = self.waitable_state.lock();
529            waitable_state.trigger_list.push_back(ReadyItem { key: self.key, events });
530        }
531        self.waiters.notify_fd_events(FdEvents::POLLIN);
532    }
533}
534
535impl EpollFileObject {
536    fn new_wait_handler(&self, key: ReadyItemKey) -> EventHandler {
537        EventHandler::Epoll(EpollEventHandler {
538            key,
539            waitable_state: Arc::clone(&self.waitable_state),
540            waiters: Arc::clone(&self.waiters),
541        })
542    }
543}
544
545#[cfg(test)]
546mod tests {
547    use super::*;
548    use crate::fs::fuchsia::create_fuchsia_pipe;
549    use crate::task::Waiter;
550    use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
551    use crate::testing::{anon_test_file, spawn_kernel_and_run};
552    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
553    use crate::vfs::eventfd::{EventFdType, new_eventfd};
554    use crate::vfs::fs_registry::FsRegistry;
555    use crate::vfs::pipe::{new_pipe, register_pipe_fs};
556    use crate::vfs::socket::{SocketDomain, SocketType, UnixSocket};
557    use starnix_lifecycle::AtomicCounter;
558    use starnix_sync::Mutex;
559    use starnix_uapi::vfs::{EpollEvent, FdEvents};
560    use syncio::Zxio;
561
562    #[::fuchsia::test]
563    async fn test_epoll_read_ready() {
564        static WRITE_COUNT: AtomicCounter<usize> = AtomicCounter::<usize>::new_const(0);
565        const EVENT_DATA: u64 = 42;
566
567        spawn_kernel_and_run(async |current_task| {
568            let kernel = current_task.kernel();
569            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
570
571            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
572
573            let test_string = "hello starnix".to_string();
574            let test_len = test_string.len();
575
576            let epoll_file_handle = EpollFileObject::new_file(&current_task);
577            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
578            epoll_file
579                .add(
580                    &current_task,
581                    &pipe_out,
582                    &epoll_file_handle,
583                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
584                )
585                .unwrap();
586
587            let (sender, receiver) = std::sync::mpsc::channel();
588            let value = test_string.clone();
589            let closure = move |task: &CurrentTask| {
590                let bytes_written =
591                    pipe_in.write(&task, &mut VecInputBuffer::new(value.as_bytes())).unwrap();
592                assert_eq!(bytes_written, test_len);
593                WRITE_COUNT.add(bytes_written);
594                sender.send(()).unwrap();
595            };
596            let req = SpawnRequestBuilder::new().with_sync_closure(closure).build();
597            kernel.kthreads.spawner().spawn_from_request(req);
598            let events =
599                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::INFINITE).unwrap();
600            receiver.recv().unwrap();
601            assert_eq!(1, events.len());
602            let event = &events[0];
603            assert!(event.events().contains(FdEvents::POLLIN));
604            assert_eq!(event.data(), EVENT_DATA);
605
606            let mut buffer = VecOutputBuffer::new(test_len);
607            let bytes_read = pipe_out.read(&current_task, &mut buffer).unwrap();
608            assert_eq!(bytes_read, WRITE_COUNT.get());
609            assert_eq!(bytes_read, test_len);
610            assert_eq!(buffer.data(), test_string.as_bytes());
611        })
612        .await;
613    }
614
615    #[::fuchsia::test]
616    async fn test_epoll_ready_then_wait() {
617        const EVENT_DATA: u64 = 42;
618
619        spawn_kernel_and_run(async |current_task| {
620            let kernel = current_task.kernel();
621            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
622
623            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
624
625            let test_string = "hello starnix".to_string();
626            let test_bytes = test_string.as_bytes();
627            let test_len = test_bytes.len();
628
629            assert_eq!(
630                pipe_in.write(&current_task, &mut VecInputBuffer::new(test_bytes)).unwrap(),
631                test_bytes.len()
632            );
633
634            let epoll_file_handle = EpollFileObject::new_file(&current_task);
635            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
636            epoll_file
637                .add(
638                    &current_task,
639                    &pipe_out,
640                    &epoll_file_handle,
641                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
642                )
643                .unwrap();
644
645            let events =
646                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::INFINITE).unwrap();
647            assert_eq!(1, events.len());
648            let event = &events[0];
649            assert!(event.events().contains(FdEvents::POLLIN));
650            assert_eq!(event.data(), EVENT_DATA);
651
652            let mut buffer = VecOutputBuffer::new(test_len);
653            let bytes_read = pipe_out.read(&current_task, &mut buffer).unwrap();
654            assert_eq!(bytes_read, test_len);
655            assert_eq!(buffer.data(), test_bytes);
656        })
657        .await;
658    }
659
660    #[::fuchsia::test]
661    async fn test_epoll_ctl_cancel() {
662        spawn_kernel_and_run(async |current_task| {
663            for do_cancel in [true, false] {
664                let event = new_eventfd(&current_task, 0, EventFdType::Counter, true);
665                let waiter = Waiter::new();
666
667                let epoll_file_handle = EpollFileObject::new_file(&current_task);
668                let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
669                const EVENT_DATA: u64 = 42;
670                epoll_file
671                    .add(
672                        &current_task,
673                        &event,
674                        &epoll_file_handle,
675                        EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
676                    )
677                    .unwrap();
678
679                if do_cancel {
680                    epoll_file.delete(&current_task, &event).unwrap();
681                }
682
683                let wait_canceler = event
684                    .wait_async(&current_task, &waiter, FdEvents::POLLIN, EventHandler::None)
685                    .expect("wait_async");
686                if do_cancel {
687                    wait_canceler.cancel();
688                }
689
690                let add_val = 1u64;
691
692                assert_eq!(
693                    event
694                        .write(&current_task, &mut VecInputBuffer::new(&add_val.to_ne_bytes()))
695                        .unwrap(),
696                    std::mem::size_of::<u64>()
697                );
698
699                let events =
700                    epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
701
702                if do_cancel {
703                    assert_eq!(0, events.len());
704                } else {
705                    assert_eq!(1, events.len());
706                    let event = &events[0];
707                    assert!(event.events().contains(FdEvents::POLLIN));
708                    assert_eq!(event.data(), EVENT_DATA);
709                }
710            }
711        })
712        .await;
713    }
714
715    #[::fuchsia::test]
716    async fn test_multiple_events() {
717        spawn_kernel_and_run(async |current_task| {
718            let (client1, server1) = zx::Socket::create_stream();
719            let (client2, server2) = zx::Socket::create_stream();
720            let pipe1 = create_fuchsia_pipe(&current_task, client1, OpenFlags::RDWR)
721                .expect("create_fuchsia_pipe");
722            let pipe2 = create_fuchsia_pipe(&current_task, client2, OpenFlags::RDWR)
723                .expect("create_fuchsia_pipe");
724            let server1_zxio = Zxio::create(server1.into_handle()).expect("Zxio::create");
725            let server2_zxio = Zxio::create(server2.into_handle()).expect("Zxio::create");
726
727            let poll = || {
728                let epoll_object = EpollFileObject::new_file(&current_task);
729                let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
730                epoll_file
731                    .add(&current_task, &pipe1, &epoll_object, EpollEvent::new(FdEvents::POLLIN, 1))
732                    .expect("epoll_file.add");
733                epoll_file
734                    .add(&current_task, &pipe2, &epoll_object, EpollEvent::new(FdEvents::POLLIN, 2))
735                    .expect("epoll_file.add");
736                epoll_file.wait(&current_task, 2, zx::MonotonicInstant::ZERO).expect("wait")
737            };
738
739            let fds = poll();
740            assert!(fds.is_empty());
741
742            assert_eq!(server1_zxio.write(&[0]).expect("write"), 1);
743
744            let fds = poll();
745            assert_eq!(fds.len(), 1);
746            assert_eq!(fds[0].events(), FdEvents::POLLIN);
747            assert_eq!(fds[0].data(), 1);
748            assert_eq!(pipe1.read(&current_task, &mut VecOutputBuffer::new(64)).expect("read"), 1);
749
750            let fds = poll();
751            assert!(fds.is_empty());
752
753            assert_eq!(server2_zxio.write(&[0]).expect("write"), 1);
754
755            let fds = poll();
756            assert_eq!(fds.len(), 1);
757            assert_eq!(fds[0].events(), FdEvents::POLLIN);
758            assert_eq!(fds[0].data(), 2);
759            assert_eq!(pipe2.read(&current_task, &mut VecOutputBuffer::new(64)).expect("read"), 1);
760
761            let fds = poll();
762            assert!(fds.is_empty());
763        })
764        .await;
765    }
766
767    #[::fuchsia::test]
768    async fn test_cancel_after_notify() {
769        spawn_kernel_and_run(async |current_task| {
770            let event = new_eventfd(&current_task, 0, EventFdType::Counter, true);
771            let epoll_file_handle = EpollFileObject::new_file(&current_task);
772            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
773
774            // Add a thing
775            const EVENT_DATA: u64 = 42;
776            epoll_file
777                .add(
778                    &current_task,
779                    &event,
780                    &epoll_file_handle,
781                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
782                )
783                .unwrap();
784
785            // Make the thing send a notification, wait for it
786            let add_val = 1u64;
787            assert_eq!(
788                event
789                    .write(&current_task, &mut VecInputBuffer::new(&add_val.to_ne_bytes()))
790                    .unwrap(),
791                std::mem::size_of::<u64>()
792            );
793
794            assert_eq!(
795                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap().len(),
796                1
797            );
798
799            // Remove the thing
800            epoll_file.delete(&current_task, &event).unwrap();
801
802            // Wait for new notifications
803            assert_eq!(
804                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap().len(),
805                0
806            );
807            // That shouldn't crash
808        })
809        .await;
810    }
811
812    #[::fuchsia::test]
813    async fn test_add_then_modify() {
814        spawn_kernel_and_run(async |current_task| {
815            let (socket1, _socket2) = UnixSocket::new_pair(
816                &current_task,
817                SocketDomain::Unix,
818                SocketType::Stream,
819                OpenFlags::RDWR,
820            )
821            .expect("Failed to create socket pair.");
822
823            let epoll_file_handle = EpollFileObject::new_file(&current_task);
824            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
825
826            const EVENT_DATA: u64 = 42;
827            epoll_file
828                .add(
829                    &current_task,
830                    &socket1,
831                    &epoll_file_handle,
832                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
833                )
834                .unwrap();
835            assert_eq!(
836                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap().len(),
837                0
838            );
839
840            let read_write_event = FdEvents::POLLIN | FdEvents::POLLOUT;
841            epoll_file
842                .modify(&current_task, &socket1, EpollEvent::new(read_write_event, EVENT_DATA))
843                .unwrap();
844            let triggered_events =
845                epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
846            assert_eq!(1, triggered_events.len());
847            let event = &triggered_events[0];
848            assert_eq!(event.events(), FdEvents::POLLOUT);
849            assert_eq!(event.data(), EVENT_DATA);
850        })
851        .await;
852    }
853
854    #[::fuchsia::test]
855    async fn test_waiter_removal() {
856        spawn_kernel_and_run(async |current_task| {
857            let event = new_eventfd(&current_task, 0, EventFdType::Counter, true);
858            let epoll_file_handle = EpollFileObject::new_file(&current_task);
859            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
860
861            const EVENT_DATA: u64 = 42;
862            epoll_file
863                .add(
864                    &current_task,
865                    &event,
866                    &epoll_file_handle,
867                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
868                )
869                .unwrap();
870
871            std::mem::drop(event);
872
873            assert!(epoll_file.waiters.is_empty());
874        })
875        .await;
876    }
877
878    /// A test file whose event query results can be dynamically controlled.
879    ///
880    /// This allows tests to simulate scenarios where `query_events` succeeds initially
881    /// (e.g., during epoll registration and the first `wait`), but returns an error on a
882    /// subsequent call (e.g., when `do_recheck` re-evaluates `recheck_list`).
883    #[derive(Clone)]
884    struct ControlledEventsFile {
885        wait_queue: Arc<WaitQueue>,
886        events: Arc<Mutex<Result<FdEvents, Errno>>>,
887    }
888
889    impl ControlledEventsFile {
890        fn new(events: FdEvents) -> Self {
891            Self {
892                wait_queue: Arc::new(WaitQueue::default()),
893                events: Arc::new(Mutex::new(Ok(events))),
894            }
895        }
896
897        fn set_events(&self, events: Result<FdEvents, Errno>) {
898            *self.events.lock() = events;
899        }
900    }
901
902    impl FileOps for ControlledEventsFile {
903        fileops_impl_nonseekable!();
904        fileops_impl_noop_sync!();
905        fileops_impl_dataless!();
906
907        fn wait_async(
908            &self,
909            _file: &FileObject,
910            _current_task: &CurrentTask,
911            waiter: &Waiter,
912            events: FdEvents,
913            handler: EventHandler,
914        ) -> Option<WaitCanceler> {
915            Some(self.wait_queue.wait_async_fd_events(waiter, events, handler))
916        }
917
918        fn query_events(
919            &self,
920            _file: &FileObject,
921            _current_task: &CurrentTask,
922        ) -> Result<FdEvents, Errno> {
923            self.events.lock().clone()
924        }
925    }
926
927    #[::fuchsia::test]
928    async fn test_epoll_recheck_failure_deactivates_wakeup_sources() {
929        spawn_kernel_and_run(async |current_task| {
930            let epoll_file_handle = EpollFileObject::new_file(&current_task);
931            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
932
933            let ops1 = ControlledEventsFile::new(FdEvents::POLLIN);
934            let file1 = anon_test_file(&current_task, Box::new(ops1.clone()), OpenFlags::RDWR);
935            let ops2 = ControlledEventsFile::new(FdEvents::POLLIN);
936            let file2 = anon_test_file(&current_task, Box::new(ops2.clone()), OpenFlags::RDWR);
937
938            epoll_file
939                .add(
940                    &current_task,
941                    &file1,
942                    &epoll_file_handle,
943                    EpollEvent::new(FdEvents::POLLIN | FdEvents::EPOLLWAKEUP, 1),
944                )
945                .unwrap();
946            epoll_file
947                .add(
948                    &current_task,
949                    &file2,
950                    &epoll_file_handle,
951                    EpollEvent::new(FdEvents::POLLIN | FdEvents::EPOLLWAKEUP, 2),
952                )
953                .unwrap();
954
955            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
956            assert_eq!(events.len(), 2);
957            assert!(!current_task.kernel().suspend_resume_manager.lock().can_suspend());
958
959            ops1.set_events(error!(EPERM));
960            ops2.set_events(error!(EPERM));
961
962            assert!(epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).is_err());
963            assert!(current_task.kernel().suspend_resume_manager.lock().can_suspend());
964        })
965        .await;
966    }
967
968    #[::fuchsia::test]
969    async fn test_level_triggered_remains_ready_until_drained() {
970        // Validates standard level-triggered epoll semantics:
971        // A level-triggered file remains ready across consecutive wait() calls as long
972        // as data remains in its buffer. Once drained, wait() returns 0 events.
973        // Once new data arrives, wait() returns ready again.
974        spawn_kernel_and_run(async |current_task| {
975            let kernel = current_task.kernel();
976            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
977
978            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
979            let epoll_file_handle = EpollFileObject::new_file(&current_task);
980            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
981            const EVENT_DATA: u64 = 100;
982            epoll_file
983                .add(
984                    &current_task,
985                    &pipe_out,
986                    &epoll_file_handle,
987                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
988                )
989                .unwrap();
990
991            let key: ReadyItemKey = pipe_out.id.as_epoll_key().into();
992
993            // Write 2 bytes.
994            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[1, 2])).unwrap();
995
996            // First wait: harvests ready event.
997            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
998            assert_eq!(events.len(), 1);
999            assert_eq!(events[0].data(), EVENT_DATA);
1000
1001            // Level-triggered files are added to recheck_list so subsequent waits recheck readiness.
1002            assert!(epoll_file.state.lock().recheck_list.contains(&key));
1003
1004            // Second wait without reading data: the file is STILL READY and must be returned again.
1005            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1006            assert_eq!(events.len(), 1);
1007            assert_eq!(events[0].data(), EVENT_DATA);
1008
1009            // Drain the pipe completely.
1010            let mut out_buf = VecOutputBuffer::new(10);
1011            assert_eq!(pipe_out.read(&current_task, &mut out_buf).unwrap(), 2);
1012
1013            // Third wait: the file is no longer ready, wait returns 0 events.
1014            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1015            assert_eq!(events.len(), 0);
1016
1017            // Write new data: the wait fires and returns the event.
1018            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[3])).unwrap();
1019            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1020            assert_eq!(events.len(), 1);
1021            assert_eq!(events[0].data(), EVENT_DATA);
1022        })
1023        .await;
1024    }
1025
1026    #[::fuchsia::test]
1027    async fn test_nested_epoll_delayed_drain() {
1028        // In nested epoll (ep_outer -> ep_inner -> target), when target has data:
1029        // 1. ep_inner.wait() returns target.
1030        // 2. Target is not drained immediately (delayed drain).
1031        // 3. ep_outer.wait() observes ep_inner is ready.
1032        // 4. Target is then drained by user space.
1033        // 5. Remote peer writes new data to target.
1034        // 6. ep_outer and ep_inner MUST both receive notifications for the new data and not hang.
1035        spawn_kernel_and_run(async |current_task| {
1036            let kernel = current_task.kernel();
1037            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1038
1039            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1040            let epoll_inner_handle = EpollFileObject::new_file(&current_task);
1041            let epoll_inner = epoll_inner_handle.downcast_file::<EpollFileObject>().unwrap();
1042            let epoll_outer_handle = EpollFileObject::new_file(&current_task);
1043            let epoll_outer = epoll_outer_handle.downcast_file::<EpollFileObject>().unwrap();
1044
1045            const INNER_DATA: u64 = 1;
1046            const OUTER_DATA: u64 = 2;
1047
1048            epoll_inner
1049                .add(
1050                    &current_task,
1051                    &pipe_out,
1052                    &epoll_inner_handle,
1053                    EpollEvent::new(FdEvents::POLLIN, INNER_DATA),
1054                )
1055                .unwrap();
1056            epoll_outer
1057                .add(
1058                    &current_task,
1059                    &epoll_inner_handle,
1060                    &epoll_outer_handle,
1061                    EpollEvent::new(FdEvents::POLLIN, OUTER_DATA),
1062                )
1063                .unwrap();
1064
1065            // Step 1: Write initial data to pipe.
1066            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[1, 2])).unwrap();
1067
1068            // Step 2: Harvest in ep_inner.wait().
1069            let inner_events =
1070                epoll_inner.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1071            assert_eq!(inner_events.len(), 1);
1072
1073            // Step 3: Do NOT drain pipe_out yet. It is still ready.
1074            // Step 4: ep_outer waits on ep_inner.
1075            let outer_events =
1076                epoll_outer.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1077            assert_eq!(outer_events.len(), 1);
1078            assert_eq!(outer_events[0].data(), OUTER_DATA);
1079
1080            // Step 5: User drains pipe_out now.
1081            let mut out_buf = VecOutputBuffer::new(10);
1082            assert_eq!(pipe_out.read(&current_task, &mut out_buf).unwrap(), 2);
1083
1084            // Step 6: Remote peer sends new data to pipe_out.
1085            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[3, 4])).unwrap();
1086
1087            // Step 7: ep_outer must observe the new event without hanging.
1088            let outer_events2 =
1089                epoll_outer.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1090            assert_eq!(outer_events2.len(), 1);
1091            assert_eq!(outer_events2[0].data(), OUTER_DATA);
1092
1093            // ep_inner also sees the new event.
1094            let inner_events2 =
1095                epoll_inner.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1096            assert_eq!(inner_events2.len(), 1);
1097            assert_eq!(inner_events2[0].data(), INNER_DATA);
1098        })
1099        .await;
1100    }
1101
1102    #[::fuchsia::test]
1103    async fn test_epoll_wait_with_external_waiter_preserves_wait_canceler() {
1104        // Validates that external waiters registered via wait_async on an epoll object
1105        // correctly receive event notifications when target events occur.
1106        spawn_kernel_and_run(async |current_task| {
1107            let kernel = current_task.kernel();
1108            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1109
1110            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1111            let epoll_file_handle = EpollFileObject::new_file(&current_task);
1112            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
1113            const EVENT_DATA: u64 = 42;
1114            epoll_file
1115                .add(
1116                    &current_task,
1117                    &pipe_out,
1118                    &epoll_file_handle,
1119                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
1120                )
1121                .unwrap();
1122
1123            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[1])).unwrap();
1124
1125            // Register an external waiter on epoll_file.
1126            let waiter = Waiter::new();
1127            let canceler = epoll_file_handle
1128                .wait_async(&current_task, &waiter, FdEvents::POLLIN, EventHandler::None)
1129                .expect("wait_async on epoll_file");
1130
1131            assert!(!epoll_file.waiters.is_empty());
1132
1133            // First wait on epoll_file while external waiter is registered.
1134            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1135            assert_eq!(events.len(), 1);
1136
1137            // Cancel the external waiter.
1138            canceler.cancel();
1139            assert!(epoll_file.waiters.is_empty());
1140
1141            // Second wait continues to function properly.
1142            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1143            assert_eq!(events.len(), 1);
1144        })
1145        .await;
1146    }
1147
1148    #[::fuchsia::test]
1149    async fn test_multithreaded_epoll_concurrent_recheck() {
1150        // Validates multithreaded behavior when multiple workers use the same epoll instance.
1151        spawn_kernel_and_run(async |current_task| {
1152            let kernel = current_task.kernel();
1153            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1154
1155            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1156            let epoll_file_handle = EpollFileObject::new_file(&current_task);
1157            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
1158            const EVENT_DATA: u64 = 42;
1159            epoll_file
1160                .add(
1161                    &current_task,
1162                    &pipe_out,
1163                    &epoll_file_handle,
1164                    EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
1165                )
1166                .unwrap();
1167
1168            // Write 2 bytes.
1169            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[1, 2])).unwrap();
1170
1171            // Worker 1 harvests the event.
1172            let events1 = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1173            assert_eq!(events1.len(), 1);
1174
1175            // Worker 2 calls wait() without any read occurring yet:
1176            // Worker 2's do_recheck observes data is still present and returns it.
1177            let events2 = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1178            assert_eq!(events2.len(), 1);
1179
1180            // Now worker 1 drains the pipe.
1181            let mut buf = VecOutputBuffer::new(10);
1182            assert_eq!(pipe_out.read(&current_task, &mut buf).unwrap(), 2);
1183
1184            // Worker 2 calls wait():
1185            // Pre-sleep do_recheck finds pipe empty, returns 0 events.
1186            let events3 = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1187            assert_eq!(events3.len(), 0);
1188
1189            // Remote peer writes new data: wait fires for Worker 1.
1190            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[3])).unwrap();
1191            let events4 = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1192            assert_eq!(events4.len(), 1);
1193        })
1194        .await;
1195    }
1196
1197    #[::fuchsia::test]
1198    async fn test_epoll_query_events_checks_recheck_list() {
1199        // When an outer epoll checks whether an inner epoll is ready, query_events()
1200        // inspects state.recheck_list directly if processing_list and trigger_list are empty.
1201        spawn_kernel_and_run(async |current_task| {
1202            let kernel = current_task.kernel();
1203            register_pipe_fs(kernel.expando.get::<FsRegistry>().as_ref());
1204
1205            let (pipe_out, pipe_in) = new_pipe(&current_task).unwrap();
1206            let epoll_file_handle = EpollFileObject::new_file(&current_task);
1207            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
1208            epoll_file
1209                .add(
1210                    &current_task,
1211                    &pipe_out,
1212                    &epoll_file_handle,
1213                    EpollEvent::new(FdEvents::POLLIN, 42),
1214                )
1215                .unwrap();
1216
1217            pipe_in.write(&current_task, &mut VecInputBuffer::new(&[1, 2])).unwrap();
1218
1219            // Harvesting the event leaves pipe_out on recheck_list.
1220            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1221            assert_eq!(events.len(), 1);
1222
1223            // query_events on the epoll file checks recheck_list and returns POLLIN.
1224            let queried = epoll_file_handle.query_events(&current_task).unwrap();
1225            assert!(queried.contains(FdEvents::POLLIN));
1226
1227            // Drain the pipe.
1228            let mut buf = VecOutputBuffer::new(10);
1229            assert_eq!(pipe_out.read(&current_task, &mut buf).unwrap(), 2);
1230
1231            // Now query_events sees the file on recheck_list is empty, so returns empty events.
1232            let queried = epoll_file_handle.query_events(&current_task).unwrap();
1233            assert!(!queried.contains(FdEvents::POLLIN));
1234        })
1235        .await;
1236    }
1237
1238    #[::fuchsia::test]
1239    async fn test_recheck_list_query_error_preserves_remaining_keys() {
1240        // In wait(), if do_recheck on one item fails with an error (e.g. query_events returns Err),
1241        // remaining unprocessed items must not be dropped from recheck_list.
1242        spawn_kernel_and_run(async |current_task| {
1243            let epoll_file_handle = EpollFileObject::new_file(&current_task);
1244            let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
1245
1246            let ops1 = ControlledEventsFile::new(FdEvents::POLLIN);
1247            let file1 = anon_test_file(&current_task, Box::new(ops1.clone()), OpenFlags::RDWR);
1248            let ops2 = ControlledEventsFile::new(FdEvents::POLLIN);
1249            let file2 = anon_test_file(&current_task, Box::new(ops2.clone()), OpenFlags::RDWR);
1250
1251            let _key1: ReadyItemKey = file1.id.as_epoll_key().into();
1252            let key2: ReadyItemKey = file2.id.as_epoll_key().into();
1253
1254            epoll_file
1255                .add(
1256                    &current_task,
1257                    &file1,
1258                    &epoll_file_handle,
1259                    EpollEvent::new(FdEvents::POLLIN, 1),
1260                )
1261                .unwrap();
1262            epoll_file
1263                .add(
1264                    &current_task,
1265                    &file2,
1266                    &epoll_file_handle,
1267                    EpollEvent::new(FdEvents::POLLIN, 2),
1268                )
1269                .unwrap();
1270
1271            // First wait: both files are ready and returned.
1272            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1273            assert_eq!(events.len(), 2);
1274
1275            // Both files are on recheck_list.
1276            assert_eq!(epoll_file.state.lock().recheck_list.len(), 2);
1277
1278            // Make file1 return an error on query_events.
1279            ops1.set_events(error!(EPERM));
1280
1281            // Second wait: do_recheck fails on file1 and returns Err.
1282            assert!(epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).is_err());
1283
1284            // Unprocessed items must be preserved on recheck_list.
1285            assert!(
1286                epoll_file.state.lock().recheck_list.contains(&key2),
1287                "file2 should remain on recheck_list despite file1's error"
1288            );
1289
1290            // Delete file1 so it does not fail subsequent calls.
1291            epoll_file.delete(&current_task, &file1).unwrap();
1292
1293            // Third wait: file2 is still ready and should be returned now.
1294            let events = epoll_file.wait(&current_task, 10, zx::MonotonicInstant::ZERO).unwrap();
1295            assert_eq!(events.len(), 1);
1296            assert_eq!(events[0].data(), 2);
1297        })
1298        .await;
1299    }
1300}