Skip to content

Commit 2e66e84

Browse files
committed
zephyr: Basic thread wrappers
This creates a Rust `Thread` type and the appropriate kernel object types to be able to create threads from within Rust. In addition, if allocation is available, these threads can be started with closures, similar to how spawn works in std. Signed-off-by: David Brown <[email protected]>
1 parent bd01f22 commit 2e66e84

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

zephyr/src/sys.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! unsafe, but as unchanged as possible.
1111
1212
pub mod sync;
13+
pub mod thread;
1314

1415
use zephyr_sys::k_timeout_t;
1516

zephyr/src/sys/thread.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//! Zephyr low level threads
2+
//!
3+
//! This is a fairly low level (but still safe) interface to Zephyr threads. This is intended to
4+
//! work the same way as threads are typically done on Zephyr systems, where the threads and their
5+
//! stacks are statically allocated, a code is called to initialize them.
6+
//!
7+
//! In addition, there are some convenience operations available that require allocation to be
8+
//! available.
9+
10+
use zephyr_sys::{
11+
k_thread, k_thread_create, k_thread_start, z_thread_stack_element, ZR_STACK_ALIGN, ZR_STACK_RESERVED
12+
};
13+
14+
use core::{cell::UnsafeCell, ffi::c_void, ptr::null_mut};
15+
16+
use crate::{align::AlignAs, object::{KobjInit, StaticKernelObject}};
17+
18+
#[cfg(CONFIG_RUST_ALLOC)]
19+
extern crate alloc;
20+
#[cfg(CONFIG_RUST_ALLOC)]
21+
use alloc::boxed::Box;
22+
#[cfg(CONFIG_RUST_ALLOC)]
23+
use core::mem::ManuallyDrop;
24+
25+
use super::K_FOREVER;
26+
27+
/// Adjust the stack size for alignment. Note that, unlike the C code, we don't include the
28+
/// reservation in this, as it has its own fields in the struct.
29+
pub const fn stack_len(size: usize) -> usize {
30+
size.next_multiple_of(ZR_STACK_ALIGN)
31+
}
32+
33+
/// A Zephyr stack declaration. It isn't meant to be used directly, as it needs additional
34+
/// decoration about linker sections and such. Unlike the C declaration, the reservation is a
35+
/// separate field. As long as the SIZE is properly aligned, this should work without padding
36+
/// between the fields.
37+
pub struct ThreadStack<const SIZE: usize> {
38+
pub align: AlignAs<ZR_STACK_ALIGN>,
39+
pub data: UnsafeCell<[z_thread_stack_element; SIZE]>,
40+
pub extra: [z_thread_stack_element; ZR_STACK_RESERVED],
41+
}
42+
43+
unsafe impl<const SIZE: usize> Sync for ThreadStack<SIZE> {}
44+
45+
impl<const SIZE: usize> ThreadStack<SIZE> {
46+
/// Get the size of this stack. This is the size, minus any reservation. This is called `size`
47+
/// to avoid any confusion with `len` which might return the actual size of the stack.
48+
pub fn size(&self) -> usize {
49+
SIZE
50+
}
51+
52+
/// Return the stack base needed as the argument to various zephyr calls.
53+
pub fn base(&self) -> *mut z_thread_stack_element {
54+
self.data.get() as *mut z_thread_stack_element
55+
}
56+
57+
/// Return the token information for this stack, which is a base and size.
58+
pub fn token(&self) -> StackToken {
59+
StackToken { base: self.base(), size: self.size() }
60+
}
61+
}
62+
63+
/// Declare a variable, of a given name, representing the stack for a thread.
64+
#[macro_export]
65+
macro_rules! kernel_stack_define {
66+
($name:ident, $size:expr) => {
67+
#[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
68+
static $name: $crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}>
69+
= unsafe { ::core::mem::zeroed() };
70+
};
71+
}
72+
73+
pub struct Thread {
74+
pub raw: *mut k_thread,
75+
}
76+
77+
unsafe impl Sync for StaticKernelObject<k_thread> { }
78+
79+
impl KobjInit<k_thread, Thread> for StaticKernelObject<k_thread> {
80+
fn wrap(ptr: *mut k_thread) -> Thread {
81+
Thread { raw: ptr }
82+
}
83+
}
84+
85+
// Public interface to threads.
86+
impl Thread {
87+
/// Start execution of the given thread.
88+
pub fn start(&self) {
89+
unsafe { k_thread_start(self.raw) }
90+
}
91+
}
92+
93+
/// Declare a global static representing a thread variable.
94+
#[macro_export]
95+
macro_rules! kernel_thread_define {
96+
($name:ident) => {
97+
// Since the static object has an atomic that we assume is initialized, let the compiler put
98+
// this in the data section it finds appropriate (probably .bss if it is initialized to zero).
99+
// This only matters when the objects are being checked.
100+
// TODO: This doesn't seem to work with the config.
101+
// #[cfg_attr(not(CONFIG_RUST_CHECK_KOBJ_INIT),
102+
// link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!()))]
103+
static $name: $crate::object::StaticKernelObject<$crate::raw::k_thread> =
104+
$crate::object::StaticKernelObject::new();
105+
// static $name: $crate::sys::thread::Thread = unsafe { ::core::mem::zeroed() };
106+
};
107+
}
108+
109+
/// For now, this "token" represents the somewhat internal information about thread.
110+
/// What we really want is to make sure that stacks and threads go together.
111+
pub struct StackToken {
112+
base: *mut z_thread_stack_element,
113+
size: usize,
114+
}
115+
116+
// This isn't really safe at all, as these can be initialized. It is unclear how, if even if it is
117+
// possible to implement safe static threads and other data structures in Zephyr.
118+
119+
pub type StaticThread = StaticKernelObject<k_thread>;
120+
121+
// The thread itself assumes we've already initialized, so this method is on the wrapper.
122+
impl StaticThread {
123+
/// Spawn this thread to the given external function. This is a simplified version that doesn't
124+
/// take any arguments. The child runs immediately.
125+
pub fn simple_spawn(&self, stack: StackToken, child: fn() -> ()) -> Thread {
126+
self.init_help(|raw| {
127+
unsafe {
128+
k_thread_create(
129+
raw,
130+
stack.base,
131+
stack.size,
132+
Some(simple_child),
133+
child as *mut c_void,
134+
null_mut(),
135+
null_mut(),
136+
5,
137+
0,
138+
K_FOREVER,
139+
);
140+
}
141+
});
142+
self.get()
143+
}
144+
145+
#[cfg(CONFIG_RUST_ALLOC)]
146+
/// Spawn a thread, running a closure. The closure will be boxed to give to the new thread.
147+
/// The new thread runs immediately.
148+
pub fn spawn<F: FnOnce() + Send + 'static>(&self, stack: StackToken, child: F) -> Thread {
149+
let child: closure::Closure = Box::new(child);
150+
let child = Box::into_raw(Box::new(closure::ThreadData {
151+
closure: ManuallyDrop::new(child),
152+
}));
153+
self.init_help(move |raw| {
154+
unsafe {
155+
k_thread_create(
156+
raw,
157+
stack.base,
158+
stack.size,
159+
Some(closure::child),
160+
child as *mut c_void,
161+
null_mut(),
162+
null_mut(),
163+
5,
164+
0,
165+
K_FOREVER,
166+
);
167+
}
168+
});
169+
self.get()
170+
}
171+
}
172+
173+
unsafe extern "C" fn simple_child(
174+
arg: *mut c_void,
175+
_p2: *mut c_void,
176+
_p3: *mut c_void,
177+
) {
178+
let child: fn() -> () = core::mem::transmute(arg);
179+
(child)();
180+
}
181+
182+
#[cfg(CONFIG_RUST_ALLOC)]
183+
/// Handle the closure case. This invokes a double box to rid us of the fat pointer. I'm not sure
184+
/// this is actually necessary.
185+
mod closure {
186+
use core::{ffi::c_void, mem::ManuallyDrop};
187+
use super::Box;
188+
189+
pub type Closure = Box<dyn FnOnce()>;
190+
191+
pub struct ThreadData {
192+
pub closure: ManuallyDrop<Closure>,
193+
}
194+
195+
pub unsafe extern "C" fn child(child: *mut c_void, _p2: *mut c_void, _p3: *mut c_void) {
196+
let mut thread_data: Box<ThreadData> = unsafe { Box::from_raw(child as *mut ThreadData) };
197+
let closure = unsafe { ManuallyDrop::take(&mut (*thread_data).closure) };
198+
closure();
199+
}
200+
}

0 commit comments

Comments
 (0)