@@ -20,6 +20,8 @@ fn main() {
20
20
test_pointer ( ) ;
21
21
test_two_same_fd_in_same_epoll_instance ( ) ;
22
22
test_epoll_wait_maxevent_zero ( ) ;
23
+ test_epoll_lost_events ( ) ;
24
+ test_ready_list_fetching_logic ( ) ;
23
25
}
24
26
25
27
// Using `as` cast since `EPOLLET` wraps around
@@ -548,3 +550,65 @@ fn test_epoll_wait_maxevent_zero() {
548
550
assert_eq ! ( e. raw_os_error( ) , Some ( libc:: EINVAL ) ) ;
549
551
assert_eq ! ( res, -1 ) ;
550
552
}
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