diff --git a/signal-hook-mio/src/lib.rs b/signal-hook-mio/src/lib.rs index 215fdf7..2833f67 100644 --- a/signal-hook-mio/src/lib.rs +++ b/signal-hook-mio/src/lib.rs @@ -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) => { @@ -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 diff --git a/signal-hook-mio/tests/mio_1_0.rs b/signal-hook-mio/tests/mio_1_0.rs new file mode 100644 index 0000000..ebc01a3 --- /dev/null +++ b/signal-hook-mio/tests/mio_1_0.rs @@ -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 = 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()); + } +}