mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Add EPOLLER support
This commit is contained in:
parent
93700208a8
commit
483120d6ce
@ -76,11 +76,19 @@ pub struct EpollReadyEvents {
|
||||
/// epollrdhup also gets set when only the write half is closed, which is possible
|
||||
/// via `shutdown(_, SHUT_WR)`.
|
||||
pub epollhup: bool,
|
||||
/// Error condition happened on the associated file descriptor.
|
||||
pub epollerr: bool,
|
||||
}
|
||||
|
||||
impl EpollReadyEvents {
|
||||
pub fn new() -> Self {
|
||||
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false, epollhup: false }
|
||||
EpollReadyEvents {
|
||||
epollin: false,
|
||||
epollout: false,
|
||||
epollrdhup: false,
|
||||
epollhup: false,
|
||||
epollerr: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
|
||||
@ -88,6 +96,7 @@ impl EpollReadyEvents {
|
||||
let epollout = ecx.eval_libc_u32("EPOLLOUT");
|
||||
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
|
||||
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
|
||||
let epollerr = ecx.eval_libc_u32("EPOLLERR");
|
||||
|
||||
let mut bitmask = 0;
|
||||
if self.epollin {
|
||||
@ -102,6 +111,9 @@ impl EpollReadyEvents {
|
||||
if self.epollhup {
|
||||
bitmask |= epollhup;
|
||||
}
|
||||
if self.epollerr {
|
||||
bitmask |= epollerr;
|
||||
}
|
||||
bitmask
|
||||
}
|
||||
}
|
||||
@ -229,6 +241,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
|
||||
let epollet = this.eval_libc_u32("EPOLLET");
|
||||
let epollhup = this.eval_libc_u32("EPOLLHUP");
|
||||
let epollerr = this.eval_libc_u32("EPOLLERR");
|
||||
|
||||
// Fail on unsupported operations.
|
||||
if op & epoll_ctl_add != epoll_ctl_add
|
||||
@ -261,10 +274,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// Unset the flag we support to discover if any unsupported flags are used.
|
||||
let mut flags = events;
|
||||
// epoll_wait(2) will always wait for epollhup; it is not
|
||||
// epoll_wait(2) will always wait for epollhup and epollerr; it is not
|
||||
// necessary to set it in events when calling epoll_ctl().
|
||||
// So we will always set this event type.
|
||||
// So we will always set these two event types.
|
||||
events |= epollhup;
|
||||
events |= epollerr;
|
||||
|
||||
if events & epollet != epollet {
|
||||
// We only support edge-triggered notification for now.
|
||||
@ -284,6 +298,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if flags & epollhup == epollhup {
|
||||
flags &= !epollhup;
|
||||
}
|
||||
if flags & epollerr == epollerr {
|
||||
flags &= !epollerr;
|
||||
}
|
||||
if flags != 0 {
|
||||
throw_unsup_format!(
|
||||
"epoll_ctl: encountered unknown unsupported flags {:#x}",
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! are entirely implemented inside Miri.
|
||||
//! We also use the same infrastructure to implement unnamed pipes.
|
||||
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::io::{Error, ErrorKind, Read};
|
||||
@ -27,6 +27,10 @@ struct AnonSocket {
|
||||
/// writing to. This is a weak reference because the other side may be closed before us; all
|
||||
/// future writes will then trigger EPIPE.
|
||||
peer_fd: OnceCell<WeakFileDescriptionRef>,
|
||||
/// Indicates whether the peer has lost data when the file description is closed.
|
||||
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
|
||||
/// of closure.
|
||||
peer_lost_data: Cell<bool>,
|
||||
is_nonblock: bool,
|
||||
}
|
||||
|
||||
@ -91,6 +95,10 @@ impl FileDescription for AnonSocket {
|
||||
// for read and write.
|
||||
epoll_ready_events.epollin = true;
|
||||
epoll_ready_events.epollout = true;
|
||||
// If there is data lost in peer_fd, set EPOLLERR.
|
||||
if self.peer_lost_data.get() {
|
||||
epoll_ready_events.epollerr = true;
|
||||
}
|
||||
}
|
||||
Ok(epoll_ready_events)
|
||||
}
|
||||
@ -101,6 +109,13 @@ impl FileDescription for AnonSocket {
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
if let Some(peer_fd) = self.peer_fd().upgrade() {
|
||||
// If the current readbuf is non-empty when the file description is closed,
|
||||
// notify the peer that data lost has happened in current file description.
|
||||
if let Some(readbuf) = &self.readbuf {
|
||||
if !readbuf.borrow().buf.is_empty() {
|
||||
peer_fd.downcast::<AnonSocket>().unwrap().peer_lost_data.set(true);
|
||||
}
|
||||
}
|
||||
// Notify peer fd that close has happened, since that can unblock reads and writes.
|
||||
ecx.check_and_update_readiness(&peer_fd)?;
|
||||
}
|
||||
@ -290,11 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let fd0 = fds.new_ref(AnonSocket {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
is_nonblock: is_sock_nonblock,
|
||||
});
|
||||
let fd1 = fds.new_ref(AnonSocket {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
is_nonblock: is_sock_nonblock,
|
||||
});
|
||||
|
||||
@ -340,10 +357,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let fd0 = fds.new_ref(AnonSocket {
|
||||
readbuf: Some(RefCell::new(Buffer::new())),
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
is_nonblock: false,
|
||||
});
|
||||
let fd1 = fds.new_ref(AnonSocket {
|
||||
readbuf: None,
|
||||
peer_fd: OnceCell::new(),
|
||||
peer_lost_data: Cell::new(false),
|
||||
is_nonblock: false,
|
||||
});
|
||||
let fd1 =
|
||||
fds.new_ref(AnonSocket { readbuf: None, peer_fd: OnceCell::new(), is_nonblock: false });
|
||||
|
||||
// Make the file descriptions point to each other.
|
||||
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
|
||||
|
@ -20,6 +20,7 @@ fn main() {
|
||||
test_pointer();
|
||||
test_two_same_fd_in_same_epoll_instance();
|
||||
test_epoll_wait_maxevent_zero();
|
||||
test_socketpair_epollerr();
|
||||
test_epoll_lost_events();
|
||||
test_ready_list_fetching_logic();
|
||||
}
|
||||
@ -551,6 +552,43 @@ fn test_epoll_wait_maxevent_zero() {
|
||||
assert_eq!(res, -1);
|
||||
}
|
||||
|
||||
fn test_socketpair_epollerr() {
|
||||
// Create an epoll instance.
|
||||
let epfd = unsafe { libc::epoll_create1(0) };
|
||||
assert_ne!(epfd, -1);
|
||||
|
||||
// Create a socketpair instance.
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
|
||||
// Write to fd[0]
|
||||
let data = "abcde".as_bytes().as_ptr();
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
|
||||
// Close fds[1].
|
||||
// EPOLLERR will be triggered if we close peer fd that still has data in its read buffer.
|
||||
let res = unsafe { libc::close(fds[1]) };
|
||||
assert_eq!(res, 0);
|
||||
|
||||
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
|
||||
let mut ev = libc::epoll_event {
|
||||
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
|
||||
u64: u64::try_from(fds[1]).unwrap(),
|
||||
};
|
||||
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
|
||||
assert_ne!(res, -1);
|
||||
|
||||
// Check result from epoll_wait.
|
||||
let expected_event = u32::try_from(
|
||||
libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLRDHUP | libc::EPOLLERR,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_value = u64::try_from(fds[1]).unwrap();
|
||||
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
|
||||
}
|
||||
|
||||
// This is a test for https://github.com/rust-lang/miri/issues/3812,
|
||||
// epoll can lose events if they don't fit in the output buffer.
|
||||
fn test_epoll_lost_events() {
|
||||
|
Loading…
Reference in New Issue
Block a user