Skip to content

Commit 03301f2

Browse files
committed
std: implement thread parking for xous
1 parent 631a116 commit 03301f2

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@
336336
#![feature(portable_simd)]
337337
#![feature(prelude_2024)]
338338
#![feature(ptr_as_uninit)]
339+
#![feature(ptr_from_ref)]
339340
#![feature(raw_os_nonzero)]
340341
#![feature(round_ties_even)]
341342
#![feature(slice_internals)]

library/std/src/sys/xous/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ pub mod process;
2828
pub mod stdio;
2929
pub mod thread;
3030
pub mod thread_local_key;
31-
#[path = "../unsupported/thread_parking.rs"]
3231
pub mod thread_parking;
3332
pub mod time;
3433

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use crate::os::xous::ffi::blocking_scalar;
2+
use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
3+
use crate::pin::Pin;
4+
use crate::ptr;
5+
use crate::sync::atomic::{
6+
AtomicI8,
7+
Ordering::{Acquire, Release},
8+
};
9+
use crate::time::Duration;
10+
11+
const NOTIFIED: i8 = 1;
12+
const EMPTY: i8 = 0;
13+
const PARKED: i8 = -1;
14+
15+
pub struct Parker {
16+
state: AtomicI8,
17+
}
18+
19+
impl Parker {
20+
pub unsafe fn new_in_place(parker: *mut Parker) {
21+
unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) }
22+
}
23+
24+
fn index(&self) -> usize {
25+
ptr::from_ref(self).addr()
26+
}
27+
28+
pub unsafe fn park(self: Pin<&Self>) {
29+
// Change NOTIFIED to EMPTY and EMPTY to PARKED.
30+
let state = self.state.fetch_sub(1, Acquire);
31+
if state == NOTIFIED {
32+
return;
33+
}
34+
35+
// The state was set to PARKED. Wait until the `unpark` wakes us up.
36+
blocking_scalar(
37+
ticktimer_server(),
38+
TicktimerScalar::WaitForCondition(self.index(), 0).into(),
39+
)
40+
.expect("failed to send WaitForCondition command");
41+
42+
self.state.swap(EMPTY, Acquire);
43+
}
44+
45+
pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
46+
// Change NOTIFIED to EMPTY and EMPTY to PARKED.
47+
let state = self.state.fetch_sub(1, Acquire);
48+
if state == NOTIFIED {
49+
return;
50+
}
51+
52+
// A value of zero indicates an indefinite wait. Clamp the number of
53+
// milliseconds to the allowed range.
54+
let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1);
55+
56+
let was_timeout = blocking_scalar(
57+
ticktimer_server(),
58+
TicktimerScalar::WaitForCondition(self.index(), millis).into(),
59+
)
60+
.expect("failed to send WaitForCondition command")[0]
61+
!= 0;
62+
63+
let state = self.state.swap(EMPTY, Acquire);
64+
if was_timeout && state == NOTIFIED {
65+
// The state was set to NOTIFIED after we returned from the wait
66+
// but before we reset the state. Therefore, a wakeup is on its
67+
// way, which we need to consume here.
68+
// NOTICE: this is a priority hole.
69+
blocking_scalar(
70+
ticktimer_server(),
71+
TicktimerScalar::WaitForCondition(self.index(), 0).into(),
72+
)
73+
.expect("failed to send WaitForCondition command");
74+
}
75+
}
76+
77+
pub fn unpark(self: Pin<&Self>) {
78+
let state = self.state.swap(NOTIFIED, Release);
79+
if state == PARKED {
80+
// The thread is parked, wake it up.
81+
blocking_scalar(
82+
ticktimer_server(),
83+
TicktimerScalar::NotifyCondition(self.index(), 1).into(),
84+
)
85+
.expect("failed to send NotifyCondition command");
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)