Add edge/oneshot combination mode (#96)

This commit is contained in:
John Nunley 2023-03-25 15:22:45 +01:00 committed by GitHub
parent e340458d3a
commit 0f38ed35ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 4 deletions

View File

@ -222,6 +222,7 @@ impl Poller {
PollMode::Oneshot => libc::EPOLLONESHOT,
PollMode::Level => 0,
PollMode::Edge => libc::EPOLLET,
PollMode::EdgeOneshot => libc::EPOLLONESHOT | libc::EPOLLET,
};
if ev.readable {
flags |= read_flags();

View File

@ -146,7 +146,7 @@ impl Poller {
);
// We don't support edge-triggered events.
if matches!(mode, PollMode::Edge) {
if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"edge-triggered events are not supported",
@ -206,7 +206,7 @@ impl Poller {
);
// We don't support edge-triggered events.
if matches!(mode, PollMode::Edge) {
if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"edge-triggered events are not supported",

View File

@ -251,6 +251,7 @@ pub(crate) fn mode_to_flags(mode: PollMode) -> FilterFlags {
PollMode::Oneshot => libc::EV_ONESHOT,
PollMode::Level => 0,
PollMode::Edge => libc::EV_CLEAR,
PollMode::EdgeOneshot => libc::EV_ONESHOT | libc::EV_CLEAR,
}
}

View File

@ -168,6 +168,16 @@ pub enum PollMode {
/// this mode in an unsupported operating system will raise an error. You can check if
/// the operating system supports this mode by calling `Poller::supports_edge`.
Edge,
/// Poll in both edge-triggered and oneshot mode.
///
/// This mode is similar to the `Oneshot` mode, but it will only deliver one event per new
/// event.
///
/// Not all operating system support this mode. Trying to register a file descriptor with
/// this mode in an unsupported operating system will raise an error. You can check if
/// the operating system supports this mode by calling `Poller::supports_edge`.
EdgeOneshot,
}
impl Event {

View File

@ -416,7 +416,7 @@ fn cvt_mode_as_remove(mode: PollMode) -> io::Result<bool> {
match mode {
PollMode::Oneshot => Ok(true),
PollMode::Level => Ok(false),
PollMode::Edge => Err(crate::unsupported_error(
_ => Err(crate::unsupported_error(
"edge-triggered I/O events are not supported in poll()",
)),
}

View File

@ -53,7 +53,7 @@ impl Poller {
flags |= libc::POLLOUT;
}
if let PollMode::Edge | PollMode::Level = mode {
if mode != PollMode::Oneshot {
return Err(crate::unsupported_error(
"this kind of event is not supported with event ports",
));

View File

@ -162,6 +162,81 @@ fn edge_triggered() {
assert_eq!(events, [Event::readable(reader_token)]);
}
#[test]
fn edge_oneshot_triggered() {
// Create our streams.
let (mut reader, mut writer) = tcp_pair().unwrap();
let reader_token = 1;
// Create our poller and register our streams.
let poller = Poller::new().unwrap();
if poller
.add_with_mode(
&reader,
Event::readable(reader_token),
PollMode::EdgeOneshot,
)
.is_err()
{
// Only panic if we're on a platform that should support level mode.
cfg_if::cfg_if! {
if #[cfg(all(
any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly"
),
not(polling_test_poll_backend)
))] {
panic!("Edge mode should be supported on this platform");
} else {
return;
}
}
}
// Write some data to the writer.
let data = [1, 2, 3, 4, 5];
writer.write_all(&data).unwrap();
// A "readable" notification should be delivered.
let mut events = Vec::new();
poller
.wait(&mut events, Some(Duration::from_secs(10)))
.unwrap();
assert_eq!(events, [Event::readable(reader_token)]);
// If we read some of the data, the notification should not still be available.
reader.read_exact(&mut [0; 3]).unwrap();
events.clear();
poller
.wait(&mut events, Some(Duration::from_secs(0)))
.unwrap();
assert_eq!(events, []);
// If we modify to re-enable the notification, it should be delivered.
poller
.modify_with_mode(
&reader,
Event::readable(reader_token),
PollMode::EdgeOneshot,
)
.unwrap();
events.clear();
poller
.wait(&mut events, Some(Duration::from_secs(0)))
.unwrap();
assert_eq!(events, [Event::readable(reader_token)]);
}
fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let a = TcpStream::connect(listener.local_addr()?)?;