summaryrefslogtreecommitdiffstats
path: root/vendor/gix-tempfile/src/signal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-tempfile/src/signal.rs')
-rw-r--r--vendor/gix-tempfile/src/signal.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/vendor/gix-tempfile/src/signal.rs b/vendor/gix-tempfile/src/signal.rs
new file mode 100644
index 000000000..3e0e7729c
--- /dev/null
+++ b/vendor/gix-tempfile/src/signal.rs
@@ -0,0 +1,100 @@
+use crate::REGISTRY;
+use once_cell::sync::Lazy;
+
+/// Initialize signal handlers and other state to keep track of tempfiles, and **must be called before the first tempfile is created**,
+/// allowing to set the `mode` in which signal handlers are installed.
+///
+/// Only has an effect the first time it is called.
+///
+/// Note that it is possible to not call this function and instead call
+/// [registry::cleanup_tempfiles_signal_safe()][crate::registry::cleanup_tempfiles_signal_safe()]
+/// from a signal handler under the application's control.
+pub fn setup(mode: handler::Mode) {
+ handler::MODE.store(mode as usize, std::sync::atomic::Ordering::SeqCst);
+ Lazy::force(&REGISTRY);
+}
+
+///
+pub mod handler {
+ use std::sync::atomic::AtomicUsize;
+
+ pub(crate) static MODE: AtomicUsize = AtomicUsize::new(Mode::None as usize);
+
+ /// Define how our signal handlers act
+ #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
+ pub enum Mode {
+ /// Do not install a signal handler at all, but have somebody else call our handler directly.
+ None = 0,
+ /// Delete all remaining registered tempfiles on termination.
+ DeleteTempfilesOnTermination = 1,
+ /// Delete all remaining registered tempfiles on termination and emulate the default handler behaviour.
+ ///
+ /// This typically leads to the process being aborted.
+ DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour = 2,
+ }
+
+ impl Default for Mode {
+ /// By default we will emulate the default behaviour and abort the process.
+ ///
+ /// While testing, we will not abort the process.
+ fn default() -> Self {
+ if cfg!(test) {
+ Mode::DeleteTempfilesOnTermination
+ } else {
+ Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour
+ }
+ }
+ }
+
+ /// On linux we can handle the actual signal as we know it.
+ #[cfg(not(windows))]
+ pub(crate) fn cleanup_tempfiles_nix(sig: &libc::siginfo_t) {
+ crate::registry::cleanup_tempfiles_signal_safe();
+ let restore_original_behaviour = Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour as usize;
+ if MODE.load(std::sync::atomic::Ordering::SeqCst) == restore_original_behaviour {
+ signal_hook::low_level::emulate_default_handler(sig.si_signo).ok();
+ }
+ }
+
+ /// On windows, assume sig-term and emulate sig-term unconditionally.
+ #[cfg(windows)]
+ pub(crate) fn cleanup_tempfiles_windows() {
+ crate::registry::cleanup_tempfiles_signal_safe();
+ let restore_original_behaviour = Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour as usize;
+ if MODE.load(std::sync::atomic::Ordering::SeqCst) == restore_original_behaviour {
+ signal_hook::low_level::emulate_default_handler(signal_hook::consts::SIGTERM).ok();
+ }
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use std::path::Path;
+
+ use crate::{AutoRemove, ContainingDirectory};
+
+ fn filecount_in(path: impl AsRef<Path>) -> usize {
+ std::fs::read_dir(path).expect("valid dir").count()
+ }
+
+ #[test]
+ fn various_termination_signals_remove_tempfiles_unconditionally() -> Result<(), Box<dyn std::error::Error>> {
+ crate::signal::setup(Default::default());
+ let dir = tempfile::tempdir()?;
+ for sig in signal_hook::consts::TERM_SIGNALS {
+ let _tempfile = crate::new(dir.path(), ContainingDirectory::Exists, AutoRemove::Tempfile)?;
+ assert_eq!(
+ filecount_in(dir.path()),
+ 1,
+ "only one tempfile exists no matter the iteration"
+ );
+ signal_hook::low_level::raise(*sig)?;
+ assert_eq!(
+ filecount_in(dir.path()),
+ 0,
+ "the signal triggers removal but won't terminate the process (anymore)"
+ );
+ }
+ Ok(())
+ }
+ }
+}