Skip to content

Commit 5420681

Browse files
committed
epoll: ensure we count the events that each thread will take
1 parent c82b572 commit 5420681

File tree

4 files changed

+36
-25
lines changed

4 files changed

+36
-25
lines changed

src/tools/miri/src/shims/unix/linux_like/epoll.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
479479
|this, unblock: UnblockKind| {
480480
match unblock {
481481
UnblockKind::Ready => {
482-
return_ready_list(&epfd, &dest, &event, this)?;
482+
let events = return_ready_list(&epfd, &dest, &event, this)?;
483+
assert!(events > 0, "we got woken up with no events to deliver");
483484
interp_ok(())
484485
},
485486
UnblockKind::TimedOut => {
@@ -583,6 +584,13 @@ fn update_readiness<'tcx>(
583584
num_ready = num_ready.saturating_sub(events);
584585
}
585586
}
587+
// Sanity-check: if there are threads left to wake up, then there are no more ready events.
588+
if !epoll.queue.borrow().is_empty() {
589+
assert!(
590+
epoll.interest_list.borrow().values().all(|i| !i.ready),
591+
"there are unconsumed ready events and threads ready to take them"
592+
);
593+
}
586594

587595
interp_ok(())
588596
}
@@ -594,7 +602,7 @@ fn return_ready_list<'tcx>(
594602
dest: &MPlaceTy<'tcx>,
595603
events: &MPlaceTy<'tcx>,
596604
ecx: &mut MiriInterpCx<'tcx>,
597-
) -> InterpResult<'tcx> {
605+
) -> InterpResult<'tcx, i32> {
598606
let mut interest_list = epfd.interest_list.borrow_mut();
599607
let mut num_of_events: i32 = 0;
600608
let mut array_iter = ecx.project_array_fields(events)?;
@@ -634,5 +642,5 @@ fn return_ready_list<'tcx>(
634642
}
635643
}
636644
ecx.write_int(num_of_events, dest)?;
637-
interp_ok(())
645+
interp_ok(num_of_events)
638646
}

src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
use std::convert::TryInto;
66
use std::thread;
77

8+
#[path = "../../utils/libc.rs"]
9+
mod libc_utils;
10+
811
// Using `as` cast since `EPOLLET` wraps around
912
const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
1013

@@ -47,37 +50,36 @@ fn main() {
4750
let epfd = unsafe { libc::epoll_create1(0) };
4851
assert_ne!(epfd, -1);
4952

50-
// Create a socketpair instance.
51-
let mut fds = [-1, -1];
52-
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
53-
assert_eq!(res, 0);
53+
// Create an eventfd instance.
54+
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
55+
let fd1 = unsafe { libc::eventfd(0, flags) };
56+
// Make a duplicate so that we have two file descriptors for the same file description.
57+
let fd2 = unsafe { libc::dup(fd1) };
5458

55-
// Register one side of the socketpair with epoll.
56-
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
57-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
59+
// Register both with epoll.
60+
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd1 as u64 };
61+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd1, &mut ev) };
62+
assert_eq!(res, 0);
63+
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd2 as u64 };
64+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd2, &mut ev) };
5865
assert_eq!(res, 0);
5966

60-
// epoll_wait to clear notification.
61-
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
62-
let expected_value = fds[0] as u64;
63-
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
67+
// Consume the initial events.
68+
let expected = [(libc::EPOLLOUT as u32, fd1 as u64), (libc::EPOLLOUT as u32, fd2 as u64)];
69+
check_epoll_wait::<8>(epfd, &expected, -1);
6470

65-
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
66-
let expected_value = fds[0] as u64;
6771
let thread1 = thread::spawn(move || {
68-
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
72+
check_epoll_wait::<2>(epfd, &expected, -1);
6973
});
7074
let thread2 = thread::spawn(move || {
71-
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
75+
check_epoll_wait::<2>(epfd, &expected, -1);
7276
//~^ERROR: deadlocked
7377
});
7478
// Yield so the threads are both blocked.
7579
thread::yield_now();
7680

77-
// Just a single write, so we only wake up one of them.
78-
let data = "abcde".as_bytes().as_ptr();
79-
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
80-
assert!(res > 0 && res <= 5);
81+
// Create two events at once.
82+
libc_utils::write_all_from_slice(fd1, &0_u64.to_ne_bytes()).unwrap();
8183

8284
thread1.join().unwrap();
8385
thread2.join().unwrap();

src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ LL | thread2.join().unwrap();
1717
error: the evaluated program deadlocked
1818
--> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
1919
|
20-
LL | check_epoll_wait::<TAG>(epfd, &[(expected_event, expected_value)], -1);
21-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here
20+
LL | check_epoll_wait::<TAG>(epfd, &expected, -1);
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here
2222

2323
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2424

src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ fn multiple_events_wake_multiple_threads() {
231231
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd2 as u64 };
232232
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd2, &mut ev) };
233233
assert_eq!(res, 0);
234-
// Consume the initial event.
234+
235+
// Consume the initial events.
235236
let expected = [(libc::EPOLLOUT as u32, fd1 as u64), (libc::EPOLLOUT as u32, fd2 as u64)];
236237
check_epoll_wait::<8>(epfd, &expected, -1);
237238

0 commit comments

Comments
 (0)