-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This test demonstrates the need to synchronize the clock of the thread waking up from an epoll_wait from the thread that issued the epoll awake event.
- Loading branch information
Showing
2 changed files
with
108 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
//@only-target: linux | ||
// test_epoll_race depends on a deterministic schedule. | ||
//@compile-flags: -Zmiri-preemption-rate=0 | ||
|
||
use std::convert::TryInto; | ||
use std::thread; | ||
|
||
fn main() { | ||
test_epoll_race(); | ||
} | ||
|
||
// Using `as` cast since `EPOLLET` wraps around | ||
const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _; | ||
|
||
#[track_caller] | ||
fn check_epoll_wait<const N: usize>( | ||
epfd: i32, | ||
expected_notifications: &[(u32, u64)], | ||
timeout: i32, | ||
) { | ||
let epoll_event = libc::epoll_event { events: 0, u64: 0 }; | ||
let mut array: [libc::epoll_event; N] = [epoll_event; N]; | ||
let maxsize = N; | ||
let array_ptr = array.as_mut_ptr(); | ||
let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) }; | ||
if res < 0 { | ||
panic!("epoll_wait failed: {}", std::io::Error::last_os_error()); | ||
} | ||
assert_eq!( | ||
res, | ||
expected_notifications.len().try_into().unwrap(), | ||
"got wrong number of notifications" | ||
); | ||
let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) }; | ||
for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) { | ||
let event = return_event.events; | ||
let data = return_event.u64; | ||
assert_eq!(event, expected_event.0, "got wrong events"); | ||
assert_eq!(data, expected_event.1, "got wrong data"); | ||
} | ||
} | ||
|
||
// This test shows a data_race before epoll had vector clocks added. | ||
fn test_epoll_race() { | ||
// Create an epoll instance. | ||
let epfd = unsafe { libc::epoll_create1(0) }; | ||
assert_ne!(epfd, -1); | ||
|
||
// Create an eventfd instance. | ||
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; | ||
let fd = unsafe { libc::eventfd(0, flags) }; | ||
|
||
// Register eventfd with the epoll instance. | ||
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; | ||
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; | ||
assert_eq!(res, 0); | ||
|
||
static mut VAL: u8 = 0; | ||
let thread1 = thread::spawn(move || { | ||
// Write to the static mut variable. | ||
unsafe { VAL = 1 }; | ||
// Write to the eventfd instance. | ||
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); | ||
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; | ||
// read returns number of bytes that have been read, which is always 8. | ||
assert_eq!(res, 8); | ||
}); | ||
thread::yield_now(); | ||
// epoll_wait for the event to happen. | ||
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); | ||
let expected_value = u64::try_from(fd).unwrap(); | ||
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); | ||
// Read from the static mut variable. | ||
#[allow(static_mut_refs)] | ||
unsafe { | ||
assert_eq!(VAL, 1) //~ ERROR: Data race detected | ||
}; | ||
thread1.join().unwrap(); | ||
} |
29 changes: 29 additions & 0 deletions
29
src/tools/miri/tests/fail-dep/libc/libc-epoll-blocking.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here | ||
--> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC | ||
| | ||
LL | assert_eq!(VAL, 1) | ||
| ^^^^^^^^^^^^^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) retag read of type `u8` on thread `main` at ALLOC. (2) just happened here | ||
| | ||
help: and (1) occurred earlier here | ||
--> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC | ||
| | ||
LL | unsafe { VAL = 1 }; | ||
| ^^^^^^^ | ||
= help: retags occur on all (re)borrows and as well as when references are copied or moved | ||
= help: retags permit optimizations that insert speculative reads or writes | ||
= help: therefore from the perspective of data races, a retag has the same implications as a read or write | ||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
= note: BACKTRACE (of the first span): | ||
= note: inside `test_epoll_race` at RUSTLIB/core/src/macros/mod.rs:LL:CC | ||
note: inside `main` | ||
--> tests/fail-dep/libc/libc-epoll-blocking.rs:LL:CC | ||
| | ||
LL | test_epoll_race(); | ||
| ^^^^^^^^^^^^^^^^^ | ||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
|
||
error: aborting due to 1 previous error | ||
|