diff --git a/Cargo.lock b/Cargo.lock index fe3bfd9..0c7eb09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,6 +493,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "mio" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8" +dependencies = [ + "libc", + "log", + "miow 0.3.7", + "ntapi", + "wasi", + "winapi 0.3.9", +] + [[package]] name = "mio-uds" version = "0.6.8" @@ -701,6 +715,7 @@ dependencies = [ "libc", "mio 0.6.23", "mio 0.7.14", + "mio 0.8.1", "mio-uds", "serial_test", "signal-hook", @@ -827,6 +842,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.78" diff --git a/signal-hook-mio/Cargo.toml b/signal-hook-mio/Cargo.toml index 6c9d931..fa348f9 100644 --- a/signal-hook-mio/Cargo.toml +++ b/signal-hook-mio/Cargo.toml @@ -22,12 +22,14 @@ maintenance = { status = "actively-developed" } [features] support-v0_6 = ["mio-0_6", "mio-uds"] support-v0_7 = ["mio-0_7"] +support-v0_8 = ["mio-0_8"] [dependencies] libc = "~0.2" signal-hook = { version = "~0.3", path = ".." } -mio-0_7 = { package = "mio", version = "~0.7", features = ["os-util", "uds"], optional = true} -mio-0_6 = { package = "mio", version = "~0.6", 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 } mio-uds = { version = "~0.6", optional = true} [dev-dependencies] diff --git a/signal-hook-mio/src/lib.rs b/signal-hook-mio/src/lib.rs index 9b0c85c..215fdf7 100644 --- a/signal-hook-mio/src/lib.rs +++ b/signal-hook-mio/src/lib.rs @@ -10,10 +10,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`] //! //! See the specific sub modules for usage examples. -#[cfg(any(feature = "support-v0_6", feature = "support-v0_7"))] +#[cfg(any( + feature = "support-v0_6", + feature = "support-v0_7", + feature = "support-v0_8" +))] macro_rules! implement_signals_with_pipe { ($pipe:path) => { use std::borrow::Borrow; @@ -89,6 +94,98 @@ macro_rules! implement_signals_with_pipe { }; } +/// A module for integrating signal handling with the MIO 0.8 runtime. +/// +/// This provides the [`Signals`][v0_8::Signals] struct as an abstraction +/// which can be used with [`mio::Poll`][mio_0_8::Poll]. +/// +/// # Examples +/// +/// ```rust +/// # use mio_0_8 as mio; +/// use std::io::{Error, ErrorKind}; +/// +/// use signal_hook::consts::signal::*; +/// use signal_hook_mio::v0_8::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-v0_8")] +pub mod v0_8 { + use mio::event::Source; + use mio::{Interest, Registry, Token}; + use mio_0_8 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.7 runtime. /// /// This provides the [`Signals`][v0_7::Signals] struct as an abstraction diff --git a/signal-hook-mio/tests/mio_0_8.rs b/signal-hook-mio/tests/mio_0_8.rs new file mode 100644 index 0000000..1022b5e --- /dev/null +++ b/signal-hook-mio/tests/mio_0_8.rs @@ -0,0 +1,118 @@ +#![cfg(feature = "support-v0_8")] + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use mio_0_8::{Events, Interest, Poll, Token}; + +use signal_hook::consts::{SIGUSR1, SIGUSR2}; +use signal_hook::low_level::raise; +use signal_hook_mio::v0_8::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()); + } +}