@@ -19,6 +19,7 @@ fn main() {
1919 test_pointer ( ) ;
2020 test_two_same_fd_in_same_epoll_instance ( ) ;
2121 test_epoll_lost_events ( ) ;
22+ test_ready_list_fetching_logic ( ) ;
2223}
2324
2425// Using `as` cast since `EPOLLET` wraps around
@@ -535,3 +536,34 @@ fn test_epoll_lost_events() {
535536 let expected_value1 = fds[ 1 ] as u64 ;
536537 assert ! ( check_epoll_wait:: <1 >( epfd, & [ ( expected_event1, expected_value1) ] ) ) ;
537538}
539+
540+ // This is testing if closing an fd that is already in ready_list will cause an empty entry in
541+ // returned notification.
542+ // Related discussion in https://github.com/rust-lang/miri/pull/3818#discussion_r1720679440.
543+ fn test_ready_list_fetching_logic ( ) {
544+ // Create an epoll instance.
545+ let epfd = unsafe { libc:: epoll_create1 ( 0 ) } ;
546+ assert_ne ! ( epfd, -1 ) ;
547+
548+ // Create two eventfd instances.
549+ let flags = libc:: EFD_NONBLOCK | libc:: EFD_CLOEXEC ;
550+ let fd0 = unsafe { libc:: eventfd ( 0 , flags) } ;
551+ let fd1 = unsafe { libc:: eventfd ( 0 , flags) } ;
552+
553+ // Register both fd to the same epoll instance. At this point, both of them is in ready list.
554+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fd0 as u64 } ;
555+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fd0, & mut ev) } ;
556+ assert_ne ! ( res, -1 ) ;
557+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fd1 as u64 } ;
558+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fd1, & mut ev) } ;
559+ assert_ne ! ( res, -1 ) ;
560+
561+ // Close fd0. So the first entry in the ready list will be empty.
562+ let res = unsafe { libc:: close ( fd0) } ;
563+ assert_eq ! ( res, 0 ) ;
564+
565+ // Notification for fd1 should be returned.
566+ let expected_event1 = u32:: try_from ( libc:: EPOLLOUT ) . unwrap ( ) ;
567+ let expected_value1 = fd1 as u64 ;
568+ assert ! ( check_epoll_wait:: <1 >( epfd, & [ ( expected_event1, expected_value1) ] ) ) ;
569+ }
0 commit comments