Skip to content

Commit 05f0019

Browse files
committed
add some shared helpers for epoll tests
1 parent 9f064da commit 05f0019

File tree

2 files changed

+76
-50
lines changed

2 files changed

+76
-50
lines changed

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

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::convert::TryInto;
44

55
#[path = "../../utils/libc.rs"]
66
mod libc_utils;
7+
use libc_utils::epoll::*;
78
use libc_utils::*;
89

910
fn main() {
@@ -57,48 +58,36 @@ fn test_epoll_socketpair() {
5758
// Create a socketpair instance.
5859
let mut fds = [-1, -1];
5960
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
60-
let fds = [fds[1], fds[0]];
6161

6262
// Write to fd[0]
63-
let data = "abcde".as_bytes().as_ptr();
64-
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
65-
assert_eq!(res, 5);
63+
write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap();
6664

6765
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
68-
let mut ev = libc::epoll_event {
69-
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
70-
u64: u64::try_from(fds[1]).unwrap(),
71-
};
72-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
73-
assert_eq!(res, 0);
66+
epoll_ctl_add(epfd, fds[1], EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP).unwrap();
7467

7568
// Check result from epoll_wait.
76-
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
77-
let expected_value = u64::try_from(fds[1]).unwrap();
78-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
69+
check_epoll_wait_noblock::<8>(epfd, &[Ev { data: fds[1], events: EPOLLIN | EPOLLOUT }]);
7970

8071
// Check that this is indeed using "ET" (edge-trigger) semantics: a second epoll should return nothing.
81-
check_epoll_wait::<8>(epfd, &[]);
72+
check_epoll_wait_noblock::<8>(epfd, &[]);
8273

8374
// Write some more to fd[0].
84-
let data = "abcde".as_bytes().as_ptr();
85-
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
86-
assert_eq!(res, 5);
75+
write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap();
8776

8877
// This did not change the readiness of fd[1], so we should get no event.
8978
// However, Linux seems to always deliver spurious events to the peer on each write,
9079
// so we match that.
91-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
80+
check_epoll_wait_noblock::<8>(epfd, &[Ev { data: fds[1], events: EPOLLIN | EPOLLOUT }]);
9281

9382
// Close the peer socketpair.
9483
errno_check(unsafe { libc::close(fds[0]) });
9584

96-
// Check result from epoll_wait.
97-
// We expect to get a read, write, HUP notification from the close since closing an FD always unblocks reads and writes on its peer.
98-
let expected_event =
99-
u32::try_from(libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLRDHUP).unwrap();
100-
let expected_value = u64::try_from(fds[1]).unwrap();
101-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
85+
// Check result from epoll_wait. We expect to get a read, write, HUP notification from the close
86+
// since closing an FD always unblocks reads and writes on its peer.
87+
check_epoll_wait_noblock::<8>(
88+
epfd,
89+
&[Ev { data: fds[1], events: EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLRDHUP }],
90+
);
10291
}
10392

10493
// This test first registers a file description with a flag that does not lead to notification,
@@ -113,49 +102,33 @@ fn test_epoll_ctl_mod() {
113102
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
114103

115104
// Register fd[1] with EPOLLIN|EPOLLET, and data of "0".
116-
let mut ev = libc::epoll_event { events: (libc::EPOLLIN | libc::EPOLLET) as _, u64: 0 };
117-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
118-
assert_eq!(res, 0);
105+
epoll_ctl(epfd, EPOLL_CTL_ADD, fds[1], Ev { events: EPOLLIN | EPOLLET, data: 0 }).unwrap();
119106

120107
// Check result from epoll_wait. No notification would be returned.
121-
check_epoll_wait::<8>(epfd, &[]);
108+
check_epoll_wait_noblock::<8>(epfd, &[]);
122109

123110
// Use EPOLL_CTL_MOD to change to EPOLLOUT flag and data.
124-
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 1 };
125-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
126-
assert_eq!(res, 0);
111+
epoll_ctl(epfd, EPOLL_CTL_MOD, fds[1], Ev { events: EPOLLOUT | EPOLLET, data: 1 }).unwrap();
127112

128113
// Check result from epoll_wait. EPOLLOUT notification and new data is expected.
129-
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
130-
let expected_value = 1;
131-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
114+
check_epoll_wait_noblock::<8>(epfd, &[Ev { events: EPOLLOUT, data: 1 }]);
132115

133116
// Write to fds[1] and read from fds[0] to make the notification ready again
134117
// (relying on there always being an event when the buffer gets emptied).
135-
let data = "abc".as_bytes();
136-
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr().cast(), data.len()) };
137-
assert_eq!(res, 3);
138-
let mut buf = [0u8; 3];
139-
let res = unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len()) };
140-
assert_eq!(res, 3);
118+
write_all_from_slice(fds[1], "abc".as_bytes()).unwrap();
119+
read_all_into_array::<3>(fds[0]).unwrap();
141120

