about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-11-29 04:23:21 +0100
committerGitHub <noreply@github.com>2023-11-29 04:23:21 +0100
commit2eec51c27cae2df56fd5cb2085e60e18f7e68940 (patch)
treec9279c107e8f1b5ddee59a7415082aea39970de1
parentb1e56deadaf4b08cd591feaa1a1283348298427d (diff)
parent2dc6ba27b5bbb4b5aca064d007b8305636422c5d (diff)
downloadrust-2eec51c27cae2df56fd5cb2085e60e18f7e68940.tar.gz
rust-2eec51c27cae2df56fd5cb2085e60e18f7e68940.zip
Rollup merge of #116839 - joboet:xous_thread_parking, r=m-ou-se
Implement thread parking for xous

This follows the pattern set by [the Windows parker](https://github.com/rust-lang/rust/blob/ddef56d5dfa18f169af9db912dc8e8343797eebb/library/std/src/sys/windows/thread_parking.rs) when it uses keyed events. An atomic variable is used to track the state and optimize the fast path, while notifications are send via the ticktime server to block and unblock the thread.

ping `@xobs`
`@rustbot` label +T-libs +A-atomic
r? libs
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/sys/xous/mod.rs1
-rw-r--r--library/std/src/sys/xous/thread_parking.rs94
3 files changed, 95 insertions, 1 deletions
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index d06012c14dc..8dc5b07ce10 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -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)]
diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs
index 6d5c218d195..c2550dcfd83 100644
--- a/library/std/src/sys/xous/mod.rs
+++ b/library/std/src/sys/xous/mod.rs
@@ -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;
 
diff --git a/library/std/src/sys/xous/thread_parking.rs b/library/std/src/sys/xous/thread_parking.rs
new file mode 100644
index 00000000000..aa39c6d2718
--- /dev/null
+++ b/library/std/src/sys/xous/thread_parking.rs
@@ -0,0 +1,94 @@
+use crate::os::xous::ffi::{blocking_scalar, 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");
+        }
+    }
+}
+
+impl Drop for Parker {
+    fn drop(&mut self) {
+        scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok();
+    }
+}