zephyr/
thread.rs

1//! Thread support.
2//!
3//! Implement the friendly Thread types used by the `zephyr::thread` proc macro to declare new
4//! threads.
5//!
6//! This is intended to be completely usable without alloc, while still allow threads to be
7//! started with any arbitrary Send arguments.  Threads can be joined, and reused after they have
8//! exited.  The model intentionally tries to be similar to how async tasks work in something like
9//! Embassy, but some changes due to the different semantics of Zephyr threads.
10
11use core::{
12    cell::UnsafeCell,
13    ffi::{c_int, c_void, CStr},
14    mem,
15    ptr::null_mut,
16    sync::atomic::Ordering,
17};
18
19use portable_atomic::AtomicU8;
20use zephyr_sys::{
21    k_thread, k_thread_create, k_thread_entry_t, k_thread_join, k_thread_name_set,
22    k_thread_priority_set, k_wakeup, z_thread_stack_element, ZR_STACK_ALIGN, ZR_STACK_RESERVED,
23};
24
25use crate::{
26    align::AlignAs,
27    error::to_result_void,
28    sys::{K_FOREVER, K_NO_WAIT},
29    time::{Forever, Timeout},
30};
31
32/// Adjust a given requested stack size up for the alignment.  This is just the stack, and the
33/// reservation is explicitly included in the stack declaration below.
34pub const fn stack_len(size: usize) -> usize {
35    size.next_multiple_of(ZR_STACK_ALIGN)
36}
37
38/// States a Zephyr thread can be in.
39#[repr(u8)]
40pub enum ThreadState {
41    /// A non running thread, that is free.
42    Init,
43    /// An allocated thread.  There is a ThreadHandle for this thread, but it has not been started.
44    Allocated,
45    /// A thread that is running, as far as we know.  Termination is not checked unless demanded.
46    Running,
47}
48
49/// The holder of data that is to be shared with the target thread.
50///
51/// # Safety
52///
53/// The Option is kept in an UnsafeCell, and it's use governed by an atomic in the `TaskData`
54/// below.  When the task is not initialized/not running, this should be set to None.  It will be
55/// set to Some in a critical section during startup, where the critical section provides the
56/// barrier.  Once the atomic is set to true, the thread owns this data.
57///
58/// The Send constraint force arguments passed to threads to be Send.
59pub struct InitData<T: Send>(pub UnsafeCell<Option<T>>);
60
61impl<T: Send> InitData<T> {
62    /// Construct new Shared init state.
63    pub const fn new() -> Self {
64        Self(UnsafeCell::new(None))
65    }
66}
67
68unsafe impl<T: Send> Sync for InitData<T> {}
69
70/// The static data associated with each thread.  The stack is kept separate, as it is intended to
71/// go into an uninitialized linker section.
72pub struct ThreadData<T: Send> {
73    init: InitData<T>,
74    state: AtomicU8,
75    thread: Thread,
76}
77
78impl<T: Send> ThreadData<T> {
79    /// Construct new ThreadData.
80    pub const fn new() -> Self {
81        Self {
82            init: InitData::new(),
83            state: AtomicU8::new(ThreadState::Init as u8),
84            thread: unsafe { Thread::new() },
85        }
86    }
87
88    /// Acquire the thread, in preparation to run it.
89    pub fn acquire_old<const SIZE: usize>(
90        &'static self,
91        args: T,
92        stack: &'static ThreadStack<SIZE>,
93        entry: k_thread_entry_t,
94    ) {
95        critical_section::with(|_| {
96            // Relaxed is sufficient, as the critical section provides both synchronization and
97            // a memory barrier.
98            let old = self.state.load(Ordering::Relaxed);
99            if old != ThreadState::Init as u8 {
100                // TODO: This is where we should check for termination.
101                panic!("Attempt to use a thread that is already in use");
102            }
103            self.state
104                .store(ThreadState::Allocated as u8, Ordering::Relaxed);
105
106            let init = self.init.0.get();
107            unsafe {
108                *init = Some(args);
109            }
110        });
111
112        // For now, just directly start the thread.  We'll want to delay this so that parameters
113        // (priority and/or flags) can be passed, as well as to have a handle to be able to join and
114        // restart threads.
115        let _tid = unsafe {
116            k_thread_create(
117                self.thread.0.get(),
118                stack.data.get() as *mut z_thread_stack_element,
119                stack.size(),
120                entry,
121                self.init.0.get() as *mut c_void,
122                null_mut(),
123                null_mut(),
124                0,
125                0,
126                K_NO_WAIT,
127            )
128        };
129    }
130
131    /// Acquire a thread from the pool of threads, panicing if the pool is exhausted.
132    pub fn acquire<const SIZE: usize>(
133        pool: &'static [Self],
134        stacks: &'static [ThreadStack<SIZE>],
135        args: T,
136        entry: k_thread_entry_t,
137        priority: c_int,
138        name: &'static CStr,
139    ) -> ReadyThread {
140        let id = Self::find_thread(pool);
141
142        let init = pool[id].init.0.get();
143        unsafe {
144            *init = Some(args);
145        }
146
147        // Create the thread in Zephyr, in a non-running state.
148        let tid = unsafe {
149            k_thread_create(
150                pool[id].thread.0.get(),
151                stacks[id].data.get() as *mut z_thread_stack_element,
152                SIZE,
153                entry,
154                pool[id].init.0.get() as *mut c_void,
155                null_mut(),
156                null_mut(),
157                priority,
158                0,
159                K_FOREVER,
160            )
161        };
162
163        // Set the name.
164        unsafe {
165            k_thread_name_set(tid, name.as_ptr());
166        }
167
168        ReadyThread { id: tid }
169    }
170
171    /// Scan the pool of threads, looking for an available thread.
172    ///
173    /// Returns the index of a newly allocated thread.  The thread will be marked 'Allocated' after
174    /// this.
175    fn find_thread(pool: &'static [Self]) -> usize {
176        let id = critical_section::with(|_| {
177            for (id, thread) in pool.iter().enumerate() {
178                // Relaxed is sufficient, due to the critical section.
179                let old = thread.state.load(Ordering::Relaxed);
180
181                match old {
182                    v if v == ThreadState::Init as u8 => {
183                        // This is available.  Mark as allocated and return from the closure.
184                        thread
185                            .state
186                            .store(ThreadState::Allocated as u8, Ordering::Relaxed);
187                        return Some(id);
188                    }
189                    v if v == ThreadState::Allocated as u8 => {
190                        // Allocate threads haven't started, so aren't available.
191                    }
192                    v if v == ThreadState::Running as u8 => {
193                        // A running thread might be available if it has terminated.  We could
194                        // improve performance here by not checking these until after the pool has
195                        // been checked for Init threads.
196                        if unsafe { k_thread_join(thread.thread.0.get(), K_NO_WAIT) } == 0 {
197                            thread
198                                .state
199                                .store(ThreadState::Allocated as u8, Ordering::Relaxed);
200                            return Some(id);
201                        }
202                    }
203                    _ => unreachable!(),
204                }
205            }
206
207            None
208        });
209
210        if let Some(id) = id {
211            id
212        } else {
213            panic!("Attempt to use more threads than declared pool size");
214        }
215    }
216}
217
218/// A thread that has been set up and is ready to start.
219///
220/// Represents a thread that has been created, but not yet started.
221pub struct ReadyThread {
222    id: *mut k_thread,
223}
224
225impl ReadyThread {
226    /// Change the priority of this thread before starting it.  The initial default priority was
227    /// determined by the declaration of the thread.
228    pub fn set_priority(&self, priority: c_int) {
229        // SAFETY: ReadyThread should only exist for valid created threads.
230        unsafe {
231            k_thread_priority_set(self.id, priority);
232        }
233    }
234
235    /// Start this thread.
236    pub fn start(self) -> RunningThread {
237        // SAFETY: ReadyThread should only exist for valid created threads.
238        unsafe {
239            // As per the docs, this should no longer be `k_thread_start`, but `k_wakeup` is fine
240            // these days.
241            k_wakeup(self.id);
242        }
243
244        RunningThread { id: self.id }
245    }
246}
247
248/// A thread that has been started.
249pub struct RunningThread {
250    id: *mut k_thread,
251}
252
253impl RunningThread {
254    /// Wait, with timeout, for this thread to finish executing.
255    ///
256    /// Will block until either the thread terminates, or the timeout occurrs.
257    pub fn join_timeout<T>(&self, timeout: T) -> crate::Result<()>
258    where
259        T: Into<Timeout>,
260    {
261        let timeout: Timeout = timeout.into();
262        let ret = unsafe { k_thread_join(self.id, timeout.0) };
263        to_result_void(ret)
264    }
265
266    /// Wait for this thread to finish executing.
267    ///
268    /// Will block until the thread has terminated.
269    ///
270    /// TODO: Allow a timeout?
271    /// TODO: Should we try to return a value?
272    pub fn join(&self) -> crate::Result<()> {
273        self.join_timeout(Forever)
274    }
275}
276
277/// A Zephyr stack declaration.
278///
279/// This isn't intended to be used directly, as it needs additional decoration about linker sections
280/// and such.  Unlike the C declaration, the reservation is a separate field.  As long as the SIZE
281/// is properly aligned, this should work without padding between the fields.
282///
283/// Generally, this should be placed in a noinit linker section to avoid having to initialize the
284/// memory.
285#[repr(C)]
286pub struct ThreadStack<const SIZE: usize> {
287    /// Align the stack itself according to the Kconfig determined alignment.
288    #[allow(dead_code)]
289    align: AlignAs<ZR_STACK_ALIGN>,
290    /// The data of the stack itself.
291    #[allow(dead_code)]
292    pub data: UnsafeCell<[z_thread_stack_element; SIZE]>,
293    /// Extra data, used by Zephyr.
294    #[allow(dead_code)]
295    extra: [z_thread_stack_element; ZR_STACK_RESERVED],
296}
297
298unsafe impl<const SIZE: usize> Sync for ThreadStack<SIZE> {}
299
300impl<const SIZE: usize> ThreadStack<SIZE> {
301    /// Construct a new ThreadStack
302    ///
303    /// # Safety
304    ///
305    /// This is unsafe as the memory remains uninitialized, and it is the responsibility of the
306    /// caller to use the stack correctly.  The stack should be associated with a single thread.
307    pub const fn new() -> Self {
308        // SAFETY: Although this is declared as zeroed, the linker section actually used on the
309        // stack can be used to place it in no-init memory.
310        unsafe { mem::zeroed() }
311    }
312
313    /// Retrieve the size of this stack.
314    pub const fn size(&self) -> usize {
315        SIZE
316    }
317}
318
319/// A zephyr thread.
320///
321/// This declares a single k_thread in Zephyr.
322pub struct Thread(pub UnsafeCell<k_thread>);
323
324// Threads are "sort of" thread safe.  But, this declaration is needed to be able to declare these
325// statically, and all generated versions will protect the thread with a critical section.
326unsafe impl Sync for Thread {}
327
328impl Thread {
329    /// Static allocation of a thread
330    ///
331    /// This makes the zero-initialized memory that can later be used as a thread.
332    ///
333    /// # Safety
334    ///
335    /// The caller is responsible for using operations such as `create` to construct the thread,
336    /// according to the underlying semantics of the Zephyr operations.
337    pub const unsafe fn new() -> Self {
338        // SAFETY: Zero initialized to match thread declarations in the C macros.
339        unsafe { mem::zeroed() }
340    }
341}