thread_control/lib.rs
1//! Library to control thread execution.
2//!
3//! Usage example:
4//!
5//! ```rust
6//! use std::thread;
7//! use thread_control::*;
8//!
9//! fn main() {
10//! let (flag, control) = make_pair();
11//! let handle = thread::spawn(move || {
12//! while flag.alive() {
13//! }
14//! });
15//! assert_eq!(control.is_done(), false);
16//! control.stop();
17//! handle.join();
18//! assert_eq!(control.is_interrupted(), false);
19//! assert_eq!(control.is_done(), true);
20//! }
21//! ```
22//!
23//! Interrupt example:
24//!
25//! ```rust
26//! use std::thread;
27//! use thread_control::*;
28//!
29//! fn main() {
30//! let (flag, control) = make_pair();
31//! let handle = thread::spawn(move || {
32//! while flag.alive() {
33//! }
34//! });
35//! control.interrupt();
36//! handle.join();
37//! assert_eq!(control.is_interrupted(), true);
38//! assert_eq!(control.is_done(), true);
39//! }
40//! ```
41//!
42//! Panics example:
43//!
44//! ```rust
45//! use std::thread;
46//! use thread_control::*;
47//!
48//! fn main() {
49//! let (flag, control) = make_pair();
50//! let handle = thread::spawn(move || {
51//! while flag.alive() {
52//! panic!("PANIC!");
53//! }
54//! });
55//! handle.join();
56//! assert_eq!(control.is_interrupted(), true);
57//! assert_eq!(control.is_done(), true);
58//! }
59//! ```
60//!
61
62use std::thread;
63use std::sync::{Arc, Weak};
64use std::sync::atomic::{AtomicBool, Ordering};
65
66/// Struct to check execution status of spawned thread.
67#[derive(Debug)]
68pub struct Flag {
69 alive: Arc<AtomicBool>,
70 interrupt: Arc<AtomicBool>,
71}
72
73impl Drop for Flag {
74 fn drop(&mut self) {
75 if thread::panicking() {
76 (*self.interrupt).store(true, Ordering::Relaxed)
77 }
78 }
79}
80
81impl Flag {
82
83 /// Creates new flag.
84 pub fn new() -> Self {
85 Flag {
86 alive: Arc::new(AtomicBool::new(true)),
87 interrupt: Arc::new(AtomicBool::new(false)),
88 }
89 }
90
91 /// Creates new `Control` to control this flag.
92 pub fn take_control(&self) -> Control {
93 Control {
94 alive: Arc::downgrade(&self.alive),
95 interrupt: self.interrupt.clone(),
96 }
97 }
98
99 /// Check the flag isn't stopped or interrupted.
100 ///
101 /// # Panics
102 ///
103 /// This method panics, if interrupt flag was set.
104 pub fn alive(&self) -> bool {
105 if (*self.interrupt).load(Ordering::Relaxed) {
106 panic!("thread interrupted by thread-contol");
107 }
108 (*self.alive).load(Ordering::Relaxed)
109 }
110
111 /// Check the flag is not stopped and not interrupted
112 /// Use it if panic is not desirable behavior
113 pub fn is_alive(&self) -> bool {
114 (*self.alive).load(Ordering::Relaxed) && !(*self.interrupt).load(Ordering::Relaxed)
115 }
116
117 /// Set interrupt flag and drop the instance
118 pub fn interrupt(self) {
119 (self.interrupt).store(true, Ordering::Relaxed)
120 }
121}
122
123/// Struct to control thread execution.
124#[derive(Debug, Clone)]
125pub struct Control {
126 alive: Weak<AtomicBool>,
127 interrupt: Arc<AtomicBool>,
128}
129
130impl Control {
131 /// Interrupt execution of thread.
132 /// Actually it panics when thread checking flag.
133 pub fn interrupt(&self) {
134 (*self.interrupt).store(true, Ordering::Relaxed)
135 }
136
137 /// Set stop flag.
138 pub fn stop(&self) {
139 self.alive.upgrade().map(|flag| {
140 (*flag).store(false, Ordering::Relaxed)
141 });
142 }
143
144 /// Return `true` if thread ended.
145 pub fn is_done(&self) -> bool {
146 self.alive.upgrade().is_none()
147 }
148
149 /// Return `true` if thread was interrupted or panicked.
150 pub fn is_interrupted(&self) -> bool {
151 (*self.interrupt).load(Ordering::Relaxed)
152 }
153}
154
155/// Makes pair with connected flag and control.
156pub fn make_pair() -> (Flag, Control) {
157 let flag = Flag::new();
158 let control = flag.take_control();
159 (flag, control)
160}
161