mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +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
|
/// epollrdhup also gets set when only the write half is closed, which is possible
|
||||||
/// via `shutdown(_, SHUT_WR)`.
|
/// via `shutdown(_, SHUT_WR)`.
|
||||||
pub epollhup: bool,
|
pub epollhup: bool,
|
||||||
|
/// Error condition happened on the associated file descriptor.
|
||||||
|
pub epollerr: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpollReadyEvents {
|
impl EpollReadyEvents {
|
||||||
pub fn new() -> Self {
|
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 {
|
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 epollout = ecx.eval_libc_u32("EPOLLOUT");
|
||||||
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
|
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
|
||||||
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
|
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
|
||||||
|
let epollerr = ecx.eval_libc_u32("EPOLLERR");
|
||||||
|
|
||||||
let mut bitmask = 0;
|
let mut bitmask = 0;
|
||||||
if self.epollin {
|
if self.epollin {
|
||||||
@ -102,6 +111,9 @@ impl EpollReadyEvents {
|
|||||||
if self.epollhup {
|
if self.epollhup {
|
||||||
bitmask |= epollhup;
|
bitmask |= epollhup;
|
||||||
}
|
}
|
||||||
|
if self.epollerr {
|
||||||
|
bitmask |= epollerr;
|
||||||
|
}
|
||||||
bitmask
|
bitmask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,6 +241,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
|
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
|
||||||
let epollet = this.eval_libc_u32("EPOLLET");
|
let epollet = this.eval_libc_u32("EPOLLET");
|
||||||
let epollhup = this.eval_libc_u32("EPOLLHUP");
|
let epollhup = this.eval_libc_u32("EPOLLHUP");
|
||||||
|
let epollerr = this.eval_libc_u32("EPOLLERR");
|
||||||
|
|
||||||
// Fail on unsupported operations.
|
// Fail on unsupported operations.
|
||||||
if op & epoll_ctl_add != epoll_ctl_add
|
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.
|
// Unset the flag we support to discover if any unsupported flags are used.
|
||||||
let mut flags = events;
|
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().
|
// 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 |= epollhup;
|
||||||
|
events |= epollerr;
|
||||||
|
|
||||||
if events & epollet != epollet {
|
if events & epollet != epollet {
|
||||||
// We only support edge-triggered notification for now.
|
// We only support edge-triggered notification for now.
|
||||||
@ -284,6 +298,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
if flags & epollhup == epollhup {
|
if flags & epollhup == epollhup {
|
||||||
flags &= !epollhup;
|
flags &= !epollhup;
|
||||||
}
|
}
|
||||||
|
if flags & epollerr == epollerr {
|
||||||
|
flags &= !epollerr;
|
||||||
|
}
|
||||||
if flags != 0 {
|
if flags != 0 {
|
||||||
throw_unsup_format!(
|
throw_unsup_format!(
|
||||||
"epoll_ctl: encountered unknown unsupported flags {:#x}",
|
"epoll_ctl: encountered unknown unsupported flags {:#x}",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//! are entirely implemented inside Miri.
|
//! are entirely implemented inside Miri.
|
||||||
//! We also use the same infrastructure to implement unnamed pipes.
|
//! 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::collections::VecDeque;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Error, ErrorKind, Read};
|
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
|
/// writing to. This is a weak reference because the other side may be closed before us; all
|
||||||
/// future writes will then trigger EPIPE.
|
/// future writes will then trigger EPIPE.
|
||||||
peer_fd: OnceCell<WeakFileDescriptionRef>,
|
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,
|
is_nonblock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +95,10 @@ impl FileDescription for AnonSocket {
|
|||||||
// for read and write.
|
// for read and write.
|
||||||
epoll_ready_events.epollin = true;
|
epoll_ready_events.epollin = true;
|
||||||
epoll_ready_events.epollout = 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)
|
Ok(epoll_ready_events)
|
||||||
}
|
}
|
||||||
@ -101,6 +109,13 @@ impl FileDescription for AnonSocket {
|
|||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<()>> {
|
) -> InterpResult<'tcx, io::Result<()>> {
|
||||||
if let Some(peer_fd) = self.peer_fd().upgrade() {
|
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.
|
// Notify peer fd that close has happened, since that can unblock reads and writes.
|
||||||
ecx.check_and_update_readiness(&peer_fd)?;
|
ecx.check_and_update_readiness(&peer_fd)?;
|
||||||
}
|
}
|
||||||
@ -290,11 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let fd0 = fds.new_ref(AnonSocket {
|
let fd0 = fds.new_ref(AnonSocket {
|
||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
|
peer_lost_data: Cell::new(false),
|
||||||
is_nonblock: is_sock_nonblock,
|
is_nonblock: is_sock_nonblock,
|
||||||
});
|
});
|
||||||
let fd1 = fds.new_ref(AnonSocket {
|
let fd1 = fds.new_ref(AnonSocket {
|
||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
|
peer_lost_data: Cell::new(false),
|
||||||
is_nonblock: is_sock_nonblock,
|
is_nonblock: is_sock_nonblock,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -340,10 +357,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let fd0 = fds.new_ref(AnonSocket {
|
let fd0 = fds.new_ref(AnonSocket {
|
||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::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,
|
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.
|
// Make the file descriptions point to each other.
|
||||||
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
|
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
|
||||||
|
@ -20,6 +20,7 @@ fn main() {
|
|||||||
test_pointer();
|
test_pointer();
|
||||||
test_two_same_fd_in_same_epoll_instance();
|
test_two_same_fd_in_same_epoll_instance();
|
||||||
test_epoll_wait_maxevent_zero();
|
test_epoll_wait_maxevent_zero();
|
||||||
|
test_socketpair_epollerr();
|
||||||
test_epoll_lost_events();
|
test_epoll_lost_events();
|
||||||
test_ready_list_fetching_logic();
|
test_ready_list_fetching_logic();
|
||||||
}
|
}
|
||||||
@ -551,6 +552,43 @@ fn test_epoll_wait_maxevent_zero() {
|
|||||||
assert_eq!(res, -1);
|
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,
|
// 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.
|
// epoll can lose events if they don't fit in the output buffer.
|
||||||
fn test_epoll_lost_events() {
|
fn test_epoll_lost_events() {
|
||||||
|
Loading…
Reference in New Issue
Block a user