1use 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
27const MAX_NESTED_DEPTH: u32 = 5;
30
31struct 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 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
60pub type EpollKey = usize;
63
64#[derive(Default)]
67pub struct EpollFileObject {
68 waiter: Waiter,
69 state: LockDepMutex<EpollState, EpollStateLock>,
71 waitable_state: Arc<LockDepMutex<EpollWaitableState, EpollWaitableStateLock>>,
72 waiters: Arc<WaitQueue>,
75}
76
77#[derive(Default)]
78struct EpollState {
79 wait_objects: HashMap<ReadyItemKey, WaitObject>,
82 processing_list: VecDeque<ReadyItem>,
90 recheck_list: Vec<ReadyItemKey>,
96}
97
98#[derive(Default)]
99struct EpollWaitableState {
100 trigger_list: VecDeque<ReadyItem>,
103}
104
105impl EpollFileObject {
106 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 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 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 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 let _token = allow_subclass();
216 child_file.check_eloop(parent, depth_left - 1)?;
217 }
218
219 Ok(())
220 }
221
222 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 if let Some(epoll_to_add) = file.downcast_file::<EpollFileObject>() {
236 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 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 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 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 wait_object.deactivate_wakeup_source(current_task);
303 Ok(())
304 } else {
305 error!(ENOENT)
306 }
307 }
308
309 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 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 if let Some(target) = wait.target.upgrade() {
339 let events = {
340 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 self.wait_on_file(locked, current_task, pending.key, wait)?;
351 }
352 }
353 }
354 }
355 }
356 Ok(())
357 }
358
359 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; }
379
380 if !pending_list.is_empty() {
381 wait_deadline = zx::MonotonicInstant::ZERO;
385 }
386
387 match self.waiter.wait_until(locked, current_task, wait_deadline) {
401 Err(err) if err == ETIMEDOUT => break,
402 Err(err) if err == EINTR => {
403 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 result => result?,
417 }
418 }
419
420 Ok(pending_list)
421 }
422
423 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 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 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 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 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 if !wait.events.contains(FdEvents::EPOLLET) {
476 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 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, ¤t_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, ¤t_task);
611 let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
612 epoll_file
613 .add(
614 locked,
615 ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_task, &mut VecInputBuffer::new(test_bytes)).unwrap(),
667 test_bytes.len()
668 );
669
670 let epoll_file_handle = EpollFileObject::new_file(locked, ¤t_task);
671 let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
672 epoll_file
673 .add(
674 locked,
675 ¤t_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, ¤t_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, ¤t_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, ¤t_task, 0, EventFdType::Counter, true);
702 let waiter = Waiter::new();
703
704 let epoll_file_handle = EpollFileObject::new_file(locked, ¤t_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 ¤t_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(¤t_task, &event).unwrap();
719 }
720
721 let wait_canceler = event
722 .wait_async(
723 locked,
724 ¤t_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 ¤t_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, ¤t_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, ¤t_task, client1, OpenFlags::RDWR)
769 .expect("create_fuchsia_pipe");
770 let pipe2 = create_fuchsia_pipe(locked, ¤t_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, ¤t_task);
777 let epoll_file = epoll_object.downcast_file::<EpollFileObject>().unwrap();
778 epoll_file
779 .add(
780 locked,
781 ¤t_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 ¤t_task,
791 &pipe2,
792 &epoll_object,
793 EpollEvent::new(FdEvents::POLLIN, 2),
794 )
795 .expect("epoll_file.add");
796 epoll_file.wait(locked, ¤t_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, ¤t_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, ¤t_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, ¤t_task, 0, EventFdType::Counter, true);
837 let epoll_file_handle = EpollFileObject::new_file(locked, ¤t_task);
838 let epoll_file = epoll_file_handle.downcast_file::<EpollFileObject>().unwrap();
839
840 const EVENT_DATA: u64 = 42;
842 epoll_file
843 .add(
844 locked,
845 ¤t_task,
846 &event,
847 &epoll_file_handle,
848 EpollEvent::new(FdEvents::POLLIN, EVENT_DATA),
849 )
850 .unwrap();
851
852 let add_val = 1u64;
854 assert_eq!(
855 event
856 .write(locked, ¤t_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, ¤t_task, 10, zx::MonotonicInstant::ZERO)
864 .unwrap()
865 .len(),
866 1
867 );
868
869 epoll_file.delete(¤t_task, &event).unwrap();
871
872 assert_eq!(
874 epoll_file
875 .wait(locked, ¤t_task, 10, zx::MonotonicInstant::ZERO)
876 .unwrap()
877 .len(),
878 0
879 );
880 })
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 ¤t_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, ¤t_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 ¤t_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, ¤t_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 ¤t_task,
923 &socket1,
924 EpollEvent::new(read_write_event, EVENT_DATA),
925 )
926 .unwrap();
927 let triggered_events =
928 epoll_file.wait(locked, ¤t_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, ¤t_task, 0, EventFdType::Counter, true);
941 let epoll_file_handle = EpollFileObject::new_file(locked, ¤t_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 ¤t_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}