Skip to content

Commit 34ad8de

Browse files
authored
Merge pull request rust-lang#4033 from tiif/checkepoll
Add test for epoll
2 parents 3e71ed1 + 711a956 commit 34ad8de

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//@compile-flags: -Zmiri-preemption-rate=0
2+
//~^ERROR: deadlocked
3+
//~^^ERROR: deadlocked
4+
//@only-target: linux
5+
//@error-in-other-file: deadlock
6+
7+
use std::convert::TryInto;
8+
use std::thread;
9+
use std::thread::spawn;
10+
11+
// Using `as` cast since `EPOLLET` wraps around
12+
const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
13+
14+
#[track_caller]
15+
fn check_epoll_wait<const N: usize>(
16+
epfd: i32,
17+
expected_notifications: &[(u32, u64)],
18+
timeout: i32,
19+
) {
20+
let epoll_event = libc::epoll_event { events: 0, u64: 0 };
21+
let mut array: [libc::epoll_event; N] = [epoll_event; N];
22+
let maxsize = N;
23+
let array_ptr = array.as_mut_ptr();
24+
let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) };
25+
if res < 0 {
26+
panic!("epoll_wait failed: {}", std::io::Error::last_os_error());
27+
}
28+
assert_eq!(
29+
res,
30+
expected_notifications.len().try_into().unwrap(),
31+
"got wrong number of notifications"
32+
);
33+
let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) };
34+
for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) {
35+
let event = return_event.events;
36+
let data = return_event.u64;
37+
assert_eq!(event, expected_event.0, "got wrong events");
38+
assert_eq!(data, expected_event.1, "got wrong data");
39+
}
40+
}
41+
42+
// Test if only one thread is unblocked if multiple threads blocked on same epfd.
43+
// Expected execution:
44+
// 1. Thread 2 blocks.
45+
// 2. Thread 3 blocks.
46+
// 3. Thread 1 unblocks thread 3.
47+
// 4. Thread 2 deadlocks.
48+
fn main() {
49+
// Create an epoll instance.
50+
let epfd = unsafe { libc::epoll_create1(0) };
51+
assert_ne!(epfd, -1);
52+
53+
// Create a socketpair instance.
54+
let mut fds = [-1, -1];
55+
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
56+
assert_eq!(res, 0);
57+
58+
// Register one side of the socketpair with epoll.
59+
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
60+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
61+
assert_eq!(res, 0);
62+
63+
// epoll_wait to clear notification.
64+
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
65+
let expected_value = fds[0] as u64;
66+
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
67+
68+
let thread1 = spawn(move || {
69+
thread::park();
70+
let data = "abcde".as_bytes().as_ptr();
71+
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
72+
assert_eq!(res, 5);
73+
});
74+
75+
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
76+
let expected_value = fds[0] as u64;
77+
let thread2 = spawn(move || {
78+
thread::park();
79+
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
80+
//~^ERROR: deadlocked
81+
});
82+
let thread3 = spawn(move || {
83+
thread::park();
84+
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
85+
});
86+
87+
thread2.thread().unpark();
88+
thread::yield_now();
89+
thread3.thread().unpark();
90+
thread::yield_now();
91+
thread1.thread().unpark();
92+
93+
thread1.join().unwrap();
94+
thread2.join().unwrap();
95+
thread3.join().unwrap();
96+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: deadlock: the evaluated program deadlocked
2+
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
3+
|
4+
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
5+
| ^ the evaluated program deadlocked
6+
|
7+
= note: BACKTRACE:
8+
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
9+
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
10+
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
11+
note: inside `main`
12+
--> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
13+
|
14+
LL | thread2.join().unwrap();
15+
| ^^^^^^^^^^^^^^
16+
17+
error: deadlock: the evaluated program deadlocked
18+
|
19+
= note: the evaluated program deadlocked
20+
= note: (no span available)
21+
= note: BACKTRACE on thread `unnamed-ID`:
22+
23+
error: deadlock: the evaluated program deadlocked
24+
--> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
25+
|
26+
LL | check_epoll_wait::<TAG>(epfd, &[(expected_event, expected_value)], -1);
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
28+
|
29+
= note: BACKTRACE on thread `unnamed-ID`:
30+
= note: inside closure at tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
31+
32+
error: deadlock: the evaluated program deadlocked
33+
|
34+
= note: the evaluated program deadlocked
35+
= note: (no span available)
36+
= note: BACKTRACE on thread `unnamed-ID`:
37+
38+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
39+
40+
error: aborting due to 4 previous errors
41+

0 commit comments

Comments
 (0)