killswitch_std/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
#![deny(missing_docs)]
//! A simple implementation of a kill switch, using only the standard library
//!
//! # Example
//! ```
//!# tokio_test::block_on( async {
//! use std::time::Duration;
//! use killswitch_std::KillSwitch;
//!
//! // Create a kill switch
//! let kill = KillSwitch::default();
//!
//! println!("Is kill switch set? {}", kill);
//!
//! // Now make a couple of clones and check
//! let k1 = kill.watcher();
//! let t1 = tokio::spawn(async move {
//! let duration = Duration::from_secs(2);
//! for _ in 0..5 {
//! tokio::time::sleep(duration).await;
//! println!("Kill switch on thread 1: {}", k1);
//! }
//! println!("Thread 1 wrapping up");
//! });
//!
//! // Now make a couple of clones and check
//! let k2 = kill.watcher();
//! let t2 = tokio::spawn(async move {
//! let duration = Duration::from_secs(2);
//! for _ in 0..5 {
//! tokio::time::sleep(duration).await;
//! println!("Kill switch on thread 2: {}", k2);
//! }
//! println!("Thread 2 wrapping up");
//! });
//!
//! let duration = Duration::from_secs(7);
//! tokio::time::sleep(duration).await;
//! println!("Flipping kill switch on main thread");
//! let _ = kill.kill();
//!
//! let _ = tokio::join!(t1, t2);
//!
//! println!("All threads finished");
//!# })
//! ```
use std::{
fmt::Display,
sync::{
atomic::{AtomicBool, Ordering::Relaxed},
Arc,
},
};
/// Convenience type which wraps a [`AtomicBool`].
/// Initially, `is_alive()` will return `true`. The value can be cloned across threads, and once it
/// has been `kill()`ed, then all of the clones will return `false` from `is_alive()`.
#[derive(Clone)]
pub struct KillSwitch {
switch: Arc<AtomicBool>,
}
/// Derived from a [`KillSwitch`], allows to check if the kill switch is still alive, but cannot
/// activate it. This may be useful in separating out a thread which is only watching the value of
/// the kill switch.
#[derive(Clone)]
pub struct KillSwitchWatcher {
switch: Arc<AtomicBool>,
}
impl KillSwitchWatcher {
/// Check if the kill switch has been flipped. Before flipping will return `true`, and
/// afterwards will return `false`
pub fn is_alive(&self) -> bool {
self.switch.load(Relaxed)
}
}
impl KillSwitch {
/// Check if the kill switch has been flipped. Before flipping will return `true`, and
/// afterwards will return `false`
pub fn is_alive(&self) -> bool {
self.switch.load(Relaxed)
}
/// Flip the kill switch (will cause `is_alive()` to return `false`
pub fn kill(&self) -> Result<(), KillSwitchErr> {
match self.is_alive() {
true => {
self.switch.store(false, Relaxed);
Ok(())
}
false => Err(KillSwitchErr::AlreadyKilled),
}
}
/// Produce a kill switch which can only watch the value, but cannot flip the switch
pub fn watcher(&self) -> KillSwitchWatcher {
KillSwitchWatcher {
switch: self.switch.clone(),
}
}
}
impl Default for KillSwitch {
fn default() -> Self {
Self {
switch: Arc::new(AtomicBool::new(true)),
}
}
}
impl Display for KillSwitchWatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.is_alive() {
true => "alive",
false => "killed",
}
)
}
}
impl Display for KillSwitch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.is_alive() {
true => "alive",
false => "killed",
}
)
}
}
/// General error type for a [`KillSwitch`]
#[derive(Debug, Clone)]
pub enum KillSwitchErr {
/// Kill switch has already been flipped
AlreadyKilled,
}
impl std::error::Error for KillSwitchErr {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl std::fmt::Display for KillSwitchErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
KillSwitchErr::AlreadyKilled => write!(f, "kill switch already killed"),
}
}
}