Skip to main content

leodos_libcfs/cfe/es/
task.rs

1//! Safe, idiomatic wrappers for CFE Executive Services Task query APIs.
2
3use crate::cfe::es::app::AppId;
4use crate::error::{CfsError, OsalError, Result};
5use crate::ffi;
6use crate::os::task::TaskFlags;
7use crate::cstring;
8use crate::status::check;
9use core::ffi::CStr;
10use core::mem::MaybeUninit;
11use heapless::CString;
12
13/// A type-safe, zero-cost wrapper for a cFE Task ID.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct TaskId(pub ffi::CFE_ES_TaskId_t);
16
17/// Type alias for a cFE child task entry point function.
18pub type TaskEntryPoint = unsafe extern "C" fn();
19
20/// A handle to a cFE child task.
21///
22/// This is a wrapper around a CFE-level task ID that will automatically call
23/// `CFE_ES_DeleteChildTask` when it goes out of scope, preventing resource leaks.
24/// It must not be used for an Application's Main Task.
25#[derive(Debug)]
26pub struct ChildTask {
27    id: TaskId,
28}
29
30impl ChildTask {
31    /// Creates a new cFE child task and starts it running.
32    ///
33    /// The new task is owned by the calling Application. The stack for the task
34    /// can be provided or allocated from the system memory heap.
35    ///
36    /// # Arguments
37    /// * `name`: A unique string to identify the task.
38    /// * `entry_point`: The function that the new task will execute. This must
39    ///   be a free function with `extern "C"` linkage.
40    /// * `stack_ptr`: A pointer to the task's stack, or `core::ptr::null_mut()`
41    ///   to have cFE allocate it.
42    /// * `stack_size`: The size of the stack to allocate for the new task.
43    /// * `priority`: The priority of the new task (0=highest, 255=lowest).
44    /// * `flags`: Task creation flags (e.g. `TaskFlags::FP_ENABLED`).
45    pub fn new(
46        name: &str,
47        entry_point: TaskEntryPoint,
48        stack_ptr: *mut (),
49        stack_size: usize,
50        priority: u16,
51        flags: TaskFlags,
52    ) -> Result<Self> {
53        let c_name = cstring::<{ ffi::OS_MAX_API_NAME as usize }>(name)?;
54
55        let mut task_id = MaybeUninit::uninit();
56        check(unsafe {
57            ffi::CFE_ES_CreateChildTask(
58                task_id.as_mut_ptr(),
59                c_name.as_ptr(),
60                Some(entry_point),
61                stack_ptr as *mut _,
62                stack_size,
63                priority,
64                flags.bits(),
65            )
66        })?;
67
68        Ok(Self {
69            id: TaskId(unsafe { task_id.assume_init() }),
70        })
71    }
72
73    /// Returns the underlying `TaskId` for this child task.
74    pub fn id(&self) -> TaskId {
75        self.id
76    }
77}
78
79impl Drop for ChildTask {
80    /// Deletes the cFE child task when the `ChildTask` object goes out of scope.
81    fn drop(&mut self) {
82        // CFE_ES_DeleteChildTask can return an error, but we ignore it in drop.
83        let _ = unsafe { ffi::CFE_ES_DeleteChildTask(self.id.0) };
84    }
85}
86
87/// A high-level wrapper around the FFI's `CFE_ES_TaskInfo_t`.
88#[derive(Debug, Clone)]
89pub struct TaskInfo {
90    inner: ffi::CFE_ES_TaskInfo_t,
91}
92
93impl TaskInfo {
94    /// Returns the OSAL Task ID for this task.
95    pub fn task_id(&self) -> TaskId {
96        TaskId(self.inner.TaskId)
97    }
98
99    /// Returns the parent Application ID for this task.
100    pub fn app_id(&self) -> AppId {
101        AppId(self.inner.AppId)
102    }
103
104    /// Returns the execution counter for this task.
105    pub fn execution_counter(&self) -> u32 {
106        self.inner.ExecutionCounter
107    }
108
109    /// Returns the registered name of the task.
110    pub fn name(&self) -> Result<CString<{ ffi::OS_MAX_API_NAME as usize }>> {
111        let c_str = unsafe { CStr::from_ptr(self.inner.TaskName.as_ptr()) };
112        let mut s = CString::new();
113        s.extend_from_bytes(c_str.to_bytes())
114            .map_err(|_| CfsError::Osal(OsalError::NameTooLong))?;
115        Ok(s)
116    }
117
118    /// Returns the registered name of the parent application.
119    pub fn app_name(&self) -> Result<CString<{ ffi::OS_MAX_API_NAME as usize }>> {
120        let c_str = unsafe { CStr::from_ptr(self.inner.AppName.as_ptr()) };
121        let mut s = CString::new();
122        s.extend_from_bytes(c_str.to_bytes())
123            .map_err(|_| CfsError::Osal(OsalError::NameTooLong))?;
124        Ok(s)
125    }
126}
127
128impl TaskId {
129    /// Retrieves detailed information about the task with this ID.
130    pub fn info(&self) -> Result<TaskInfo> {
131        let mut task_info_uninit = MaybeUninit::uninit();
132        check(unsafe { ffi::CFE_ES_GetTaskInfo(task_info_uninit.as_mut_ptr(), self.0) })?;
133        Ok(TaskInfo {
134            inner: unsafe { task_info_uninit.assume_init() },
135        })
136    }
137
138    /// Deletes a child task with this ID.
139    ///
140    /// This function is a standalone wrapper for `CFE_ES_DeleteChildTask`. Using the
141    /// `ChildTask` RAII struct is generally preferred to ensure the task is always deleted.
142    /// It must not be called for an Application's Main Task.
143    pub fn delete(&self) -> Result<()> {
144        check(unsafe { ffi::CFE_ES_DeleteChildTask(self.0) })?;
145        Ok(())
146    }
147
148    /// Retrieves the cFE Task Name for this task ID.
149    pub fn name(&self) -> Result<CString<{ ffi::OS_MAX_API_NAME as usize }>> {
150        let mut buffer = [0u8; ffi::OS_MAX_API_NAME as usize];
151        check(unsafe {
152            ffi::CFE_ES_GetTaskName(
153                buffer.as_mut_ptr() as *mut libc::c_char,
154                self.0,
155                buffer.len(),
156            )
157        })?;
158
159        // Find the null terminator to determine the actual length.
160        let len = buffer.iter().position(|&b| b == 0).unwrap_or(0);
161        let mut s = CString::new();
162        s.extend_from_bytes(&buffer[..len])
163            .map_err(|_| CfsError::Osal(OsalError::NameTooLong))?;
164        Ok(s)
165    }
166
167    /// Converts this CFE Task ID into a zero-based integer suitable for array indexing.
168    pub fn to_index(&self) -> Result<u32> {
169        let mut index = MaybeUninit::uninit();
170        check(unsafe { ffi::CFE_ES_TaskID_ToIndex(self.0, index.as_mut_ptr()) })?;
171        Ok(unsafe { index.assume_init() })
172    }
173
174    /// Retrieves the cFE Task ID for a given task name.
175    pub fn from_name(name: &str) -> Result<Self> {
176        let c_name = cstring::<{ ffi::OS_MAX_API_NAME as usize }>(name)?;
177
178        let mut task_id = MaybeUninit::uninit();
179        check(unsafe { ffi::CFE_ES_GetTaskIDByName(task_id.as_mut_ptr(), c_name.as_ptr()) })?;
180        Ok(TaskId(unsafe { task_id.assume_init() }))
181    }
182
183    /// Retrieves the CFE Task ID of the currently executing task.
184    pub fn current() -> Result<Self> {
185        let mut task_id = MaybeUninit::uninit();
186        check(unsafe { ffi::CFE_ES_GetTaskID(task_id.as_mut_ptr()) })?;
187        Ok(TaskId(unsafe { task_id.assume_init() }))
188    }
189}
190
191/// Exits the calling child task.
192///
193/// This function terminates the currently running child task and does not return.
194/// It must not be called from an Application's Main Task.
195pub fn exit_child_task() -> ! {
196    unsafe {
197        ffi::CFE_ES_ExitChildTask();
198    }
199    // This function never returns, but we add a loop to satisfy the compiler.
200    loop {}
201}
202
203/// Increments the execution counter for the calling task.
204///
205/// This is typically not needed for main application tasks that call `run_cycle`
206/// (via `CFE_ES_RunLoop`), as the counter is incremented automatically.
207/// It is useful for child tasks or other contexts where the counter needs to
208/// be manually managed to indicate liveness.
209pub fn increment_task_counter() {
210    unsafe {
211        ffi::CFE_ES_IncrementTaskCounter();
212    }
213}