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
use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
thread::{self, Thread},
};
#[derive(Clone, Debug)]
pub struct Parker {
thread: Thread,
queued: Arc<AtomicBool>,
flag: Arc<AtomicBool>,
}
impl Parker {
pub fn current() -> Self {
let thread = thread::current();
let queued = Arc::new(AtomicBool::new(false));
let flag = Arc::new(AtomicBool::new(false));
Self {
thread,
queued,
flag,
}
}
pub fn park(&self) {
// Signal that we are going to `park`. Between this store and our `park`, there may
// be no other `park`, or else that `park` could consume our `unpark` token!
self.queued.store(true, Ordering::Release);
// We want to wait until the flag is set. We *could* just spin, but using
// park/unpark is more efficient.
while !self.flag.load(Ordering::Acquire) {
// We can *not* use `println!` here since that could use thread parking internally.
thread::park();
// We *could* get here spuriously.
// But that is no problem, we are in a loop until the flag is set anyway.
}
}
fn try_unpark(&self) -> bool {
// Ensure the thread is about to park.
// This is crucial! It guarantees that the `unpark` below is not consumed
// by some other code in the parked thread (e.g. inside `println!`).
if !self.queued.load(Ordering::Acquire) {
return false;
}
// Set the flag, and let the thread wake up.
// There is no race condition here: if `unpark`
// happens first, `park` will return immediately.
// There is also no other `park` that could consume this token,
// since we waited until the other thread got queued.
// Hence there is no risk of a deadlock.
self.flag.store(true, Ordering::Release);
self.thread.unpark();
true
}
pub fn unpark(&self) {
while !self.try_unpark() {
// Yielding is of course inefficient;
// since we run this at the end of the vCPU thread, though,
// we have nothing better to do.
thread::yield_now();
}
}
}