1use alloc::{
2 alloc::{alloc, dealloc, handle_alloc_error},
3 boxed::Box,
4};
5use core::{
6 alloc::Layout,
7 cell::UnsafeCell,
8 ffi::{c_char, c_void},
9 ptr::NonNull,
10};
11
12use sys::os::thread::*;
13
14pub const STACK_ALIGNMENT: usize = 0x1000;
15
16pub struct Thread {
20 started: UnsafeCell<bool>,
21 inner: Box<UnsafeCell<ThreadType>>,
22 _stack: Stack,
23}
24
25pub struct Builder {
36 name: Option<[c_char; 32]>,
37 halted: bool,
38 stack: EitherOr<&'static mut [u8], usize>,
39 priority: i32,
40}
41
42struct AlignedStack {
43 ptr: NonNull<u8>,
44 size: usize,
45}
46
47enum EitherOr<A, B> {
48 A(A),
49 B(B),
50}
51
52#[allow(dead_code)]
53enum Stack {
54 Static(&'static mut [u8]),
55 Dynamic(AlignedStack),
56}
57
58pub fn spawn<F: FnOnce() + Send + 'static>(f: F, stack_size: usize, priority: i32) -> Thread {
60 let t = spawn_halted(f, stack_size, priority);
61 t.start();
62 t
63}
64
65pub fn spawn_with<F: FnOnce() + Send + 'static>(f: F, stack: &'static mut [u8], priority: i32) -> Thread {
67 let t = spawn_halted_with(f, stack, priority);
68 t.start();
69 t
70}
71
72pub fn spawn_halted<F: FnOnce() + Send + 'static>(f: F, stack_size: usize, priority: i32) -> Thread {
74 let thread = Box::new(UnsafeCell::new(unsafe { core::mem::zeroed() }));
75 let arg = Box::into_raw(Box::new(f)) as *mut c_void;
76 let mut stack = AlignedStack::new(stack_size);
77
78 unsafe {
79 if nnosCreateThread(
80 thread.get(),
81 thread_entry::<F>,
82 arg,
83 stack.as_mut_ptr() as *mut c_void,
84 stack.len(),
85 priority,
86 ) != 0
87 {
88 drop(Box::<F>::from_raw(arg as *mut F));
89 panic!("Failed to create thread");
90 }
91 }
92
93 Thread {
94 started: UnsafeCell::new(false),
95 inner: thread,
96 _stack: Stack::Dynamic(stack),
97 }
98}
99
100pub fn spawn_halted_with<F: FnOnce() + Send + 'static>(f: F, stack: &'static mut [u8], priority: i32) -> Thread {
102 let thread = Box::new(UnsafeCell::new(unsafe { core::mem::zeroed() }));
103 let arg = Box::into_raw(Box::new(f)) as *mut c_void;
104
105 unsafe {
106 if nnosCreateThread(
107 thread.get(),
108 thread_entry::<F>,
109 arg,
110 stack.as_mut_ptr() as *mut c_void,
111 stack.len(),
112 priority,
113 ) != 0
114 {
115 drop(Box::<F>::from_raw(arg as *mut F));
116 panic!("Failed to create thread");
117 }
118 }
119
120 Thread {
121 started: UnsafeCell::new(false),
122 inner: thread,
123 _stack: Stack::Static(stack),
124 }
125}
126
127unsafe extern "C" fn thread_entry<F: FnOnce()>(arg: *mut c_void) {
128 let f = unsafe { Box::from_raw(arg as *mut F) };
129 f();
130}
131
132impl Thread {
133 pub fn is_finished(&self) -> bool {
135 unsafe { nnosTryWaitThread(self.inner.get()) }
136 }
137
138 pub fn join(&self) {
140 unsafe { nnosWaitThread(self.inner.get()) };
141 }
142
143 pub fn id(&self) -> u64 {
145 unsafe { nnosGetThreadId(self.inner.get()) }
146 }
147
148 pub fn set_name(&self, name: &str) {
150 let mut buffer = [0 as c_char; 32];
151 let bytes = name.as_bytes();
152 let len = bytes.len().min(31);
153 for i in 0..len {
154 buffer[i] = bytes[i] as c_char;
155 }
156
157 unsafe {
158 nnosSetThreadName(self.inner.get(), buffer.as_ptr());
159 }
160 }
161
162 pub fn start(&self) {
164 unsafe {
165 nnosStartThread(self.inner.get());
166 *self.started.get() = true;
167 }
168 }
169}
170
171impl Drop for Thread {
172 fn drop(&mut self) {
173 unsafe {
174 nnosWaitThread(self.inner.get());
175 nnosDestroyThread(self.inner.get());
176 }
177 }
178}
179
180impl Builder {
181 pub const DEFAULT_STACK_SIZE: usize = 0x2000;
183
184 pub fn new() -> Self {
186 Self {
187 name: None,
188 halted: false,
189 stack: EitherOr::B(Self::DEFAULT_STACK_SIZE),
190 priority: 16,
191 }
192 }
193
194 pub fn name(mut self, name: &str) -> Self {
196 let mut buffer = [0 as c_char; 32];
197 let bytes = name.as_bytes();
198 let len = bytes.len().min(31);
199 for i in 0..len {
200 buffer[i] = bytes[i] as c_char;
201 }
202 self.name = Some(buffer);
203 self
204 }
205
206 pub fn halted(mut self) -> Self {
208 self.halted = true;
209 self
210 }
211
212 pub fn stack(mut self, stack: &'static mut [u8]) -> Self {
214 self.stack = EitherOr::A(stack);
215 self
216 }
217
218 pub fn stack_size(mut self, size: usize) -> Self {
220 self.stack = EitherOr::B(size);
221 self
222 }
223
224 pub fn priority(mut self, priority: i32) -> Self {
226 self.priority = priority;
227 self
228 }
229
230 pub fn spawn<F: FnOnce() + Send + 'static>(self, f: F) -> Thread {
232 let thread = match self.stack {
233 EitherOr::A(stack) => spawn_halted_with(f, stack, self.priority),
234 EitherOr::B(size) => spawn_halted(f, size, self.priority),
235 };
236
237 if let Some(name) = self.name {
238 unsafe {
239 nnosSetThreadName(thread.inner.get(), name.as_ptr());
240 }
241 }
242
243 if !self.halted {
244 thread.start();
245 }
246
247 thread
248 }
249}
250
251impl AlignedStack {
252 fn new(size: usize) -> Self {
253 let layout = Layout::from_size_align(size, STACK_ALIGNMENT).expect("Invalid Stack Layout");
254 let ptr = unsafe { alloc(layout) };
255 NonNull::new(ptr)
256 .map(|ptr| Self { ptr, size })
257 .unwrap_or_else(|| handle_alloc_error(layout))
258 }
259
260 fn as_mut_ptr(&mut self) -> *mut u8 {
261 self.ptr.as_ptr()
262 }
263
264 fn len(&self) -> usize {
265 self.size
266 }
267}
268
269impl Drop for AlignedStack {
270 fn drop(&mut self) {
271 unsafe {
272 dealloc(
273 self.as_mut_ptr(),
274 Layout::from_size_align_unchecked(self.size, STACK_ALIGNMENT),
275 );
276 }
277 }
278}