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

Add wait_for_shutdown_signal convenience function. #156

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
**/*.rs.bk
tags
.ccls-cache
.idea
1 change: 1 addition & 0 deletions Cargo.lock

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

19 changes: 19 additions & 0 deletions signal-hook-async-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly};
use async_io::Async;
use futures_lite::io::AsyncRead;
use futures_lite::stream::Stream;
use futures_lite::StreamExt;
use signal_hook::consts;

/// An asynchronous [`Stream`] of arriving signals.
///
Expand Down Expand Up @@ -133,3 +135,20 @@ impl<E: Exfiltrator> Stream for SignalsInfo<E> {
/// This one simply returns the signal numbers, while [`SignalsInfo`] can provide additional
/// information.
pub type Signals = SignalsInfo<SignalOnly>;

/// Waits for the the process to receive a shutdown signal.
/// Waits for any of SIGHUP, SIGINT, SIGQUIT, and SIGTERM.
/// # Errors
/// Returns `Err` after failing to register the signal handler.
pub async fn wait_for_shutdown_signal() -> Result<(), String> {
let signals = [
consts::SIGHUP,
consts::SIGINT,
consts::SIGQUIT,
consts::SIGTERM,
];
let mut signals = Signals::new(&signals)
.map_err(|e| format!("error setting up handler for signals {signals:?}: {e}"))?;
let _ = signals.next().await;
Ok(())
}
29 changes: 26 additions & 3 deletions signal-hook-async-std/tests/async_std.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use async_std::stream::StreamExt;
use std::convert::TryFrom;

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

use signal_hook::consts::SIGUSR1;
use signal_hook::consts::{SIGHUP, SIGUSR1};
use signal_hook::low_level::raise;
use signal_hook_async_std::Signals;

Expand Down Expand Up @@ -52,3 +53,25 @@ async fn delayed() {
signals_task.await;
assert!(recieved.load(Ordering::SeqCst));
}

#[async_std::test]
#[serial]
async fn wait_for_shutdown_signal() {
let elapsed_ms = Arc::new(AtomicU64::new(0));
let elapsed_ms_clone = Arc::clone(&elapsed_ms);
async_std::task::spawn(async move {
let before = Instant::now();
signal_hook_async_std::wait_for_shutdown_signal()
.await
.unwrap();
let elapsed_ms_u64 =
u64::try_from(Instant::now().saturating_duration_since(before).as_millis())
.unwrap_or(u64::MAX);
elapsed_ms_clone.store(elapsed_ms_u64, Ordering::Release)
});
async_std::task::sleep(Duration::from_millis(100)).await;
raise(SIGHUP).unwrap();
async_std::task::sleep(Duration::from_millis(100)).await;
let elapsed_ms_u64 = elapsed_ms.load(Ordering::Acquire);
assert!((50..=150).contains(&elapsed_ms_u64), "{:?}", elapsed_ms_u64);
}
2 changes: 2 additions & 0 deletions signal-hook-tokio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ travis-ci = { repository = "vorner/signal-hook" }
maintenance = { status = "actively-developed" }

[features]
convenience = ["futures-v0_3", "futures-util"]
futures-v0_3 = ["futures-core-0_3"]

[dependencies]
libc = "~0.2"
signal-hook = { version = "~0.3", path = ".." }
futures-core-0_3 = { package = "futures-core", version = "~0.3", optional = true }
futures-util = { version = "~0.3", features = [], optional = true }
tokio = { version = "~1", features = ["net"] }

[dev-dependencies]
Expand Down
21 changes: 21 additions & 0 deletions signal-hook-tokio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly};

#[cfg(feature = "futures-v0_3")]
use futures_core_0_3::Stream;
#[cfg(feature = "convenience")]
use futures_util::StreamExt;
use signal_hook::consts;

/// An asynchronous [`Stream`] of arriving signals.
///
Expand Down Expand Up @@ -156,3 +159,21 @@ impl<E: Exfiltrator> Stream for SignalsInfo<E> {
}
}
}

#[cfg(feature = "convenience")]
/// Waits for the the process to receive a shutdown signal.
/// Waits for any of SIGHUP, SIGINT, SIGQUIT, and SIGTERM.
/// # Errors
/// Returns `Err` after failing to register the signal handler.
pub async fn wait_for_shutdown_signal() -> Result<(), String> {
let signals = [
consts::SIGHUP,
consts::SIGINT,
consts::SIGQUIT,
consts::SIGTERM,
];
let mut signals = Signals::new(&signals)
.map_err(|e| format!("error setting up handler for signals {signals:?}: {e}"))?;
let _ = signals.next().await;
Ok(())
}
27 changes: 24 additions & 3 deletions signal-hook-tokio/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

use futures::stream::StreamExt;

use std::sync::atomic::{AtomicBool, Ordering};
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use std::time::{Duration, Instant};

use signal_hook::consts::SIGUSR1;
use signal_hook::consts::{SIGHUP, SIGUSR1};
use signal_hook::low_level::raise;
use signal_hook_tokio::Signals;

Expand Down Expand Up @@ -54,3 +55,23 @@ async fn delayed() {
signals_task.await.unwrap();
assert!(recieved.load(Ordering::SeqCst));
}

#[tokio::test]
#[serial]
async fn wait_for_shutdown_signal() {
let elapsed_ms = Arc::new(AtomicU64::new(0));
let elapsed_ms_clone = Arc::clone(&elapsed_ms);
tokio::spawn(async move {
let before = Instant::now();
signal_hook_tokio::wait_for_shutdown_signal().await.unwrap();
let elapsed_ms_u64 =
u64::try_from(Instant::now().saturating_duration_since(before).as_millis())
.unwrap_or(u64::MAX);
elapsed_ms_clone.store(elapsed_ms_u64, Ordering::Release)
});
tokio::time::sleep(Duration::from_millis(100)).await;
raise(SIGHUP).unwrap();
tokio::time::sleep(Duration::from_millis(100)).await;
let elapsed_ms_u64 = elapsed_ms.load(Ordering::Acquire);
assert!((50..=150).contains(&elapsed_ms_u64), "{:?}", elapsed_ms_u64);
}
17 changes: 17 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,20 @@ pub mod consts {
}

pub use signal_hook_registry::SigId;

/// Waits for the the process to receive a shutdown signal.
/// Waits for any of SIGHUP, SIGINT, SIGQUIT, and SIGTERM.
/// # Errors
/// Returns `Err` after failing to register the signal handler.
pub fn wait_for_shutdown_signal() -> Result<(), String> {
let signals = [
consts::SIGHUP,
consts::SIGINT,
consts::SIGQUIT,
consts::SIGTERM,
];
let mut signals = iterator::Signals::new(&signals)
.map_err(|e| format!("error setting up handler for signals {signals:?}: {e}"))?;
let _ = signals.forever().next();
Ok(())
}
20 changes: 20 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::time::{Duration, Instant};

#[test]
fn wait_for_shutdown_signal() {
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
std::thread::spawn(move || {
let before = Instant::now();
signal_hook::wait_for_shutdown_signal().unwrap();
let elapsed = Instant::now().saturating_duration_since(before);
sender.send(elapsed).unwrap();
});
std::thread::sleep(Duration::from_millis(100));
signal_hook::low_level::raise(signal_hook::consts::SIGHUP).unwrap();
let elapsed = receiver.recv_timeout(Duration::from_millis(100)).unwrap();
assert!(
(Duration::from_millis(50)..=Duration::from_millis(150)).contains(&elapsed),
"{:?}",
elapsed
);
}