Skip to content

Commit 78dfb8a

Browse files
committed
Auto merge of #3814 - tiif:epollhup, r=RalfJung
Add epoll EPOLLHUP flag support Related discussion in rust-lang/miri#3811 (comment). This PR added support for ``EPOLLHUP`` flag.
2 parents 94e57fc + cab81d3 commit 78dfb8a

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

src/tools/miri/src/shims/unix/linux/epoll.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,24 @@ pub struct EpollReadyEvents {
6969
/// Stream socket peer closed connection, or shut down writing
7070
/// half of connection.
7171
pub epollrdhup: bool,
72+
/// For stream socket, this event merely indicates that the peer
73+
/// closed its end of the channel.
74+
/// Unlike epollrdhup, this should only be set when the stream is fully closed.
75+
/// epollrdhup also gets set when only the write half is closed, which is possible
76+
/// via `shutdown(_, SHUT_WR)`.
77+
pub epollhup: bool,
7278
}
7379

7480
impl EpollReadyEvents {
7581
pub fn new() -> Self {
76-
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false }
82+
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false, epollhup: false }
7783
}
7884

7985
pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
8086
let epollin = ecx.eval_libc_u32("EPOLLIN");
8187
let epollout = ecx.eval_libc_u32("EPOLLOUT");
8288
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
89+
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
8390

8491
let mut bitmask = 0;
8592
if self.epollin {
@@ -91,6 +98,9 @@ impl EpollReadyEvents {
9198
if self.epollrdhup {
9299
bitmask |= epollrdhup;
93100
}
101+
if self.epollhup {
102+
bitmask |= epollhup;
103+
}
94104
bitmask
95105
}
96106
}
@@ -217,6 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
217227
let epollout = this.eval_libc_u32("EPOLLOUT");
218228
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
219229
let epollet = this.eval_libc_u32("EPOLLET");
230+
let epollhup = this.eval_libc_u32("EPOLLHUP");
220231

221232
// Fail on unsupported operations.
222233
if op & epoll_ctl_add != epoll_ctl_add
@@ -244,11 +255,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
244255

245256
if op == epoll_ctl_add || op == epoll_ctl_mod {
246257
// Read event bitmask and data from epoll_event passed by caller.
247-
let events = this.read_scalar(&this.project_field(&event, 0)?)?.to_u32()?;
258+
let mut events = this.read_scalar(&this.project_field(&event, 0)?)?.to_u32()?;
248259
let data = this.read_scalar(&this.project_field(&event, 1)?)?.to_u64()?;
249260

250261
// Unset the flag we support to discover if any unsupported flags are used.
251262
let mut flags = events;
263+
// epoll_wait(2) will always wait for epollhup; it is not
264+
// necessary to set it in events when calling epoll_ctl().
265+
// So we will always set this event type.
266+
events |= epollhup;
267+
252268
if events & epollet != epollet {
253269
// We only support edge-triggered notification for now.
254270
throw_unsup_format!("epoll_ctl: epollet flag must be included.");
@@ -264,6 +280,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
264280
if flags & epollrdhup == epollrdhup {
265281
flags &= !epollrdhup;
266282
}
283+
if flags & epollhup == epollhup {
284+
flags &= !epollhup;
285+
}
267286
if flags != 0 {
268287
throw_unsup_format!(
269288
"epoll_ctl: encountered unknown unsupported flags {:#x}",

src/tools/miri/src/shims/unix/socket.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ impl FileDescription for SocketPair {
4949
}
5050

5151
fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
52-
// We only check the status of EPOLLIN, EPOLLOUT and EPOLLRDHUP flags. If other event flags
53-
// need to be supported in the future, the check should be added here.
52+
// We only check the status of EPOLLIN, EPOLLOUT, EPOLLHUP and EPOLLRDHUP flags.
53+
// If other event flags need to be supported in the future, the check should be added here.
5454

5555
let mut epoll_ready_events = EpollReadyEvents::new();
5656

@@ -69,8 +69,10 @@ impl FileDescription for SocketPair {
6969
epoll_ready_events.epollout = true;
7070
}
7171
} else {
72-
// Peer FD has been closed.
72+
// Peer FD has been closed. This always sets both the RDHUP and HUP flags
73+
// as we do not support `shutdown` that could be used to partially close the stream.
7374
epoll_ready_events.epollrdhup = true;
75+
epoll_ready_events.epollhup = true;
7476
// Since the peer is closed, even if no data is available reads will return EOF and
7577
// writes will return EPIPE. In other words, they won't block, so we mark this as ready
7678
// for read and write.

src/tools/miri/tests/pass-dep/libc/libc-epoll.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ fn test_epoll_socketpair() {
9797

9898
// Check result from epoll_wait.
9999
// We expect to get a read, write, HUP notification from the close since closing an FD always unblocks reads and writes on its peer.
100-
let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
100+
let expected_event =
101+
u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP).unwrap();
101102
let expected_value = u64::try_from(fds[1]).unwrap();
102103
assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
103104
}
@@ -141,7 +142,8 @@ fn test_epoll_ctl_mod() {
141142
assert_eq!(res, 0);
142143

143144
// Check result from epoll_wait.
144-
let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
145+
let expected_event =
146+
u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP).unwrap();
145147
let expected_value = u64::try_from(fds[1]).unwrap();
146148
assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
147149
}

0 commit comments

Comments
 (0)