mirror of https://github.com/smol-rs/polling
Add edge/oneshot combination mode (#96)
This commit is contained in:
parent
e340458d3a
commit
0f38ed35ea
|
@ -222,6 +222,7 @@ impl Poller {
|
||||||
PollMode::Oneshot => libc::EPOLLONESHOT,
|
PollMode::Oneshot => libc::EPOLLONESHOT,
|
||||||
PollMode::Level => 0,
|
PollMode::Level => 0,
|
||||||
PollMode::Edge => libc::EPOLLET,
|
PollMode::Edge => libc::EPOLLET,
|
||||||
|
PollMode::EdgeOneshot => libc::EPOLLONESHOT | libc::EPOLLET,
|
||||||
};
|
};
|
||||||
if ev.readable {
|
if ev.readable {
|
||||||
flags |= read_flags();
|
flags |= read_flags();
|
||||||
|
|
|
@ -146,7 +146,7 @@ impl Poller {
|
||||||
);
|
);
|
||||||
|
|
||||||
// We don't support edge-triggered events.
|
// We don't support edge-triggered events.
|
||||||
if matches!(mode, PollMode::Edge) {
|
if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"edge-triggered events are not supported",
|
"edge-triggered events are not supported",
|
||||||
|
@ -206,7 +206,7 @@ impl Poller {
|
||||||
);
|
);
|
||||||
|
|
||||||
// We don't support edge-triggered events.
|
// We don't support edge-triggered events.
|
||||||
if matches!(mode, PollMode::Edge) {
|
if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"edge-triggered events are not supported",
|
"edge-triggered events are not supported",
|
||||||
|
|
|
@ -251,6 +251,7 @@ pub(crate) fn mode_to_flags(mode: PollMode) -> FilterFlags {
|
||||||
PollMode::Oneshot => libc::EV_ONESHOT,
|
PollMode::Oneshot => libc::EV_ONESHOT,
|
||||||
PollMode::Level => 0,
|
PollMode::Level => 0,
|
||||||
PollMode::Edge => libc::EV_CLEAR,
|
PollMode::Edge => libc::EV_CLEAR,
|
||||||
|
PollMode::EdgeOneshot => libc::EV_ONESHOT | libc::EV_CLEAR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -168,6 +168,16 @@ pub enum PollMode {
|
||||||
/// this mode in an unsupported operating system will raise an error. You can check if
|
/// 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`.
|
/// the operating system supports this mode by calling `Poller::supports_edge`.
|
||||||
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 {
|
impl Event {
|
||||||
|
|
|
@ -416,7 +416,7 @@ fn cvt_mode_as_remove(mode: PollMode) -> io::Result<bool> {
|
||||||
match mode {
|
match mode {
|
||||||
PollMode::Oneshot => Ok(true),
|
PollMode::Oneshot => Ok(true),
|
||||||
PollMode::Level => Ok(false),
|
PollMode::Level => Ok(false),
|
||||||
PollMode::Edge => Err(crate::unsupported_error(
|
_ => Err(crate::unsupported_error(
|
||||||
"edge-triggered I/O events are not supported in poll()",
|
"edge-triggered I/O events are not supported in poll()",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl Poller {
|
||||||
flags |= libc::POLLOUT;
|
flags |= libc::POLLOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let PollMode::Edge | PollMode::Level = mode {
|
if mode != PollMode::Oneshot {
|
||||||
return Err(crate::unsupported_error(
|
return Err(crate::unsupported_error(
|
||||||
"this kind of event is not supported with event ports",
|
"this kind of event is not supported with event ports",
|
||||||
));
|
));
|
||||||
|
|
|
@ -162,6 +162,81 @@ fn edge_triggered() {
|
||||||
assert_eq!(events, [Event::readable(reader_token)]);
|
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)> {
|
fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> {
|
||||||
let listener = TcpListener::bind("127.0.0.1:0")?;
|
let listener = TcpListener::bind("127.0.0.1:0")?;
|
||||||
let a = TcpStream::connect(listener.local_addr()?)?;
|
let a = TcpStream::connect(listener.local_addr()?)?;
|
||||||
|
|
Loading…
Reference in New Issue