Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mio option update proposal, for having haiku support mostly. #161

Merged
merged 2 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions signal-hook-mio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ maintenance = { status = "actively-developed" }
support-v0_6 = ["mio-0_6", "mio-uds"]
support-v0_7 = ["mio-0_7"]
support-v0_8 = ["mio-0_8"]
support-v1_0 = ["mio-1_0"]

[dependencies]
libc = "~0.2"
signal-hook = { version = "~0.3", path = ".." }
mio-1_0 = { package = "mio", version = "~1.0", features = ["net", "os-ext"], optional = true }
mio-0_8 = { package = "mio", version = "~0.8", features = ["net", "os-ext"], optional = true }
mio-0_7 = { package = "mio", version = "~0.7", features = ["os-util", "uds"], optional = true }
mio-0_6 = { package = "mio", version = "~0.6", optional = true }
Expand Down
96 changes: 95 additions & 1 deletion signal-hook-mio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
//! * `support-v0_6` for sub module [`v0_6`]
//! * `support-v0_7` for sub module [`v0_7`]
//! * `support-v0_8` for sub module [`v0_8`]
//! * `support-v0_8` for sub module [`v1_0`]
//!
//! See the specific sub modules for usage examples.

#[cfg(any(
feature = "support-v0_6",
feature = "support-v0_7",
feature = "support-v0_8"
feature = "support-v0_8",
feature = "support-v1_0"
))]
macro_rules! implement_signals_with_pipe {
($pipe:path) => {
Expand Down Expand Up @@ -94,6 +96,98 @@ macro_rules! implement_signals_with_pipe {
};
}

/// A module for integrating signal handling with the MIO 1.0 runtime.
///
/// This provides the [`Signals`][v1_0::Signals] struct as an abstraction
/// which can be used with [`mio::Poll`][mio_1_0::Poll].
///
/// # Examples
///
/// ```rust
/// # use mio_1_0 as mio;
/// use std::io::{Error, ErrorKind};
///
/// use signal_hook::consts::signal::*;
/// use signal_hook_mio::v1_0::Signals;
///
/// use mio::{Events, Poll, Interest, Token};
///
/// fn main() -> Result<(), Error> {
/// let mut poll = Poll::new()?;
///
/// let mut signals = Signals::new(&[
/// SIGTERM,
/// # SIGUSR1,
/// ])?;
///
/// let signal_token = Token(0);
///
/// poll.registry().register(&mut signals, signal_token, Interest::READABLE)?;
/// # signal_hook::low_level::raise(SIGUSR1).unwrap(); // Just for terminating the example
///
/// let mut events = Events::with_capacity(10);
/// 'outer: loop {
/// poll.poll(&mut events, None)
/// .or_else(|e| if e.kind() == ErrorKind::Interrupted {
/// // We get interrupt when a signal happens inside poll. That's non-fatal, just
/// // retry.
/// events.clear();
/// Ok(())
/// } else {
/// Err(e)
/// })?;
/// for event in events.iter() {
/// match event.token() {
/// Token(0) => {
/// for signal in signals.pending() {
/// match signal {
/// SIGTERM => break 'outer,
/// # SIGUSR1 => return Ok(()),
/// _ => unreachable!(),
/// }
/// }
/// },
/// _ => unreachable!("Register other sources and match for their tokens here"),
/// }
/// }
/// }
///
/// Ok(())
/// }
/// ```
#[cfg(feature = "support-v1_0")]
pub mod v1_0 {
use mio::event::Source;
use mio::{Interest, Registry, Token};
use mio_1_0 as mio;

implement_signals_with_pipe!(mio::net::UnixStream);

impl Source for Signals {
fn register(
&mut self,
registry: &Registry,
token: Token,
interest: Interest,
) -> Result<(), Error> {
self.0.get_read_mut().register(registry, token, interest)
}

fn reregister(
&mut self,
registry: &Registry,
token: Token,
interest: Interest,
) -> Result<(), Error> {
self.0.get_read_mut().reregister(registry, token, interest)
}

fn deregister(&mut self, registry: &Registry) -> Result<(), Error> {
self.0.get_read_mut().deregister(registry)
}
}
}

/// A module for integrating signal handling with the MIO 0.8 runtime.
///
/// This provides the [`Signals`][v0_8::Signals] struct as an abstraction
Expand Down
118 changes: 118 additions & 0 deletions signal-hook-mio/tests/mio_1_0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#![cfg(feature = "support-v1_0")]

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use mio_1_0::{Events, Interest, Poll, Token};

use signal_hook::consts::{SIGUSR1, SIGUSR2};
use signal_hook::low_level::raise;
use signal_hook_mio::v1_0::Signals;

use serial_test::serial;

use libc::c_int;

#[test]
#[serial]
fn mio_wakeup() {
let mut signals = Signals::new(&[SIGUSR1]).unwrap();
let mut poll = Poll::new().unwrap();
let token = Token(0);
poll.registry()
.register(&mut signals, token, Interest::READABLE)
.unwrap();

let mut events = Events::with_capacity(10);

// The self pipe shouldn't be readable yet
poll.poll(&mut events, Some(Duration::from_secs(0)))
.unwrap();
assert!(events.is_empty());

raise(SIGUSR1).unwrap();
poll.poll(&mut events, Some(Duration::from_secs(10)))
.unwrap();
let event = events.iter().next().unwrap();

assert!(event.is_readable());
assert_eq!(token, event.token());
let sig = signals.pending().next().unwrap();
assert_eq!(SIGUSR1, sig);

// The self pipe shouldn't be readable after consuming signals
poll.poll(&mut events, Some(Duration::from_secs(0)))
.unwrap();
assert!(events.is_empty());
}

#[test]
#[serial]
fn mio_multiple_signals() {
let mut signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap();
let mut poll = Poll::new().unwrap();
let token = Token(0);
poll.registry()
.register(&mut signals, token, Interest::READABLE)
.unwrap();

let mut events = Events::with_capacity(10);

raise(SIGUSR1).unwrap();
raise(SIGUSR2).unwrap();

poll.poll(&mut events, Some(Duration::from_secs(10)))
.unwrap();

let event = events.iter().next().unwrap();
assert!(event.is_readable());

let sigs: Vec<c_int> = signals.pending().collect();
assert_eq!(2, sigs.len());
assert!(sigs.contains(&SIGUSR1));
assert!(sigs.contains(&SIGUSR2));

// The self pipe shouldn't be completely empty after calling pending()
poll.poll(&mut events, Some(Duration::from_secs(0)))
.unwrap();
assert!(events.is_empty());
}

#[test]
#[serial]
fn mio_parallel_multiple() {
let mut signals = Signals::new(&[SIGUSR1]).unwrap();
let mut poll = Poll::new().unwrap();
let token = Token(0);
poll.registry()
.register(&mut signals, token, Interest::READABLE)
.unwrap();

let mut events = Events::with_capacity(10);

let thread_done = Arc::new(AtomicBool::new(false));

let done = Arc::clone(&thread_done);
thread::spawn(move || {
for _ in 0..10 {
// Wait some time to allow main thread to poll
thread::sleep(Duration::from_millis(25));
raise(SIGUSR1).unwrap();
}
done.store(true, Ordering::SeqCst);

// Raise a final signal so the main thread wakes up
// if it already called poll.
raise(SIGUSR1).unwrap();
});

while !thread_done.load(Ordering::SeqCst) {
poll.poll(&mut events, Some(Duration::from_secs(10)))
.unwrap();
let event = events.iter().next().unwrap();
assert!(event.is_readable());
assert_eq!(SIGUSR1, signals.pending().next().unwrap());
}
}
Loading