]> git.proxmox.com Git - rustc.git/blob - vendor/rustc-rayon-core/src/latch.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / vendor / rustc-rayon-core / src / latch.rs
1 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
2 use std::sync::{Condvar, Mutex};
3 use std::usize;
4
5 use crate::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(super) trait Latch: LatchProbe {
34 /// Set the latch, signalling others.
35 fn set(&self);
36 }
37
38 pub(super) 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(super) struct SpinLatch {
47 b: AtomicBool,
48 }
49
50 impl SpinLatch {
51 #[inline]
52 pub(super) fn new() -> SpinLatch {
53 SpinLatch {
54 b: AtomicBool::new(false),
55 }
56 }
57 }
58
59 impl LatchProbe for SpinLatch {
60 #[inline]
61 fn probe(&self) -> bool {
62 self.b.load(Ordering::SeqCst)
63 }
64 }
65
66 impl Latch for SpinLatch {
67 #[inline]
68 fn set(&self) {
69 self.b.store(true, Ordering::SeqCst);
70 }
71 }
72
73 /// A Latch starts as false and eventually becomes true. You can block
74 /// until it becomes true.
75 pub(super) struct LockLatch {
76 m: Mutex<bool>,
77 v: Condvar,
78 }
79
80 impl LockLatch {
81 #[inline]
82 pub(super) fn new() -> LockLatch {
83 LockLatch {
84 m: Mutex::new(false),
85 v: Condvar::new(),
86 }
87 }
88
89 /// Block until latch is set, then resets this lock latch so it can be reused again.
90 pub(super) fn wait_and_reset(&self) {
91 let mut guard = self.m.lock().unwrap();
92 while !*guard {
93 guard = self.v.wait(guard).unwrap();
94 }
95 *guard = false;
96 }
97
98 /// Block until latch is set.
99 pub(super) fn wait(&self) {
100 let mut guard = self.m.lock().unwrap();
101 while !*guard {
102 guard = self.v.wait(guard).unwrap();
103 }
104 }
105 }
106
107 impl LatchProbe for LockLatch {
108 #[inline]
109 fn probe(&self) -> bool {
110 // Not particularly efficient, but we don't really use this operation
111 let guard = self.m.lock().unwrap();
112 *guard
113 }
114 }
115
116 impl Latch for LockLatch {
117 #[inline]
118 fn set(&self) {
119 let mut guard = self.m.lock().unwrap();
120 *guard = true;
121 self.v.notify_all();
122 }
123 }
124
125 /// Counting latches are used to implement scopes. They track a
126 /// counter. Unlike other latches, calling `set()` does not
127 /// necessarily make the latch be considered `set()`; instead, it just
128 /// decrements the counter. The latch is only "set" (in the sense that
129 /// `probe()` returns true) once the counter reaches zero.
130 #[derive(Debug)]
131 pub(super) struct CountLatch {
132 counter: AtomicUsize,
133 }
134
135 impl CountLatch {
136 #[inline]
137 pub(super) fn new() -> CountLatch {
138 CountLatch {
139 counter: AtomicUsize::new(1),
140 }
141 }
142
143 #[inline]
144 pub(super) fn increment(&self) {
145 debug_assert!(!self.probe());
146 self.counter.fetch_add(1, Ordering::Relaxed);
147 }
148 }
149
150 impl LatchProbe for CountLatch {
151 #[inline]
152 fn probe(&self) -> bool {
153 // Need to acquire any memory reads before latch was set:
154 self.counter.load(Ordering::SeqCst) == 0
155 }
156 }
157
158 impl Latch for CountLatch {
159 /// Set the latch to true, releasing all threads who are waiting.
160 #[inline]
161 fn set(&self) {
162 self.counter.fetch_sub(1, Ordering::SeqCst);
163 }
164 }
165
166 /// A tickling latch wraps another latch type, and will also awaken a thread
167 /// pool when it is set. This is useful for jobs injected between thread pools,
168 /// so the source pool can continue processing its own work while waiting.
169 pub(super) struct TickleLatch<'a, L: Latch> {
170 inner: L,
171 sleep: &'a Sleep,
172 }
173
174 impl<'a, L: Latch> TickleLatch<'a, L> {
175 #[inline]
176 pub(super) fn new(latch: L, sleep: &'a Sleep) -> Self {
177 TickleLatch {
178 inner: latch,
179 sleep,
180 }
181 }
182 }
183
184 impl<'a, L: Latch> LatchProbe for TickleLatch<'a, L> {
185 #[inline]
186 fn probe(&self) -> bool {
187 self.inner.probe()
188 }
189 }
190
191 impl<'a, L: Latch> Latch for TickleLatch<'a, L> {
192 #[inline]
193 fn set(&self) {
194 self.inner.set();
195 self.sleep.tickle(usize::MAX);
196 }
197 }
198
199 impl<'a, L> LatchProbe for &'a L
200 where
201 L: LatchProbe,
202 {
203 fn probe(&self) -> bool {
204 L::probe(self)
205 }
206 }
207
208 impl<'a, L> Latch for &'a L
209 where
210 L: Latch,
211 {
212 fn set(&self) {
213 L::set(self);
214 }
215 }