Skip to content

Commit

Permalink
Merge pull request #161 from devnexen/update_mio
Browse files Browse the repository at this point in the history
mio option update proposal, for having haiku support mostly.
  • Loading branch information
vorner authored Jul 28, 2024
2 parents 91ad1fc + 5a0b19a commit 172283a
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 1 deletion.
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());
}
}

0 comments on commit 172283a

Please sign in to comment.