GCC Code Coverage Report


Directory: ./
File: Os/Task.hpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 2 2 100.0%
Functions: 2 2 100.0%
Branches: 0 0 -%

Line Branch Exec Source
1 // ======================================================================
2 // \title Os/Task.hpp
3 // \brief common function definitions for Os::Task
4 // ======================================================================
5 #ifndef Os_Task_hpp_
6 #define Os_Task_hpp_
7
8 #include <Fw/FPrimeBasicTypes.hpp>
9 #include <Fw/Time/TimeInterval.hpp>
10 #include <Fw/Types/Serializable.hpp>
11 #include <Os/Mutex.hpp>
12 #include <Os/Os.hpp>
13 #include <Os/TaskString.hpp>
14
15 #include <Fw/Deprecate.hpp>
16 #include <limits>
17
18 // Forward declare for UTs
19 namespace Os {
20 namespace Test {
21 namespace Task {
22 struct Tester;
23 }
24 } // namespace Test
25 } // namespace Os
26
27 namespace Os {
28
29 // Forward declarations
30 class TaskRegistry;
31
32 //! Task handle representation
33 class TaskHandle {};
34
35 class TaskInterface {
36 public:
37 //! Sentinel value to use a default value for the task argument to which this is supplied.
38 //!
39 //! Implementations of TaskInterface::start() should make a special case to use some default value that is
40 //! valid for the target platform.
41 static constexpr FwSizeType TASK_DEFAULT = std::numeric_limits<FwSizeType>::max();
42
43 //! Sentinel value to use a default priority for the task.
44 //!
45 //! Implementations of TaskInterface::start() should make a special case to use some default task priority
46 //! that is valid for the target platform.
47 static constexpr FwTaskPriorityType TASK_PRIORITY_DEFAULT = std::numeric_limits<FwTaskPriorityType>::max();
48
49 enum Status {
50 OP_OK, //!< message sent/received okay
51 INVALID_HANDLE, //!< Task handle invalid
52 INVALID_PARAMS, //!< started task with invalid parameters
53 INVALID_PRIORITY, //!< started task with invalid priority
54 INVALID_STACK, //!< started with invalid stack size
55 UNKNOWN_ERROR, //!< unexpected error return value
56 INVALID_AFFINITY, //!< unable to set the task affinity
57 DELAY_ERROR, //!< error trying to delay the task
58 JOIN_ERROR, //!< error trying to join the task
59 ERROR_RESOURCES, //!< unable to allocate more tasks
60 ERROR_PERMISSION, //!< permissions error setting-up tasks
61 NOT_SUPPORTED, //!< Task feature is not supported
62 INVALID_STATE, //!< Task is in an invalid state for the operation
63 };
64
65 enum SuspensionType { INTENTIONAL, UNINTENTIONAL };
66
67 enum State { NOT_STARTED, STARTING, RUNNING, SUSPENDED_INTENTIONALLY, SUSPENDED_UNINTENTIONALLY, EXITED, UNKNOWN };
68
69 //! Prototype for task routine started in task context
70 typedef void (*taskRoutine)(void* ptr);
71
72 class Arguments {
73 public:
74 //! \brief construct a set of arguments to start a task
75 //!
76 //! Construct a set of arguments to start a task. It is illegal to supply a task routine that is
77 //! set to a nullptr.
78 //!
79 //! \param name: name of the task
80 //! \param routine: routine to run as part of this task
81 //! \param routine_argument: (optional) argument to supply to the task routine
82 //! \param priority: (optional) priority of this task
83 //! \param stackSize: (optional) size of stack supplied to this task
84 //! \param cpuAffinity: (optional) cpu affinity of this task. TODO: fix this into an array
85 //! \param identifier: (optional) identifier for this task
86 Arguments(const Fw::ConstStringBase& name,
87 const taskRoutine routine,
88 void* const routine_argument = nullptr,
89 const FwTaskPriorityType priority = TASK_PRIORITY_DEFAULT,
90 const FwSizeType stackSize = TASK_DEFAULT,
91 const FwSizeType cpuAffinity = TASK_DEFAULT,
92 const FwTaskIdType identifier = static_cast<FwTaskIdType>(TASK_DEFAULT));
93
94 public:
95 const Os::TaskString m_name;
96 taskRoutine m_routine;
97 void* m_routine_argument;
98 FwTaskPriorityType m_priority;
99 FwSizeType m_stackSize;
100 FwSizeType m_cpuAffinity;
101 FwTaskIdType m_identifier;
102 };
103
104 //! \brief default constructor
105 46 TaskInterface() = default;
106
107 //! \brief default virtual destructor
108 92 virtual ~TaskInterface() = default;
109
110 //! \brief copy constructor is forbidden
111 TaskInterface(const TaskInterface& other) = delete;
112
113 //! \brief assignment operator is forbidden
114 TaskInterface& operator=(const TaskInterface& other) = delete;
115
116 // =================
117 // Implementation functions (static) to be supplied by the linker
118 // =================
119
120 //! \brief provide a pointer to a task delegate object
121 //!
122 //! This function must return a pointer to a `TaskInterface` object that contains the real implementation of the
123 //! file functions as defined by the implementor. This function must do several things to be considered correctly
124 //! implemented:
125 //!
126 //! 1. Assert that the supplied memory is non-null. e.g `FW_ASSERT(aligned_placement_new_memory != NULL);`
127 //! 2. Assert that their implementation fits within FW_HANDLE_MAX_SIZE.
128 //! e.g. `static_assert(sizeof(PosixTaskImplementation) <= sizeof Os::Task::m_handle_storage,
129 //! "FW_HANDLE_MAX_SIZE to small");`
130 //! 3. Assert that their implementation aligns within FW_HANDLE_ALIGNMENT.
131 //! e.g. `static_assert((FW_HANDLE_ALIGNMENT % alignof(PosixTaskImplementation)) == 0, "Bad handle alignment");`
132 //! 4. Placement new their implementation into `aligned_placement_new_memory`
133 //! e.g. `TaskInterface* interface = new (aligned_placement_new_memory) PosixTaskImplementation;`
134 //! 5. Return the result of the placement new
135 //! e.g. `return interface;`
136 //!
137 //! \return result of placement new, must be equivalent to `aligned_placement_new_memory`
138 //!
139 static TaskInterface* getDelegate(TaskHandleStorage& aligned_placement_new_memory);
140
141 // =================
142 // Implementation functions (instance) to be supplied by the Os::TaskInterface children
143 // =================
144
145 //! \brief perform required task start actions
146 virtual void onStart() = 0;
147
148 //! \brief block until the task has ended
149 //!
150 //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
151 //! signals required to stop this task have already been emitted or will be emitted by another task.
152 //!
153 //! \return status of the block
154 virtual Status join() = 0;
155
156 //! \brief suspend the task given the suspension type
157 //!
158 //! Suspends the task. Some implementations track if the suspension of a task was intentional or
159 //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
160 //! unintentional. The type of suspension is also returned when calling `isSuspended`.
161 //!
162 //! \param suspensionType intentionality of the suspension
163 virtual void suspend(SuspensionType suspensionType) = 0;
164
165 //! \brief resume a suspended task
166 //!
167 //! Resumes this task. Not started, running, and exited tasks take no action.
168 //!
169 virtual void resume() = 0;
170
171 //! \brief delay the currently scheduled task using the given architecture
172 //!
173 //! Delays, or sleeps, the current task by the supplied time interval. In non-preempting os implementations
174 //! the task will resume no earlier than expected but an exact wake-up time is not guaranteed.
175 //!
176 //! \param interval: delay time
177 //! \return status of the delay
178 virtual Status _delay(const Fw::TimeInterval& interval) = 0;
179
180 //! \brief determine if the task requires cooperative multitasking
181 //!
182 //! Some task implementations require cooperative multitasking where the task execution is run by a user
183 //! defined task scheduler and not the operating system task scheduler. These tasks cooperatively on
184 //! multitask by doing one unit of work and return from the function.
185 //!
186 //! This function indicates if the task requires cooperative support.
187 //! The default implementation returns false.
188 //!
189 //! \return true when the task expects cooperation, false otherwise
190 virtual bool isCooperative();
191
192 //! \brief return the underlying task handle (implementation specific)
193 //! \return internal task handle representation
194 virtual TaskHandle* getHandle() = 0;
195
196 //! \brief start the task
197 //!
198 //! Starts the task given the supplied arguments.
199 //!
200 //! \param arguments: arguments supplied to the task start call
201 //! \return status of the task start
202 virtual Status start(const Arguments& arguments) = 0;
203 };
204
205 //! Task class intended to be used by the rest of the fprime system. This is final as it is not intended to be a
206 //! parent class. Instead it wraps a delegate provided by `TaskInterface::getDelegate()` to provide system specific
207 //! behaviour.
208 class Task final : public TaskInterface {
209 friend struct Os::Test::Task::Tester;
210
211 public:
212 //! Wrapper for task routine that ensures `onStart()` is called once the task actually begins
213 class TaskRoutineWrapper {
214 public:
215 explicit TaskRoutineWrapper(Task& self);
216
217 //! \brief run the task routine wrapper
218 //!
219 //! Sets the Os::Task to started via the setStarted method. Then runs the user function passing in the
220 //! user argument.
221 //! \param task_pointer: pointer to TaskRoutineWrapper being run
222 static void run(void* task_pointer);
223
224 //! \brief invoke the run method with "self" as argument
225 void invoke();
226
227 Task& m_task; //!< Reference to owning task
228 taskRoutine m_user_function = nullptr; //!< User function to run once started
229 void* m_user_argument = nullptr; //!< Argument to user function
230 };
231
232 //! \brief backwards-compatible parameter type
233 typedef FwSizeType ParamType;
234
235 //! \brief default constructor
236 Task();
237
238 //! \brief default virtual destructor
239 ~Task() final;
240
241 //! \brief copy constructor is forbidden
242 Task(const Task& other) = delete;
243
244 //! \brief assignment operator is forbidden
245 Task& operator=(const Task& other) = delete;
246
247 //! \brief suspend the current task
248 //!
249 //! Suspend the current task unintentionally. If the user needs to indicate that the task was suspended
250 //! intentionally then a call to `suspend(SuspensionType::INTENTIONAL)` should be used.
251 void suspend();
252
253 //! \brief get the task's state
254 //!
255 //! Returns the task state: not started, running, suspended (intentionally), suspended (unintentionally),
256 //! and exited.
257 //!
258 //! \return task state
259 State getState() const;
260
261 //! \brief start this task
262 //!
263 //! Start this task with supplied name, task routine (run function), priority, stack, affinity, and task
264 //! identifier. These arguments are supplied into an Arguments class and that version of the function is called.
265 //! It is illegal to supply a nullptr as routine.
266 //!
267 //! \param name: name of the task to start
268 //! \param routine: user routine to run
269 //! \param arg: (optional) user argument to supply to task routine
270 //! \param priority: (optional) priority of this task
271 //! \param stackSize: (optional) stack size of this task
272 //! \param cpuAffinity: (optional) affinity of this task. Use `Task::start(Arguments&)` to supply affinity set.
273 //! \param identifier: (optional) identifier of this task
274 //! \return: status of the start call
275 DEPRECATED(Status start(const Fw::ConstStringBase& name,
276 const taskRoutine routine,
277 void* const arg = nullptr,
278 const FwTaskPriorityType priority = TASK_PRIORITY_DEFAULT,
279 const ParamType stackSize = TASK_DEFAULT,
280 const ParamType cpuAffinity = TASK_DEFAULT,
281 const ParamType identifier = TASK_DEFAULT),
282 "Switch to Task::start(Arguments&)");
283
284 //! \brief start the task
285 //!
286 //! Starts the task given the supplied arguments. This is done via a task routine wrapper intermediary that
287 //! ensures that `setStarted` is called once the task has actually started to run. The task then runs the user
288 //! routine. This function may return before the new task begins to run.
289 //
290 //! It is illegal for arguments.m_routine to be null.
291 //!
292 //! \param arguments: arguments supplied to the task start call
293 //! \return status of the task start
294 Status start(const Arguments& arguments) override;
295
296 //! \brief perform delegate's required task start actions
297 void onStart() override;
298
299 //! \brief invoke the task's routine
300 //~
301 //! This will invoke the task's routine passing this as the argument to that call. This is used as a helper when
302 //! running this task (e.g. repetitive cooperative calls).
303 void invokeRoutine();
304
305 //! \brief join calling thread to this thread
306 //!
307 //! Note: this function is deprecated as the value_ptr object is not used anyway and should always be set
308 //! to nullptr.
309 //!
310 //! \param value_ptr must be set to nullptr
311 //! \return status of the join
312 DEPRECATED(Status join(void** value_ptr), "Please switch to argument free join.");
313
314 //! \brief block until the task has ended
315 //!
316 //! Blocks the current (calling) task until this task execution has ended. Callers should ensure that any
317 //! signals required to stop this task have already been emitted or will be emitted by another task.
318 //!
319 //! \return status of the block
320 Status join() override; //!< Wait for task to finish
321
322 //! \brief suspend the task given the suspension type
323 //!
324 //! Suspends the task. Some implementations track if the suspension of a task was intentional or
325 //! unintentional. The supplied `suspensionType` parameter indicates that this was intentional or
326 //! unintentional. The type of suspension is also returned when calling `isSuspended`.
327 //!
328 //! \param suspensionType intentionality of the suspension
329 void suspend(SuspensionType suspensionType) override;
330
331 //! \brief resume a suspended task
332 //!
333 //! Resumes this task. Not started, running, and exited tasks take no action.
334 //!
335 void resume() override;
336
337 //! \brief delay the current task
338 //!
339 //! Delays, or sleeps, the current task by the supplied time interval. In non-preempting os implementations
340 //! the task will resume no earlier than expected but an exact wake-up time is not guaranteed.
341 //!
342 //! \param interval: delay time
343 //! \return status of the delay
344 Status _delay(const Fw::TimeInterval& interval) override;
345
346 //! \brief determine if the task is cooperative multitasking (implementation specific)
347 //! \return true if cooperative, false otherwise
348 bool isCooperative() override;
349
350 //! \brief get the task name
351 TaskString getName();
352
353 //! \brief get the task priority
354 FwTaskPriorityType getPriority();
355
356 //! \brief return the underlying task handle (implementation specific)
357 //! \return internal task handle representation
358 TaskHandle* getHandle() override;
359
360 //! \brief initialize singleton
361 static void init();
362
363 //! \brief get the current number of tasks
364 //! \return current number of tasks
365 static FwSizeType getNumTasks();
366
367 //! \brief register a task registry to track Threads
368 //!
369 static void registerTaskRegistry(TaskRegistry* registry);
370
371 //! \brief get a reference to singleton
372 //! \return reference to singleton
373 static Task& getSingleton();
374
375 //! \brief delay the current task
376 //!
377 //! Delays, or sleeps, the current task by the supplied time interval. In non-preempting os implementations
378 //! the task will resume no earlier than expected but an exact wake-up time is not guaranteed.
379 //!
380 //! \param interval: delay time
381 //! \return status of the delay
382 static Status delay(const Fw::TimeInterval& interval);
383
384 private:
385 static TaskRegistry* s_taskRegistry; //!< Pointer to registered task registry
386 static FwSizeType s_numTasks; //!< Stores the number of tasks created.
387 static Mutex s_taskMutex; //!< Guards s_numTasks
388
389 TaskString m_name; //!< Task object name
390 TaskInterface::State m_state = Task::NOT_STARTED;
391 mutable Mutex m_lock; //!< Guards state transitions
392 TaskRoutineWrapper m_wrapper; //!< Concrete storage for task routine wrapper
393 FwTaskPriorityType m_priority = 0; // Storage of priority
394
395 bool m_registered = false; //!< Was this task registered
396
397 // This section is used to store the implementation-defined file handle. To Os::File and fprime, this type is
398 // opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store then handle in
399 // the byte-array here and set `handle` to that address for storage.
400 //
401 alignas(FW_HANDLE_ALIGNMENT) TaskHandleStorage m_handle_storage; //!< Storage for aligned FileHandle data
402 TaskInterface& m_delegate; //!< Delegate for the real implementation
403 };
404
405 class TaskRegistry {
406 public:
407 //! \brief default task registry constructor
408 TaskRegistry() = default;
409 //! \brief default task registry constructor
410 virtual ~TaskRegistry() = default;
411 //! \brief add supplied task to the registry
412 //!
413 //! \param task: pointer to task to register
414 virtual void addTask(Task* task) = 0; //!< Add a task to the registry
415
416 //! \brief remove supplied task to the registry
417 //!
418 //! \param task: pointer to task to deregister
419 virtual void removeTask(Task* task) = 0;
420 };
421 } // namespace Os
422
423 #endif
424