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
160
161
//! Library to control thread execution.
//!
//! Usage example:
//!
//! ```rust
//! use std::thread;
//! use thread_control::*;
//!
//! fn main() {
//!     let (flag, control) = make_pair();
//!     let handle = thread::spawn(move || {
//!         while flag.alive() {
//!         }
//!     });
//!     assert_eq!(control.is_done(), false);
//!     control.stop();
//!     handle.join();
//!     assert_eq!(control.is_interrupted(), false);
//!     assert_eq!(control.is_done(), true);
//! }
//! ```
//!
//! Interrupt example:
//!
//! ```rust
//! use std::thread;
//! use thread_control::*;
//!
//! fn main() {
//!     let (flag, control) = make_pair();
//!     let handle = thread::spawn(move || {
//!         while flag.alive() {
//!         }
//!     });
//!     control.interrupt();
//!     handle.join();
//!     assert_eq!(control.is_interrupted(), true);
//!     assert_eq!(control.is_done(), true);
//! }
//! ```
//!
//! Panics example:
//!
//! ```rust
//! use std::thread;
//! use thread_control::*;
//!
//! fn main() {
//!     let (flag, control) = make_pair();
//!     let handle = thread::spawn(move || {
//!         while flag.alive() {
//!             panic!("PANIC!");
//!         }
//!     });
//!     handle.join();
//!     assert_eq!(control.is_interrupted(), true);
//!     assert_eq!(control.is_done(), true);
//! }
//! ```
//!

use std::thread;
use std::sync::{Arc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};

/// Struct to check execution status of spawned thread.
#[derive(Debug)]
pub struct Flag {
    alive: Arc<AtomicBool>,
    interrupt: Arc<AtomicBool>,
}

impl Drop for Flag {
    fn drop(&mut self) {
        if thread::panicking() {
            (*self.interrupt).store(true, Ordering::Relaxed)
        }
    }
}

impl Flag {

    /// Creates new flag.
    pub fn new() -> Self {
        Flag {
            alive: Arc::new(AtomicBool::new(true)),
            interrupt: Arc::new(AtomicBool::new(false)),
        }
    }

    /// Creates new `Control` to control this flag.
    pub fn take_control(&self) -> Control {
        Control {
            alive: Arc::downgrade(&self.alive),
            interrupt: self.interrupt.clone(),
        }
    }

    /// Check the flag isn't stopped or interrupted.
    ///
    /// # Panics
    ///
    /// This method panics, if interrupt flag was set.
    pub fn alive(&self) -> bool {
        if (*self.interrupt).load(Ordering::Relaxed) {
            panic!("thread interrupted by thread-contol");
        }
        (*self.alive).load(Ordering::Relaxed)
    }

    /// Check the flag is not stopped and not interrupted
    /// Use it if panic is not desirable behavior
    pub fn is_alive(&self) -> bool {
        (*self.alive).load(Ordering::Relaxed) && !(*self.interrupt).load(Ordering::Relaxed)
    }

    /// Set interrupt flag and drop the instance
    pub fn interrupt(self) {
        (self.interrupt).store(true, Ordering::Relaxed)
    }
}

/// Struct to control thread execution.
#[derive(Debug, Clone)]
pub struct Control {
    alive: Weak<AtomicBool>,
    interrupt: Arc<AtomicBool>,
}

impl Control {
    /// Interrupt execution of thread.
    /// Actually it panics when thread checking flag.
    pub fn interrupt(&self) {
        (*self.interrupt).store(true, Ordering::Relaxed)
    }

    /// Set stop flag.
    pub fn stop(&self) {
        self.alive.upgrade().map(|flag| {
            (*flag).store(false, Ordering::Relaxed)
        });
    }

    /// Return `true` if thread ended.
    pub fn is_done(&self) -> bool {
        self.alive.upgrade().is_none()
    }

    /// Return `true` if thread was interrupted or panicked.
    pub fn is_interrupted(&self) -> bool {
        (*self.interrupt).load(Ordering::Relaxed)
    }
}

/// Makes pair with connected flag and control.
pub fn make_pair() -> (Flag, Control) {
    let flag = Flag::new();
    let control = flag.take_control();
    (flag, control)
}