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