about summary refs log tree commit diff
path: root/library/std/src/sys/thread/xous.rs
diff options
context:
space:
mode:
authorjoboet <jonasboettiger@icloud.com>2025-08-29 18:01:02 +0200
committerjoboet <jonasboettiger@icloud.com>2025-09-08 17:02:25 +0200
commit4b15dd5a84742f9e7641b62e490765e0c1cb415c (patch)
tree323e992bcaee2813813a29d5d27c9a94304311cd /library/std/src/sys/thread/xous.rs
parentbeeb8e3af54295ba494c250e84ecda4c2c5d85ff (diff)
downloadrust-4b15dd5a84742f9e7641b62e490765e0c1cb415c.tar.gz
rust-4b15dd5a84742f9e7641b62e490765e0c1cb415c.zip
std: move `thread` into `sys` (rename only)
Diffstat (limited to 'library/std/src/sys/thread/xous.rs')
-rw-r--r--library/std/src/sys/thread/xous.rs155
1 files changed, 155 insertions, 0 deletions
diff --git a/library/std/src/sys/thread/xous.rs b/library/std/src/sys/thread/xous.rs
new file mode 100644
index 00000000000..92803c94c6e
--- /dev/null
+++ b/library/std/src/sys/thread/xous.rs
@@ -0,0 +1,155 @@
+use core::arch::asm;
+
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZero;
+use crate::os::xous::ffi::{
+    MemoryFlags, Syscall, ThreadId, blocking_scalar, create_thread, do_yield, join_thread,
+    map_memory, update_memory_flags,
+};
+use crate::os::xous::services::{TicktimerScalar, ticktimer_server};
+use crate::time::{Duration, Instant};
+
+pub struct Thread {
+    tid: ThreadId,
+}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 131072;
+const MIN_STACK_SIZE: usize = 4096;
+pub const GUARD_PAGE_SIZE: usize = 4096;
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(
+        stack: usize,
+        _name: Option<&str>,
+        p: Box<dyn FnOnce()>,
+    ) -> io::Result<Thread> {
+        let p = Box::into_raw(Box::new(p));
+        let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
+
+        if (stack_size & 4095) != 0 {
+            stack_size = (stack_size + 4095) & !4095;
+        }
+
+        // Allocate the whole thing, then divide it up after the fact. This ensures that
+        // even if there's a context switch during this function, the whole stack plus
+        // guard pages will remain contiguous.
+        let stack_plus_guard_pages: &mut [u8] = unsafe {
+            map_memory(
+                None,
+                None,
+                GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE,
+                MemoryFlags::R | MemoryFlags::W | MemoryFlags::X,
+            )
+        }
+        .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+        // No access to this page. Note: Write-only pages are illegal, and will
+        // cause an access violation.
+        unsafe {
+            update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W)
+                .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+        };
+
+        // No access to this page. Note: Write-only pages are illegal, and will
+        // cause an access violation.
+        unsafe {
+            update_memory_flags(
+                &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..],
+                MemoryFlags::W,
+            )
+            .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+        };
+
+        let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize;
+        let tid = create_thread(
+            thread_start as *mut usize,
+            &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)],
+            p as usize,
+            guard_page_pre,
+            stack_size,
+            0,
+        )
+        .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+        extern "C" fn thread_start(
+            main: *mut usize,
+            guard_page_pre: usize,
+            stack_size: usize,
+        ) -> ! {
+            unsafe {
+                // Run the contents of the new thread.
+                Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+            }
+
+            // Destroy TLS, which will free the TLS page and call the destructor for
+            // any thread local storage (if any).
+            unsafe {
+                crate::sys::thread_local::key::destroy_tls();
+            }
+
+            // Deallocate the stack memory, along with the guard pages. Afterwards,
+            // exit the thread by returning to the magic address 0xff80_3000usize,
+            // which tells the kernel to deallocate this thread.
+            let mapped_memory_base = guard_page_pre;
+            let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE;
+            unsafe {
+                asm!(
+                    "ecall",
+                    "ret",
+                                        in("a0") Syscall::UnmapMemory as usize,
+                                        in("a1") mapped_memory_base,
+                                        in("a2") mapped_memory_length,
+                                        in("ra") 0xff80_3000usize,
+                                        options(nomem, nostack, noreturn)
+                );
+            }
+        }
+
+        Ok(Thread { tid })
+    }
+
+    pub fn yield_now() {
+        do_yield();
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(dur: Duration) {
+        // Because the sleep server works on units of `usized milliseconds`, split
+        // the messages up into these chunks. This means we may run into issues
+        // if you try to sleep a thread for more than 49 days on a 32-bit system.
+        let mut millis = dur.as_millis();
+        while millis > 0 {
+            let sleep_duration =
+                if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
+            blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
+                .expect("failed to send message to ticktimer server");
+            millis -= sleep_duration as u128;
+        }
+    }
+
+    pub fn sleep_until(deadline: Instant) {
+        let now = Instant::now();
+
+        if let Some(delay) = deadline.checked_duration_since(now) {
+            Self::sleep(delay);
+        }
+    }
+
+    pub fn join(self) {
+        join_thread(self.tid).unwrap();
+    }
+}
+
+pub(crate) fn current_os_id() -> Option<u64> {
+    None
+}
+
+pub fn available_parallelism() -> io::Result<NonZero<usize>> {
+    // We're unicore right now.
+    Ok(unsafe { NonZero::new_unchecked(1) })
+}