]> git.proxmox.com Git - rustc.git/blob - src/vendor/rustc-rayon-core/src/latch.rs
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / vendor / rustc-rayon-core / src / latch.rs
1 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
2 use std::sync::{Mutex, Condvar};
3 use std::usize;
4
5 use sleep::Sleep;
6
7 /// We define various kinds of latches, which are all a primitive signaling
8 /// mechanism. A latch starts as false. Eventually someone calls `set()` and
9 /// it becomes true. You can test if it has been set by calling `probe()`.
10 ///
11 /// Some kinds of latches, but not all, support a `wait()` operation
12 /// that will wait until the latch is set, blocking efficiently. That
13 /// is not part of the trait since it is not possibly to do with all
14 /// latches.
15 ///
16 /// The intention is that `set()` is called once, but `probe()` may be
17 /// called any number of times. Once `probe()` returns true, the memory
18 /// effects that occurred before `set()` become visible.
19 ///
20 /// It'd probably be better to refactor the API into two paired types,
21 /// but that's a bit of work, and this is not a public API.
22 ///
23 /// ## Memory ordering
24 ///
25 /// Latches need to guarantee two things:
26 ///
27 /// - Once `probe()` returns true, all memory effects from the `set()`
28 /// are visible (in other words, the set should synchronize-with
29 /// the probe).
30 /// - Once `set()` occurs, the next `probe()` *will* observe it. This
31 /// typically requires a seq-cst ordering. See [the "tickle-then-get-sleepy" scenario in the sleep
32 /// README](/src/sleep/README.md#tickle-then-get-sleepy) for details.
33 pub trait Latch: LatchProbe {
34 /// Set the latch, signalling others.
35 fn set(&self);
36 }
37
38 pub trait LatchProbe {
39 /// Test if the latch is set.
40 fn probe(&self) -> bool;
41 }
42
43 /// Spin latches are the simplest, most efficient kind, but they do
44 /// not support a `wait()` operation. They just have a boolean flag
45 /// that becomes true when `set()` is called.
46 pub struct SpinLatch {
47 b: AtomicBool,
48 }
49
50 impl SpinLatch {
51 #[inline]
52 pub fn new() -> SpinLatch {
53 SpinLatch { b: AtomicBool::new(false) }
54 }
55 }
56
57 impl LatchProbe for SpinLatch {
58 #[inline]
59 fn probe(&self) -> bool {
60 self.b.load(Ordering::SeqCst)
61 }
62 }
63
64 impl Latch for SpinLatch {
65 #[inline]
66 fn set(&self) {
67 self.b.store(true, Ordering::SeqCst);
68 }
69 }
70
71 /// A Latch starts as false and eventually becomes true. You can block
72 /// until it becomes true.
73 pub struct LockLatch {
74 m: Mutex<bool>,
75 v: Condvar,
76 }
77
78 impl LockLatch {
79 #[inline]
80 pub fn new() -> LockLatch {
81 LockLatch {
82 m: Mutex::new(false),
83 v: Condvar::new(),
84 }
85 }
86
87 /// Block until latch is set.
88 pub fn wait(&self) {
89 let mut guard = self.m.lock().unwrap();
90 while !*guard {
91 guard = self.v.wait(guard).unwrap();
92 }
93 }
94 }
95
96 impl LatchProbe for LockLatch {
97 #[inline]
98 fn probe(&self) -> bool {
99 // Not particularly efficient, but we don't really use this operation
100 let guard = self.m.lock().unwrap();
101 *guard
102 }
103 }
104
105 impl Latch for LockLatch {
106 #[inline]
107 fn set(&self) {
108 let mut guard = self.m.lock().unwrap();
109 *guard = true;
110 self.v.notify_all();
111 }
112 }
113
114 /// Counting latches are used to implement scopes. They track a
115 /// counter. Unlike other latches, calling `set()` does not
116 /// necessarily make the latch be considered `set()`; instead, it just
117 /// decrements the counter. The latch is only "set" (in the sense that
118 /// `probe()` returns true) once the counter reaches zero.
119 #[derive(Debug)]
120 pub struct CountLatch {
121 counter: AtomicUsize,
122 }
123
124 impl CountLatch {
125 #[inline]
126 pub fn new() -> CountLatch {
127 CountLatch { counter: AtomicUsize::new(1) }
128 }
129
130 #[inline]
131 pub fn increment(&self) {
132 debug_assert!(!self.probe());
133 self.counter.fetch_add(1, Ordering::Relaxed);
134 }
135 }
136
137 impl LatchProbe for CountLatch {
138 #[inline]
139 fn probe(&self) -> bool {
140 // Need to acquire any memory reads before latch was set:
141 self.counter.load(Ordering::SeqCst) == 0
142 }
143 }
144
145 impl Latch for CountLatch {
146 /// Set the latch to true, releasing all threads who are waiting.
147 #[inline]
148 fn set(&self) {
149 self.counter.fetch_sub(1, Ordering::SeqCst);
150 }
151 }
152
153
154 /// A tickling latch wraps another latch type, and will also awaken a thread
155 /// pool when it is set. This is useful for jobs injected between thread pools,
156 /// so the source pool can continue processing its own work while waiting.
157 pub struct TickleLatch<'a, L: Latch> {
158 inner: L,
159 sleep: &'a Sleep,
160 }
161
162 impl<'a, L: Latch> TickleLatch<'a, L> {
163 #[inline]
164 pub fn new(latch: L, sleep: &'a Sleep) -> Self {
165 TickleLatch {
166 inner: latch,
167 sleep: sleep,
168 }
169 }
170 }
171
172 impl<'a, L: Latch> LatchProbe for TickleLatch<'a, L> {
173 #[inline]
174 fn probe(&self) -> bool {
175 self.inner.probe()
176 }
177 }
178
179 impl<'a, L: Latch> Latch for TickleLatch<'a, L> {
180 #[inline]
181 fn set(&self) {
182 self.inner.set();
183 self.sleep.tickle(usize::MAX);
184 }
185 }