142121
// Now that the event is already ready, change the "data" value.
143-
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 2 };
144-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
145-
assert_eq!(res, 0);
122+
epoll_ctl(epfd, EPOLL_CTL_MOD, fds[1], Ev { events: EPOLLOUT | EPOLLET, data: 2 }).unwrap();
146123

147124
// Receive event, with latest data value.
148-
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
149-
let expected_value = 2;
150-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
125+
check_epoll_wait_noblock::<8>(epfd, &[Ev { events: EPOLLOUT, data: 2 }]);
151126

152127
// Do another update that changes nothing.
153-
let mut ev = libc::epoll_event { events: (libc::EPOLLOUT | libc::EPOLLET) as _, u64: 2 };
154-
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
155-
assert_eq!(res, 0);
128+
epoll_ctl(epfd, EPOLL_CTL_MOD, fds[1], Ev { events: EPOLLOUT | EPOLLET, data: 2 }).unwrap();
156129

157130
// This re-triggers the event, even if it's the same flags as before.
158-
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
131+
check_epoll_wait_noblock::<8>(epfd, &[Ev { events: EPOLLOUT, data: 2 }]);
159132
}
160133

161134
fn test_epoll_ctl_del() {

src/tools/miri/tests/utils/libc.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub unsafe fn read_all(
4040
return read_so_far as libc::ssize_t;
4141
}
4242

43+
/// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read.
4344
#[track_caller]
4445
pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> {
4546
let mut buf = [0; N];
@@ -70,6 +71,7 @@ pub unsafe fn write_all(
7071
return written_so_far as libc::ssize_t;
7172
}
7273

74+
/// Write the entire `buf` to `fd`. Error if not all bytes could be written.
7375
#[track_caller]
7476
pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssize_t> {
7577
let res = unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) };
@@ -80,3 +82,54 @@ pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssi
8082
Err(res)
8183
}
8284
}
85+
86+
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
87+
#[allow(unused_imports)]
88+
pub mod epoll {
89+
use libc::c_int;
90+
pub use libc::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD};
91+
// Re-export some constants we need a lot for this.
92+
pub use libc::{EPOLLET, EPOLLHUP, EPOLLIN, EPOLLOUT, EPOLLRDHUP};
93+
94+
use super::*;
95+
96+
/// The libc epoll_event type doesn't fit to the EPOLLIN etc constants, so we have our
97+
/// own type. We also make the data field an int since we typically want to store FDs there.
98+
#[derive(PartialEq, Debug)]
99+
pub struct Ev {
100+
pub events: c_int,
101+
pub data: c_int,
102+
}
103+
104+
#[track_caller]
105+
pub fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: Ev) -> io::Result<()> {
106+
let mut event = libc::epoll_event {
107+
events: event.events.cast_unsigned(),
108+
u64: event.data.try_into().unwrap(),
109+
};
110+
errno_result(unsafe { libc::epoll_ctl(epfd, op, fd, &raw mut event) })
111+
.map(|r| assert_eq!(r, 0))
112+
}
113+
114+
/// Helper for the common case of adding an FD to an epoll with the FD itself being
115+
/// the `data`.
116+
#[track_caller]
117+
pub fn epoll_ctl_add(epfd: c_int, fd: c_int, events: c_int) -> io::Result<()> {
118+
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, Ev { events, data: fd })
119+
}
120+
121+
#[track_caller]
122+
pub fn check_epoll_wait_noblock<const N: usize>(epfd: i32, expected: &[Ev]) {
123+
let mut array: [libc::epoll_event; N] = [libc::epoll_event { events: 0, u64: 0 }; N];
124+
let num = errno_result(unsafe {
125+
libc::epoll_wait(epfd, array.as_mut_ptr(), N.try_into().unwrap(), 0)
126+
})
127+
.unwrap();
128+
let got = &mut array[..num.try_into().unwrap()];
129+
let got = got
130+
.iter()
131+
.map(|e| Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() })
132+
.collect::<Vec<_>>();
133+
assert_eq!(got, expected, "got wrong notifications");
134+
}
135+
}

0 commit comments

Comments
 (0)