osal_rs/freertos/
thread.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***************************************************************************/
19
20use core::any::Any;
21use core::ffi::{c_char, c_void};
22use core::fmt::{Debug, Display, Formatter};
23use core::ops::Deref;
24use core::ptr::null_mut;
25
26use alloc::boxed::Box;
27use alloc::string::{String, ToString};
28use alloc::sync::Arc;
29
30use super::ffi::{INVALID, TaskStatus, ThreadHandle, pdPASS, pdTRUE, vTaskDelete, vTaskGetInfo, vTaskResume, vTaskSuspend, xTaskCreate, xTaskGetCurrentTaskHandle};
31use super::types::{StackType, UBaseType, BaseType, TickType};
32use super::thread::ThreadState::*;
33use crate::os::ThreadSimpleFnPtr;
34use crate::traits::{ThreadFn, ThreadParam, ThreadFnPtr, ThreadNotification, ToTick, ToPriority};
35use crate::utils::{Result, Error, DoublePtr};
36use crate::{from_c_str, xTaskNotify, xTaskNotifyFromISR, xTaskNotifyWait};
37
38#[derive(Copy, Clone, Debug, PartialEq)]
39#[repr(u8)]
40pub enum ThreadState {
41    Running = 0,
42    Ready = 1,
43    Blocked = 2,
44    Suspended = 3,
45    Deleted = 4,
46    Invalid,
47}
48
49#[derive(Clone, Debug)]
50pub struct ThreadMetadata {
51    pub thread: ThreadHandle,
52    pub name: String,
53    pub stack_depth: StackType,
54    pub priority: UBaseType,
55    pub thread_number: UBaseType,
56    pub state: ThreadState,
57    pub current_priority: UBaseType,
58    pub base_priority: UBaseType,
59    pub run_time_counter: UBaseType,
60    pub stack_high_water_mark: StackType,
61}
62
63unsafe impl Send for ThreadMetadata {}
64unsafe impl Sync for ThreadMetadata {}
65
66impl From<(ThreadHandle,TaskStatus)> for ThreadMetadata {
67    fn from(status: (ThreadHandle, TaskStatus)) -> Self {
68        let state = match status.1.eCurrentState {
69            0 => Running,
70            1 => Ready,
71            2 => Blocked,
72            3 => Suspended,
73            4 => Deleted,
74            _ => Invalid,
75        };
76
77        ThreadMetadata {
78            thread: status.0,
79            name: from_c_str!(status.1.pcTaskName),
80            // Avoid dereferencing pxStackBase, which may be null or otherwise invalid.
81            // Use 0 as a safe default for unknown stack depth.
82            stack_depth: 0,
83            priority: status.1.uxBasePriority,
84            thread_number: status.1.xTaskNumber,
85            state,
86            current_priority: status.1.uxCurrentPriority,
87            base_priority: status.1.uxBasePriority,
88            run_time_counter: status.1.ulRunTimeCounter,
89            stack_high_water_mark: status.1.usStackHighWaterMark,
90        }
91    }
92}
93
94impl Default for ThreadMetadata {
95    fn default() -> Self {
96        ThreadMetadata {
97            thread: null_mut(),
98            name: String::new(),
99            stack_depth: 0,
100            priority: 0,
101            thread_number: 0,
102            state: Invalid,
103            current_priority: 0,
104            base_priority: 0,
105            run_time_counter: 0,
106            stack_high_water_mark: 0,
107        }
108    }
109}
110
111#[derive(Clone)]
112pub struct Thread {
113    handle: ThreadHandle,
114    name: String,
115    stack_depth: StackType,
116    priority: UBaseType,
117    callback: Option<Arc<ThreadFnPtr>>,
118    param: Option<ThreadParam>
119}
120
121unsafe impl Send for Thread {}
122unsafe impl Sync for Thread {}
123
124impl Thread {
125
126    pub fn new_with_to_priority(name: &str, stack_depth: StackType, priority: impl ToPriority) -> Self 
127    {
128        Self { 
129            handle: null_mut(), 
130            name: name.to_string(), 
131            stack_depth, 
132            priority: priority.to_priority(), 
133            callback: None,
134            param: None 
135        }
136    }
137
138    pub fn new_with_handle_and_to_priority(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: impl ToPriority) -> Result<Self> {
139        if handle.is_null() {
140            return Err(Error::NullPtr);
141        }
142        Ok(Self { 
143            handle, 
144            name: name.to_string(), 
145            stack_depth, 
146            priority: priority.to_priority(), 
147            callback: None,
148            param: None 
149        })
150    }
151
152    pub fn get_metadata_from_handle(handle: ThreadHandle) -> ThreadMetadata {
153        let mut status = TaskStatus::default();
154        unsafe {
155            vTaskGetInfo(handle, &mut status, pdTRUE, INVALID);
156        }
157        ThreadMetadata::from((handle, status))
158    }
159
160    pub fn get_metadata(thread: &Thread) -> ThreadMetadata {
161        if thread.handle.is_null() {
162            return ThreadMetadata::default();
163        }
164        Self::get_metadata_from_handle(thread.handle)
165    }
166
167    #[inline]
168    pub fn wait_notification_with_to_tick(&self, bits_to_clear_on_entry: u32, bits_to_clear_on_exit: u32 , timeout_ticks: impl ToTick) -> Result<u32> {
169        if self.handle.is_null() {
170            return Err(Error::NullPtr);
171        }
172        self.wait_notification(bits_to_clear_on_entry, bits_to_clear_on_exit, timeout_ticks.to_ticks())
173    }
174
175}
176
177unsafe extern "C" fn callback_c_wrapper(param_ptr: *mut c_void) {
178    if param_ptr.is_null() {
179        return;
180    }
181
182    let mut thread_instance: Box<Thread> = unsafe { Box::from_raw(param_ptr as *mut _) };
183
184    thread_instance.as_mut().handle = unsafe { xTaskGetCurrentTaskHandle() };
185
186    let thread = *thread_instance.clone();
187
188    let param_arc: Option<ThreadParam> = thread_instance
189        .param
190        .clone();
191
192    if let Some(callback) = &thread_instance.callback.clone() {
193        let _ = callback(thread_instance, param_arc);
194    }
195
196    thread.delete();
197}
198
199unsafe extern "C" fn simple_callback_wrapper(param_ptr: *mut c_void) {
200    if param_ptr.is_null() {
201        return;
202    }
203
204    let func: Box<Arc<ThreadSimpleFnPtr>> = unsafe { Box::from_raw(param_ptr as *mut _) };
205    func();
206
207    unsafe { vTaskDelete( xTaskGetCurrentTaskHandle()); } 
208}
209
210
211
212impl ThreadFn for Thread {
213    fn new(name: &str, stack_depth: StackType, priority: UBaseType) -> Self 
214    {
215        Self { 
216            handle: null_mut(), 
217            name: name.to_string(), 
218            stack_depth, 
219            priority, 
220            callback: None,
221            param: None 
222        }
223    }
224
225    fn new_with_handle(handle: ThreadHandle, name: &str, stack_depth: StackType, priority: UBaseType) -> Result<Self> {
226        if handle.is_null() {
227            return Err(Error::NullPtr);
228        }
229        Ok(Self { 
230            handle, 
231            name: name.to_string(), 
232            stack_depth, 
233            priority, 
234            callback: None,
235            param: None 
236        })
237    }
238
239    /// Spawns a new thread with a callback.
240    /// 
241    /// # Important
242    /// The callback must be `'static`, which means it cannot borrow local variables.
243    /// Use `move` in the closure to transfer ownership of any captured values:
244    /// 
245    /// ```ignore
246    /// let data = Arc::new(Mutex::new(0));
247    /// let thread = Thread::new("my_thread", 4096, 3, move |_thread, _param| {
248    ///     // Use 'move' to capture 'data' by value
249    ///     let mut guard = data.lock().unwrap();
250    ///     *guard += 1;
251    ///     Ok(Arc::new(()))
252    /// });
253    /// ``
254    fn spawn<F>(&mut self, param: Option<ThreadParam>, callback: F) -> Result<Self> 
255        where 
256        F: Fn(Box<dyn ThreadFn>, Option<ThreadParam>) -> Result<ThreadParam>,
257        F: Send + Sync + 'static {
258
259        let mut handle: ThreadHandle =  null_mut();
260
261        let func: Arc<ThreadFnPtr> = Arc::new(callback);
262        
263        self.callback = Some(func);
264        self.param = param.clone();
265
266        let boxed_thread = Box::new(self.clone());
267
268        let ret = unsafe {
269            xTaskCreate(
270                Some(super::thread::callback_c_wrapper),
271                self.name.clone().as_ptr() as *const c_char,
272                self.stack_depth,
273                Box::into_raw(boxed_thread) as *mut _,
274                self.priority,
275                &mut handle,
276            )
277        };
278
279        if ret != pdPASS {
280            return Err(Error::OutOfMemory)
281        }
282
283        Ok(Self { 
284            handle,
285            callback: self.callback.clone(),
286            param,
287            ..self.clone()
288        })
289    }
290
291    /// Spawns a new thread with a simple closure, similar to `std::thread::spawn`.
292    /// This is the recommended way to create threads for most use cases.
293    /// 
294    /// # Example
295    /// ```ignore
296    /// let counter = Arc::new(Mutex::new(0));
297    /// let counter_clone = Arc::clone(&counter);
298    /// 
299    /// let handle = Thread::spawn_simple("worker", 4096, 3, move || {
300    ///     let mut num = counter_clone.lock().unwrap();
301    ///     *num += 1;
302    /// }).unwrap();
303    /// 
304    /// handle.join(core::ptr::null_mut());
305    /// ```
306    fn spawn_simple<F>(&mut self, callback: F) -> Result<Self>
307    where
308        F: Fn() + Send + Sync + 'static,
309    {
310        let func: Arc<ThreadSimpleFnPtr> = Arc::new(callback);
311        let boxed_func = Box::new(func);
312        
313        let mut handle: ThreadHandle = null_mut();
314
315
316        let ret = unsafe {
317            xTaskCreate(
318                Some(simple_callback_wrapper),
319                self.name.clone().as_ptr() as *const c_char,
320                self.stack_depth,
321                Box::into_raw(boxed_func) as *mut _,
322                self.priority,
323                &mut handle,
324            )
325        };
326
327        if ret != pdPASS {
328            return Err(Error::OutOfMemory);
329        }
330
331        Ok(Self {
332            handle,
333            ..self.clone()
334        })
335    }
336
337    fn delete(&self) {
338        if !self.handle.is_null() {
339            unsafe { vTaskDelete( self.handle ); } 
340        }
341    }
342
343    fn suspend(&self) {
344        if !self.handle.is_null() {
345            unsafe { vTaskSuspend( self.handle ); } 
346        }
347    }
348
349    fn resume(&self) {
350        if !self.handle.is_null() {
351            unsafe { vTaskResume( self.handle ); } 
352        }
353    }
354
355    fn join(&self, _retval: DoublePtr) -> Result<i32> {
356        if !self.handle.is_null() {
357            unsafe { vTaskDelete( self.handle ); } 
358        }
359        Ok(0)
360    }
361
362    fn get_metadata(&self) -> ThreadMetadata {
363        let mut status = TaskStatus::default();
364        unsafe {
365            vTaskGetInfo(self.handle, &mut status, pdTRUE, INVALID);
366        }
367        ThreadMetadata::from((self.handle, status))
368    }
369
370    fn get_current() -> Self {
371        let handle = unsafe { xTaskGetCurrentTaskHandle() };
372        let metadata = Self::get_metadata_from_handle(handle);
373        Self {
374            handle,
375            name: metadata.name,
376            stack_depth: metadata.stack_depth,
377            priority: metadata.priority,
378            callback: None,
379            param: None,
380        }
381    }
382
383    fn notify(&self, notification: ThreadNotification) -> Result<()> {
384        if self.handle.is_null() {
385            return Err(Error::NullPtr);
386        }
387
388        let (action, value) = notification.into();
389
390        let ret = xTaskNotify!(
391            self.handle,
392            value,
393            action
394        );
395        
396        if ret != pdPASS {
397            Err(Error::QueueFull)
398        } else {
399            Ok(())
400        }
401
402    }
403
404    fn notify_from_isr(&self, notification: ThreadNotification, higher_priority_task_woken: &mut BaseType) -> Result<()> {
405        if self.handle.is_null() {
406            return Err(Error::NullPtr);
407        }
408
409        let (action, value) = notification.into();
410
411        let ret = xTaskNotifyFromISR!(
412            self.handle,
413            value,
414            action,
415            higher_priority_task_woken
416        );
417
418        if ret != pdPASS {
419            Err(Error::QueueFull)
420        } else {
421            Ok(())
422        }
423    }
424
425    fn wait_notification(&self, bits_to_clear_on_entry: u32, bits_to_clear_on_exit: u32 , timeout_ticks: TickType) -> Result<u32> {
426        if self.handle.is_null() {
427            return Err(Error::NullPtr);
428        }
429
430        let mut notification_value: u32 = 0;
431
432        let ret = xTaskNotifyWait!(
433            bits_to_clear_on_entry,
434            bits_to_clear_on_exit,
435            &mut notification_value,
436            timeout_ticks
437        );
438        
439
440        if ret == pdTRUE {
441            Ok(notification_value)
442        } else {
443            Err(Error::Timeout)
444        }
445    }
446
447}
448
449
450// impl Drop for Thread {
451//     fn drop(&mut self) {
452//         if !self.handle.is_null() {
453//             unsafe { vTaskDelete( self.handle ); } 
454//         }
455//     }
456// }
457
458impl Deref for Thread {
459    type Target = ThreadHandle;
460
461    fn deref(&self) -> &Self::Target {
462        &self.handle
463    }
464}
465
466impl Debug for Thread {
467    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
468        f.debug_struct("Thread")
469            .field("handle", &self.handle)
470            .field("name", &self.name)
471            .field("stack_depth", &self.stack_depth)
472            .field("priority", &self.priority)
473            .field("callback", &self.callback.as_ref().map(|_| "Some(...)"))
474            .field("param", &self.param)
475            .finish()
476    }
477}
478
479impl Display for Thread {
480    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
481        write!(f, "Thread {{ handle: {:?}, name: {}, priority: {}, stack_depth: {} }}", self.handle, self.name, self.priority, self.stack_depth)
482    }
483}
484
485