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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
use crate::os::xous::ffi::{blocking_scalar, scalar};
use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
use crate::pin::Pin;
use crate::ptr;
use crate::sync::atomic::Ordering::{Acquire, Release};
use crate::sync::atomic::{Atomic, AtomicI8};
use crate::time::Duration;
const NOTIFIED: i8 = 1;
const EMPTY: i8 = 0;
const PARKED: i8 = -1;
pub struct Parker {
state: Atomic<i8>,
}
impl Parker {
pub unsafe fn new_in_place(parker: *mut Parker) {
unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) }
}
fn index(&self) -> usize {
ptr::from_ref(self).addr()
}
pub unsafe fn park(self: Pin<&Self>) {
// Change NOTIFIED to EMPTY and EMPTY to PARKED.
let state = self.state.fetch_sub(1, Acquire);
if state == NOTIFIED {
// The state has gone from NOTIFIED (1) to EMPTY (0)
return;
}
// The state has gone from EMPTY (0) to PARKED (-1)
assert!(state == EMPTY);
// The state is now PARKED (-1). Wait until the `unpark` wakes us up.
blocking_scalar(
ticktimer_server(),
TicktimerScalar::WaitForCondition(self.index(), 0).into(),
)
.expect("failed to send WaitForCondition command");
let state = self.state.swap(EMPTY, Acquire);
assert!(state == NOTIFIED || state == PARKED);
}
pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
// Change NOTIFIED to EMPTY and EMPTY to PARKED.
let state = self.state.fetch_sub(1, Acquire);
if state == NOTIFIED {
// The state has gone from NOTIFIED (1) to EMPTY (0)
return;
}
// The state has gone from EMPTY (0) to PARKED (-1)
assert!(state == EMPTY);
// A value of zero indicates an indefinite wait. Clamp the number of
// milliseconds to the allowed range.
let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1);
// The state is now PARKED (-1). Wait until the `unpark` wakes us up,
// or things time out.
let _was_timeout = blocking_scalar(
ticktimer_server(),
TicktimerScalar::WaitForCondition(self.index(), millis).into(),
)
.expect("failed to send WaitForCondition command")[0]
!= 0;
let state = self.state.swap(EMPTY, Acquire);
assert!(state == PARKED || state == NOTIFIED);
}
pub fn unpark(self: Pin<&Self>) {
// If the state is already `NOTIFIED`, then another thread has
// indicated it wants to wake up the target thread.
//
// If the state is `EMPTY` then there is nothing to wake up, and
// the target thread will immediately exit from `park()` the
// next time that function is called.
if self.state.swap(NOTIFIED, Release) != PARKED {
return;
}
// The thread is parked, wake it up. Keep trying until we wake something up.
// This will happen when the `NotifyCondition` call returns the fact that
// 1 condition was notified.
// Alternately, keep going until the state is seen as `EMPTY`, indicating
// the thread woke up and kept going. This can happen when the Park
// times out before we can send the NotifyCondition message.
while blocking_scalar(
ticktimer_server(),
TicktimerScalar::NotifyCondition(self.index(), 1).into(),
)
.expect("failed to send NotifyCondition command")[0]
== 0
&& self.state.load(Acquire) != EMPTY
{
// The target thread hasn't yet hit the `WaitForCondition` call.
// Yield to let the target thread run some more.
crate::thread::yield_now();
}
}
}
impl Drop for Parker {
fn drop(&mut self) {
scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok();
}
}
|