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}