@@ -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
@@ -542,3 +544,65 @@ fn test_epoll_wait_maxevent_zero() {
542
544
assert_eq ! ( e. raw_os_error( ) , Some ( libc:: EINVAL ) ) ;
543
545
assert_eq ! ( res, -1 ) ;
544
546
}
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