Skip to content

Commit d36e516

Browse files
committed
std: xous: add thread support
Add initial support for threads on Xous. This includes thread creation and joining. Signed-off-by: Sean Cross <[email protected]>
1 parent efa470d commit d36e516

File tree

2 files changed

+144
-1
lines changed

2 files changed

+144
-1
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ pub mod pipe;
2727
#[path = "../unsupported/process.rs"]
2828
pub mod process;
2929
pub mod stdio;
30-
#[path = "../unsupported/thread.rs"]
3130
pub mod thread;
3231
#[path = "../unsupported/thread_local_key.rs"]
3332
pub mod thread_local_key;

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

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use crate::ffi::CStr;
2+
use crate::io;
3+
use crate::num::NonZeroUsize;
4+
use crate::os::xous::ffi::{
5+
blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags,
6+
MemoryFlags, Syscall, ThreadId,
7+
};
8+
use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
9+
use crate::time::Duration;
10+
use core::arch::asm;
11+
12+
pub struct Thread {
13+
tid: ThreadId,
14+
}
15+
16+
pub const DEFAULT_MIN_STACK_SIZE: usize = 131072;
17+
const MIN_STACK_SIZE: usize = 4096;
18+
pub const GUARD_PAGE_SIZE: usize = 4096;
19+
20+
impl Thread {
21+
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
22+
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
23+
let p = Box::into_raw(Box::new(p));
24+
let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
25+
26+
if (stack_size & 4095) != 0 {
27+
stack_size = (stack_size + 4095) & !4095;
28+
}
29+
30+
// Allocate the whole thing, then divide it up after the fact. This ensures that
31+
// even if there's a context switch during this function, the whole stack plus
32+
// guard pages will remain contiguous.
33+
let stack_plus_guard_pages: &mut [u8] = unsafe {
34+
map_memory(
35+
None,
36+
None,
37+
GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE,
38+
MemoryFlags::R | MemoryFlags::W | MemoryFlags::X,
39+
)
40+
}
41+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
42+
43+
// No access to this page. Note: Write-only pages are illegal, and will
44+
// cause an access violation.
45+
unsafe {
46+
update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W)
47+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?
48+
};
49+
50+
// No access to this page. Note: Write-only pages are illegal, and will
51+
// cause an access violation.
52+
unsafe {
53+
update_memory_flags(
54+
&mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..],
55+
MemoryFlags::W,
56+
)
57+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?
58+
};
59+
60+
let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize;
61+
let tid = create_thread(
62+
thread_start as *mut usize,
63+
&mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)],
64+
p as usize,
65+
guard_page_pre,
66+
stack_size,
67+
0,
68+
)
69+
.map_err(|code| io::Error::from_raw_os_error(code as i32))?;
70+
71+
extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) {
72+
unsafe {
73+
// Finally, let's run some code.
74+
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
75+
}
76+
77+
// Destroy TLS, which will free the TLS page and call the destructor for
78+
// any thread local storage.
79+
unsafe {
80+
crate::sys::thread_local_key::destroy_tls();
81+
}
82+
83+
// Deallocate the stack memory, along with the guard pages. Afterwards,
84+
// exit the thread by returning to the magic address 0xff80_3000usize,
85+
// which tells the kernel to deallocate this thread.
86+
let mapped_memory_base = guard_page_pre;
87+
let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE;
88+
unsafe {
89+
asm!(
90+
"ecall",
91+
"ret",
92+
in("a0") Syscall::UnmapMemory as usize,
93+
in("a1") mapped_memory_base,
94+
in("a2") mapped_memory_length,
95+
in("ra") 0xff80_3000usize,
96+
options(nomem, nostack, noreturn)
97+
);
98+
}
99+
}
100+
101+
Ok(Thread { tid })
102+
}
103+
104+
pub fn yield_now() {
105+
do_yield();
106+
}
107+
108+
pub fn set_name(_name: &CStr) {
109+
// nope
110+
}
111+
112+
pub fn sleep(dur: Duration) {
113+
// Because the sleep server works on units of `usized milliseconds`, split
114+
// the messages up into these chunks. This means we may run into issues
115+
// if you try to sleep a thread for more than 49 days on a 32-bit system.
116+
let mut millis = dur.as_millis();
117+
while millis > 0 {
118+
let sleep_duration =
119+
if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
120+
blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
121+
.expect("failed to send message to ticktimer server");
122+
millis -= sleep_duration as u128;
123+
}
124+
}
125+
126+
pub fn join(self) {
127+
join_thread(self.tid).unwrap();
128+
}
129+
}
130+
131+
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
132+
// We're unicore right now.
133+
Ok(unsafe { NonZeroUsize::new_unchecked(1) })
134+
}
135+
136+
pub mod guard {
137+
pub type Guard = !;
138+
pub unsafe fn current() -> Option<Guard> {
139+
None
140+
}
141+
pub unsafe fn init() -> Option<Guard> {
142+
None
143+
}
144+
}

0 commit comments

Comments
 (0)