Skip to content

Commit 4812fa6

Browse files
committed
Edge case: Only provide epoll notification for read if it emptied the buffer
1 parent 5c197a6 commit 4812fa6

File tree

4 files changed

+79
-44
lines changed

4 files changed

+79
-44
lines changed

src/shims/unix/socket.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,6 @@ impl FileDescription for SocketPair {
111111
return Ok(Ok(0));
112112
}
113113

114-
// In libc socketpair, reading from a previously full socketpair buffer won't trigger
115-
// epoll notification. We want to inform the user about this inconsistency in libc and
116-
// hope to receive a bug report if someone has a use case for this.
117-
if readbuf.buf.len() == MAX_SOCKETPAIR_BUFFER_CAPACITY {
118-
throw_unsup_format!(
119-
"A previously full socketpair has been read, please submit a \
120-
bug report in https://github.com/rust-lang/miri/issues/new"
121-
);
122-
}
123-
124114
if readbuf.buf.is_empty() {
125115
if !readbuf.buf_has_writer {
126116
// Socketpair with no writer and empty buffer.
@@ -146,9 +136,21 @@ impl FileDescription for SocketPair {
146136
// FIXME: this over-synchronizes; a more precise approach would be to
147137
// only sync with the writes whose data we will read.
148138
ecx.acquire_clock(&readbuf.clock);
139+
149140
// Do full read / partial read based on the space available.
150141
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
151142
let actual_read_size = readbuf.buf.read(bytes).unwrap();
143+
144+
// Notification should be provided for peer fd when read emptied the buffer.
145+
if let Some(peer_fd) = self.peer_fd.upgrade() {
146+
if readbuf.buf.is_empty() {
147+
// The readbuf needs to be explicitly dropped because it will cause panic when
148+
// check_and_update_readiness borrows it again.
149+
drop(readbuf);
150+
peer_fd.check_and_update_readiness(ecx)?;
151+
}
152+
}
153+
152154
return Ok(Ok(actual_read_size));
153155
}
154156

tests/fail-dep/tokio/read_from_full_socketpair.rs

Lines changed: 0 additions & 17 deletions
This file was deleted.

tests/fail-dep/tokio/read_from_full_socketpair.stderr

Lines changed: 0 additions & 14 deletions
This file was deleted.

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

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fn main() {
1616
test_epoll_ctl_del();
1717
test_pointer();
1818
test_two_same_fd_in_same_epoll_instance();
19+
test_socketpair_read();
1920
}
2021

2122
fn check_epoll_wait<const N: usize>(
@@ -339,16 +340,18 @@ fn test_epoll_socketpair_special_case() {
339340
vec![(expected_event1, expected_value1), (expected_event0, expected_value0)]
340341
));
341342

342-
// Read from fds[0]
343+
// Read from fds[0].
343344
let mut buf: [u8; 5] = [0; 5];
344345
res = unsafe {
345346
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
346347
};
347348
assert_eq!(res, 5);
348349
assert_eq!(buf, "abcde".as_bytes());
349350

350-
// No notification should be returned.
351-
assert!(check_epoll_wait::<8>(epfd, vec![]));
351+
// Notification should be provided for fds[1].
352+
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
353+
let expected_value = fds[1] as u64;
354+
assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
352355
}
353356

354357
// When file description is fully closed, epoll_wait should not provide any notification for
@@ -481,3 +484,64 @@ fn test_event_overwrite() {
481484
let expected_value = u64::try_from(fd).unwrap();
482485
assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
483486
}
487+
488+
// read in socketpair will only provide epoll notification if it emptied the buffer.
489+
fn test_socketpair_read() {
490+
// Create an epoll instance.
491+
let epfd = unsafe { libc::epoll_create1(0) };
492+
assert_ne!(epfd, -1);
493+
494+
// Create a socketpair instance.
495+
let mut fds = [-1, -1];
496+
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
497+
assert_eq!(res, 0);
498+
499+
// Register both fd to the same epoll instance.
500+
let epollet = libc::EPOLLET as u32;
501+
let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
502+
let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[0] as u64 };
503+
let mut res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
504+
assert_ne!(res, -1);
505+
let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[1] as u64 };
506+
res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
507+
assert_ne!(res, -1);
508+
509+
// Write 5 bytes to fds[1].
510+
let data = "abcde".as_bytes().as_ptr();
511+
res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() };
512+
assert_eq!(res, 5);
513+
514+
//Two notification should be received.
515+
let expected_event0 = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
516+
let expected_value0 = fds[0] as u64;
517+
let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap();
518+
let expected_value1 = fds[1] as u64;
519+
assert!(check_epoll_wait::<8>(
520+
epfd,
521+
vec![(expected_event1, expected_value1), (expected_event0, expected_value0)]
522+
));
523+
524+
// Read 3 bytes from fds[0].
525+
let mut buf: [u8; 3] = [0; 3];
526+
res = unsafe {
527+
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
528+
};
529+
assert_eq!(res, 3);
530+
assert_eq!(buf, "abc".as_bytes());
531+
532+
// No notification should be provided.
533+
assert!(check_epoll_wait::<8>(epfd, vec![]));
534+
535+
// Read until the buffer is empty.
536+
let mut buf: [u8; 2] = [0; 2];
537+
res = unsafe {
538+
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
539+
};
540+
assert_eq!(res, 2);
541+
assert_eq!(buf, "de".as_bytes());
542+
543+
// Notification should be provided.
544+
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
545+
let expected_value = fds[1] as u64;
546+
assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
547+
}

0 commit comments

Comments
 (0)