fast_stm/transaction/
control_block.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2use std::thread::{self, Thread};
3use std::time::Duration;
4
5#[cfg(test)]
6use super::super::test::{terminates, terminates_async};
7
8/// A control block for a currently running STM instance.
9///
10/// STM blocks on all read variables if retry was called.
11/// This control block is used to let the vars inform the STM instance.
12///
13/// Be careful when using this directly,
14/// because you can easily create deadlocks.
15pub struct ControlBlock {
16    /// This is the handle to the thread, that waits on the control block.
17    thread: Thread,
18
19    /// Atomic bool stores if the thread has been blocked yet.
20    /// Make sure, that park is repeated if no change has happened.
21    blocked: AtomicBool,
22
23    /// Upper bound on time a thread is parked. This prevents a possible deadlock.
24    max_parked_time: Duration,
25}
26
27impl Default for ControlBlock {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl ControlBlock {
34    /// Create a new `StmControlBlock`.
35    pub fn new() -> ControlBlock {
36        ControlBlock {
37            thread: thread::current(),
38            blocked: AtomicBool::new(true),
39            max_parked_time: Duration::from_millis(1000),
40        }
41    }
42
43    /// Inform the control block that a variable has changed.
44    ///
45    /// Need to be called from outside of STM.
46    pub fn set_changed(&self) {
47        // Only wakeup once.
48        if self.blocked.swap(false, Ordering::SeqCst) {
49            // wake thread
50            self.thread.unpark();
51        }
52    }
53
54    /// Block until one variable has changed.
55    ///
56    /// `wait` may immediately return.
57    ///
58    /// `wait` needs to be called by the STM instance itself.
59    pub fn wait(&self) {
60        while self.blocked.load(Ordering::SeqCst) {
61            /* // original code:
62            thread::park();
63            */
64
65            // deadlock-safe code:
66            thread::park_timeout(self.max_parked_time);
67        }
68    }
69}
70
71// TESTS
72#[cfg(test)]
73mod test {
74    use super::*;
75
76    /// Test if `ControlBlock` correctly blocks on `wait`.
77    #[test]
78    fn blocked() {
79        let ctrl = ControlBlock::new();
80        // waiting should immediately finish
81        assert!(!terminates(100, move || ctrl.wait()));
82    }
83
84    /// A `ControlBlock` does immediately return,
85    /// when it was set to changed before calling waiting.
86    ///
87    /// This scenario may occur, when a variable changes, while the
88    /// transaction has not yet blocked.
89    #[test]
90    fn wait_after_change() {
91        let ctrl = ControlBlock::new();
92        // set to changed
93        ctrl.set_changed();
94        // waiting should immediately finish
95        assert!(terminates(50, move || ctrl.wait()));
96    }
97
98    /// Test calling `set_changed` multiple times.
99    #[test]
100    fn wait_after_multiple_changes() {
101        let ctrl = ControlBlock::new();
102        // set to changed
103        ctrl.set_changed();
104        ctrl.set_changed();
105        ctrl.set_changed();
106        ctrl.set_changed();
107
108        // waiting should immediately finish
109        assert!(terminates(50, move || ctrl.wait()));
110    }
111
112    /// Perform a wakeup from another thread.
113    #[test]
114    fn wait_threaded_wakeup() {
115        use std::sync::Arc;
116
117        let ctrl = Arc::new(ControlBlock::new());
118        let ctrl2 = ctrl.clone();
119        let terminated = terminates_async(500, move || ctrl.wait(), move || ctrl2.set_changed());
120
121        assert!(terminated);
122    }
123}