Skip to content

Commit b364407

Browse files
committed
Fix event fetching logic and add test
1 parent 4f0c281 commit b364407

File tree

2 files changed

+54
-16
lines changed

2 files changed

+54
-16
lines changed

src/shims/unix/linux/epoll.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -411,24 +411,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
411411
let mut num_of_events: i32 = 0;
412412
let mut array_iter = this.project_array_fields(&event)?;
413413

414-
while let Some(des) = array_iter.next(this)? {
414+
while let Some(des) = array_iter.next(this)?
415+
&& !ready_list.is_empty()
416+
{
417+
let mut entry_written = false;
415418
// Fetch an event from the ready list.
416-
if let Some((epoll_key, epoll_return)) = ready_list.pop_first() {
417-
// If the file description is fully close, the entry for corresponding FdID in the
418-
// global epoll event interest table would be empty.
419-
if this.machine.epoll_interests.get_epoll_interest(epoll_key.0).is_some() {
420-
// Return notification to the caller if the file description is not fully closed.
421-
this.write_int_fields_named(
422-
&[
423-
("events", epoll_return.events.into()),
424-
("u64", epoll_return.data.into()),
425-
],
426-
&des.1,
427-
)?;
428-
num_of_events = num_of_events.checked_add(1).unwrap();
419+
while !entry_written {
420+
if let Some((epoll_key, epoll_return)) = ready_list.pop_first() {
421+
// If the file description is fully close, the entry for corresponding FdID in the
422+
// global epoll event interest table would be empty.
423+
if this.machine.epoll_interests.get_epoll_interest(epoll_key.0).is_some() {
424+
// Return notification to the caller if the file description is not fully closed.
425+
this.write_int_fields_named(
426+
&[
427+
("events", epoll_return.events.into()),
428+
("u64", epoll_return.data.into()),
429+
],
430+
&des.1,
431+
)?;
432+
num_of_events = num_of_events.checked_add(1).unwrap();
433+
entry_written = true;
434+
}
435+
} else {
436+
break;
429437
}
430-
} else {
431-
break;
432438
}
433439
}
434440
Ok(Scalar::from_i32(num_of_events))

tests/pass-dep/libc/libc-epoll.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)