Skip to content

Commit ec476bf

Browse files
committed
eventfd: force spurious wakeups, matching Linux
1 parent d751877 commit ec476bf

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

src/shims/unix/linux_like/eventfd.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,10 @@ fn eventfd_write<'tcx>(
217217

218218
// The state changed; we check and update the status of all supported event
219219
// types for current file description.
220-
ecx.epoll_send_fd_ready_events(eventfd, /* force_edge */ false)?;
220+
// Linux seems to cause spurious wakeups here, and Tokio seems to rely on that
221+
// (see <https://github.com/rust-lang/miri/pull/4676#discussion_r2510528994>
222+
// and also <https://www.illumos.org/issues/16700>).
223+
ecx.epoll_send_fd_ready_events(eventfd, /* force_edge */ true)?;
221224

222225
// Return how many bytes we consumed from the user-provided buffer.
223226
return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize()));
@@ -312,7 +315,8 @@ fn eventfd_read<'tcx>(
312315

313316
// The state changed; we check and update the status of all supported event
314317
// types for current file description.
315-
ecx.epoll_send_fd_ready_events(eventfd, /* force_edge */ false)?;
318+
// Linux seems to always emit do notifications here, even if we were already writable.
319+
ecx.epoll_send_fd_ready_events(eventfd, /* force_edge */ true)?;
316320

317321
// Tell userspace how many bytes we put into the buffer.
318322
return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize()));

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,34 @@ fn test_epoll_eventfd() {
258258
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
259259
let expected_value = u64::try_from(fd).unwrap();
260260
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
261+
262+
// Write to the eventfd again.
263+
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
264+
assert_eq!(res, 8);
265+
266+
// This does not change the status, so we should get no event.
267+
// However, Linux performs a spurious wakeup.
268+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
269+
270+
// Read from the eventfd.
271+
let mut buf = [0u8; 8];
272+
let res = unsafe { libc_utils::read_all(fd, buf.as_mut_ptr().cast(), 8) };
273+
assert_eq!(res, 8);
274+
275+
// This consumes the event, so the read status is gone. However, deactivation
276+
// does not trigger an event.
277+
// Still, we see a spurious wakeup.
278+
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
279+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
280+
281+
// Write the maximum possible value.
282+
let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
283+
let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
284+
assert_eq!(res, 8);
285+
286+
// This reactivates reads, therefore triggering an event. Writing is no longer possible.
287+
let expected_event = u32::try_from(libc::EPOLLIN).unwrap();
288+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
261289
}
262290

263291
// When read/write happened on one side of the socketpair, only the other side will be notified.

0 commit comments

Comments
 (0)