#[cfg(target_os = "macos")]
mod macos;
#[cfg(unix)]
mod signals;
#[cfg(target_os = "windows")]
mod windows;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(target_os = "macos")]
pub use macos::install_terminate_hook;
#[cfg(unix)]
pub use signals::SignalWatch;
#[cfg(target_os = "windows")]
pub use windows::{ConsoleWatch, SessionEndWatch};
pub struct TerminateRequest {
session_end: bool,
reply: Option<Box<dyn FnOnce(bool) + Send>>,
}
impl TerminateRequest {
pub fn new(session_end: bool, reply: impl FnOnce(bool) + Send + 'static) -> Self {
Self {
session_end,
reply: Some(Box::new(reply)),
}
}
pub fn session_end(&self) -> bool {
self.session_end
}
pub fn reply(mut self, terminate: bool) {
if let Some(reply) = self.reply.take() {
reply(terminate);
}
}
}
impl Drop for TerminateRequest {
fn drop(&mut self) {
if let Some(reply) = self.reply.take() {
reply(false);
}
}
}
impl std::fmt::Debug for TerminateRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TerminateRequest")
.field("session_end", &self.session_end)
.field("answered", &self.reply.is_none())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct SessionEndNow {
done: Arc<AtomicBool>,
expired: Arc<AtomicBool>,
}
impl SessionEndNow {
pub fn new(done: Arc<AtomicBool>) -> Self {
Self {
done,
expired: Arc::new(AtomicBool::new(false)),
}
}
pub fn finished(&self) {
self.done.store(true, Ordering::Release);
}
pub fn expire(&self) {
self.expired.store(true, Ordering::Release);
}
pub fn is_expired(&self) -> bool {
self.expired.load(Ordering::Acquire)
}
}
#[derive(Debug, Clone)]
pub struct ConsoleClosing {
session_end: bool,
done: Arc<AtomicBool>,
}
impl ConsoleClosing {
pub fn new(session_end: bool, done: Arc<AtomicBool>) -> Self {
Self { session_end, done }
}
pub fn session_end(&self) -> bool {
self.session_end
}
pub fn finished(&self) {
self.done.store(true, Ordering::Release);
}
}
pub const SESSION_END_BUDGET: std::time::Duration = std::time::Duration::from_secs(4);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TerminationSignal {
pub signal: i32,
}
pub const SIGTERM: i32 = 15;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HiddenWindowClose;
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
pub(crate) struct OwedReplies(std::sync::atomic::AtomicUsize);
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
impl OwedReplies {
pub(crate) const fn new() -> Self {
Self(std::sync::atomic::AtomicUsize::new(0))
}
pub(crate) fn owe(&self) {
self.0.fetch_add(1, Ordering::AcqRel);
}
pub(crate) fn take(&self) -> bool {
let mut owed = self.0.load(Ordering::Acquire);
while let Some(left) = owed.checked_sub(1) {
match self
.0
.compare_exchange_weak(owed, left, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return true,
Err(now) => owed = now,
}
}
false
}
}
#[cfg(test)]
mod owed_replies_tests {
use super::OwedReplies;
#[test]
fn every_later_gets_its_reply_and_no_more() {
let owed = OwedReplies::new();
owed.owe();
owed.owe();
assert!(owed.take(), "the first request's reply");
assert!(owed.take(), "the second request's reply");
assert!(!owed.take(), "nothing more is owed");
}
}