@@ -20,6 +20,8 @@ fn main() {
2020 test_pointer ( ) ;
2121 test_two_same_fd_in_same_epoll_instance ( ) ;
2222 test_epoll_wait_maxevent_zero ( ) ;
23+ test_epoll_lost_events ( ) ;
24+ test_ready_list_fetching_logic ( ) ;
2325}
2426
2527// Using `as` cast since `EPOLLET` wraps around
@@ -548,3 +550,65 @@ fn test_epoll_wait_maxevent_zero() {
548550 assert_eq ! ( e. raw_os_error( ) , Some ( libc:: EINVAL ) ) ;
549551 assert_eq ! ( res, -1 ) ;
550552}
553+
554+ // This is a test for https://github.com/rust-lang/miri/issues/3812,
555+ // epoll can lose events if they don't fit in the output buffer.
556+ fn test_epoll_lost_events ( ) {
557+ // Create an epoll instance.
558+ let epfd = unsafe { libc:: epoll_create1 ( 0 ) } ;
559+ assert_ne ! ( epfd, -1 ) ;
560+
561+ // Create a socketpair instance.
562+ let mut fds = [ -1 , -1 ] ;
563+ let res = unsafe { libc:: socketpair ( libc:: AF_UNIX , libc:: SOCK_STREAM , 0 , fds. as_mut_ptr ( ) ) } ;
564+ assert_eq ! ( res, 0 ) ;
565+
566+ // Register both fd to the same epoll instance.
567+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fds[ 0 ] as u64 } ;
568+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fds[ 0 ] , & mut ev) } ;
569+ assert_eq ! ( res, 0 ) ;
570+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fds[ 1 ] as u64 } ;
571+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fds[ 1 ] , & mut ev) } ;
572+ assert_eq ! ( res, 0 ) ;
573+
574+ //Two notification should be received. But we only provide buffer for one event.
575+ let expected_event0 = u32:: try_from ( libc:: EPOLLOUT ) . unwrap ( ) ;
576+ let expected_value0 = fds[ 0 ] as u64 ;
577+ check_epoll_wait :: < 1 > ( epfd, & [ ( expected_event0, expected_value0) ] ) ;
578+
579+ // Previous event should be returned for the second epoll_wait.
580+ let expected_event1 = u32:: try_from ( libc:: EPOLLOUT ) . unwrap ( ) ;
581+ let expected_value1 = fds[ 1 ] as u64 ;
582+ check_epoll_wait :: < 1 > ( epfd, & [ ( expected_event1, expected_value1) ] ) ;
583+ }
584+
585+ // This is testing if closing an fd that is already in ready list will cause an empty entry in
586+ // returned notification.
587+ // Related discussion in https://github.com/rust-lang/miri/pull/3818#discussion_r1720679440.
588+ fn test_ready_list_fetching_logic ( ) {
589+ // Create an epoll instance.
590+ let epfd = unsafe { libc:: epoll_create1 ( 0 ) } ;
591+ assert_ne ! ( epfd, -1 ) ;
592+
593+ // Create two eventfd instances.
594+ let flags = libc:: EFD_NONBLOCK | libc:: EFD_CLOEXEC ;
595+ let fd0 = unsafe { libc:: eventfd ( 0 , flags) } ;
596+ let fd1 = unsafe { libc:: eventfd ( 0 , flags) } ;
597+
598+ // Register both fd to the same epoll instance. At this point, both of them are on the ready list.
599+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fd0 as u64 } ;
600+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fd0, & mut ev) } ;
601+ assert_eq ! ( res, 0 ) ;
602+ let mut ev = libc:: epoll_event { events : EPOLL_IN_OUT_ET , u64 : fd1 as u64 } ;
603+ let res = unsafe { libc:: epoll_ctl ( epfd, libc:: EPOLL_CTL_ADD , fd1, & mut ev) } ;
604+ assert_eq ! ( res, 0 ) ;
605+
606+ // Close fd0 so the first entry in the ready list will be empty.
607+ let res = unsafe { libc:: close ( fd0) } ;
608+ assert_eq ! ( res, 0 ) ;
609+
610+ // Notification for fd1 should be returned.
611+ let expected_event1 = u32:: try_from ( libc:: EPOLLOUT ) . unwrap ( ) ;
612+ let expected_value1 = fd1 as u64 ;
613+ check_epoll_wait :: < 1 > ( epfd, & [ ( expected_event1, expected_value1) ] ) ;
614+ }
0 commit comments