Skip to content

Commit

Permalink
std: implement thread parking for xous
Browse files Browse the repository at this point in the history
  • Loading branch information
joboet committed Oct 18, 2023
1 parent 631a116 commit 03301f2
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@
#![feature(portable_simd)]
#![feature(prelude_2024)]
#![feature(ptr_as_uninit)]
#![feature(ptr_from_ref)]
#![feature(raw_os_nonzero)]
#![feature(round_ties_even)]
#![feature(slice_internals)]
Expand Down
1 change: 0 additions & 1 deletion library/std/src/sys/xous/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ pub mod process;
pub mod stdio;
pub mod thread;
pub mod thread_local_key;
#[path = "../unsupported/thread_parking.rs"]
pub mod thread_parking;
pub mod time;

Expand Down
88 changes: 88 additions & 0 deletions library/std/src/sys/xous/thread_parking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::os::xous::ffi::blocking_scalar;
use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
use crate::pin::Pin;
use crate::ptr;
use crate::sync::atomic::{
AtomicI8,
Ordering::{Acquire, Release},
};
use crate::time::Duration;

const NOTIFIED: i8 = 1;
const EMPTY: i8 = 0;
const PARKED: i8 = -1;

pub struct Parker {
state: AtomicI8,
}

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 {
return;
}

// The state was set to PARKED. Wait until the `unpark` wakes us up.
blocking_scalar(
ticktimer_server(),
TicktimerScalar::WaitForCondition(self.index(), 0).into(),
)
.expect("failed to send WaitForCondition command");

self.state.swap(EMPTY, Acquire);
}

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 {
return;
}

// 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);

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);
if was_timeout && state == NOTIFIED {
// The state was set to NOTIFIED after we returned from the wait
// but before we reset the state. Therefore, a wakeup is on its
// way, which we need to consume here.
// NOTICE: this is a priority hole.
blocking_scalar(
ticktimer_server(),
TicktimerScalar::WaitForCondition(self.index(), 0).into(),
)
.expect("failed to send WaitForCondition command");
}
}

pub fn unpark(self: Pin<&Self>) {
let state = self.state.swap(NOTIFIED, Release);
if state == PARKED {
// The thread is parked, wake it up.
blocking_scalar(
ticktimer_server(),
TicktimerScalar::NotifyCondition(self.index(), 1).into(),
)
.expect("failed to send NotifyCondition command");
}
}
}

0 comments on commit 03301f2

Please sign in to comment.