GCC Code Coverage Report


Directory: Os/
File: Task.cpp
Date: 2026-09-03 21:15:10
Exec Total Coverage
Lines: 14 137 10.2%
Functions: 6 26 23.1%
Branches: 9 50 18.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title Os/Task.cpp
3 // \brief common function implementation for Os::Task
4 // ======================================================================
5 #include <Fw/Types/Assert.hpp>
6 #include <Os/Task.hpp>
7
8 namespace Os {
9
10 TaskInterface::Arguments::Arguments(const Fw::ConstStringBase& name,
11 const Os::TaskInterface::taskRoutine routine,
12 void* const routine_argument,
13 const FwTaskPriorityType priority,
14 const FwSizeType stackSize,
15 const FwSizeType cpuAffinity,
16 const FwTaskIdType identifier)
17 : m_name(name),
18 m_routine(routine),
19 m_routine_argument(routine_argument),
20 m_priority(priority),
21 m_stackSize(stackSize),
22 m_cpuAffinity(cpuAffinity),
23 m_identifier(identifier) {
24 FW_ASSERT(routine != nullptr);
25 }
26
27 1 Task::TaskRoutineWrapper::TaskRoutineWrapper(Task& self) : m_task(self) {}
28
29 void Task::TaskRoutineWrapper::run(void* wrapper_pointer) {
30 FW_ASSERT(wrapper_pointer != nullptr);
31 TaskRoutineWrapper& wrapper = *reinterpret_cast<TaskRoutineWrapper*>(wrapper_pointer);
32 FW_ASSERT(wrapper.m_user_function != nullptr);
33
34 wrapper.m_task.m_lock.lock();
35 Task::State state = wrapper.m_task.m_state;
36 wrapper.m_task.m_lock.unlock();
37 FW_ASSERT(state != Task::State::NOT_STARTED);
38 // Run once start code
39 if (state == Task::State::STARTING) {
40 wrapper.m_task.m_lock.lock();
41 wrapper.m_task.m_state = Task::State::RUNNING;
42 wrapper.m_task.m_lock.unlock();
43 wrapper.m_task.onStart();
44 }
45
46 // Call user function supplying the user argument
47 wrapper.m_user_function(wrapper.m_user_argument);
48 }
49
50 void Task::TaskRoutineWrapper::invoke() {
51 TaskRoutineWrapper::run(this);
52 }
53
54 TaskRegistry* Task::s_taskRegistry = nullptr;
55 FwSizeType Task::s_numTasks = 0;
56 Mutex Task::s_taskMutex;
57
58 bool TaskInterface::isCooperative() {
59 return false;
60 }
61
62
5/5
✓ Branch 10 taken 1 times.
✓ Branch 20 taken 1 times.
✓ Branch 39 taken 40 times.
✓ Branch 40 taken 1 times.
✓ Branch 46 taken 1 times.
41 Task::Task() : m_wrapper(*this), m_handle_storage(), m_delegate(*TaskInterface::getDelegate(m_handle_storage)) {}
63
64 6 Task::~Task() {
65 // If a registry has been registered and the task has been started then remove task from the registry
66
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2 if ((Task::s_taskRegistry != nullptr) && this->m_registered) {
67 Task::s_taskRegistry->removeTask(this);
68 }
69 2 m_delegate.~TaskInterface();
70 4 }
71
72 void Task::suspend() {
73 this->suspend(Task::SuspensionType::UNINTENTIONAL);
74 }
75
76 Task::State Task::getState() const {
77 Task::State state;
78 this->m_lock.lock();
79 state = this->m_state;
80 this->m_lock.unlock();
81 return state;
82 }
83
84 Task::Status Task::start(const Fw::ConstStringBase& name,
85 const taskRoutine routine,
86 void* const arg,
87 const FwTaskPriorityType priority,
88 const ParamType stackSize,
89 const ParamType cpuAffinity,
90 const ParamType identifier) {
91 FW_ASSERT(routine != nullptr);
92 return this->start(
93 Task::Arguments(name, routine, arg, priority, stackSize, cpuAffinity, static_cast<FwTaskIdType>(identifier)));
94 }
95
96 Task::Status Task::start(const Task::Arguments& arguments) {
97 Task::init();
98 // init call above is to ensure singleton is initialized in a thread-safe
99 // manner and such that the address sanitizer does not inadvertently
100 // result in a stack overflow when multiple calls to getSingleton are made
101 // simultaneously from different threads. (As was observed in UT runs.)
102 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
103 FW_ASSERT(arguments.m_routine != nullptr);
104 this->m_name = arguments.m_name;
105 this->m_state = State::STARTING;
106
107 Arguments wrapped_arguments = arguments;
108 // Intercept routine and argument with the local wrapper
109 this->m_wrapper.m_user_function = arguments.m_routine;
110 this->m_wrapper.m_user_argument = arguments.m_routine_argument;
111 wrapped_arguments.m_routine = Task::TaskRoutineWrapper::run;
112 wrapped_arguments.m_routine_argument = &this->m_wrapper;
113
114 Task::Status status = this->m_delegate.start(wrapped_arguments);
115 if (status == Task::Status::OP_OK) {
116 Task::m_lock.lock();
117 this->m_priority = wrapped_arguments.m_priority;
118 Task::m_lock.unlock();
119 Task::s_taskMutex.lock();
120 Task::s_numTasks++;
121 Task::s_taskMutex.unlock();
122
123 // If a registry has been registered, register task to it
124 if (Task::s_taskRegistry) {
125 Task::s_taskRegistry->addTask(this);
126 this->m_registered = true;
127 }
128 }
129 return status;
130 }
131
132 void Task::onStart() {
133 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
134 this->m_delegate.onStart();
135 }
136
137 void Task::invokeRoutine() {
138 this->m_wrapper.invoke();
139 }
140
141 Task::Status Task::join() {
142 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
143 Task::Status status = Task::Status::INVALID_STATE;
144 Task::State state = this->getState();
145 if (state == Task::RUNNING || state == STARTING) {
146 status = this->m_delegate.join();
147 this->m_lock.lock();
148 if (status == Task::Status::OP_OK) {
149 this->m_state = Task::State::EXITED;
150 } else {
151 this->m_state = Task::State::UNKNOWN;
152 }
153 this->m_lock.unlock();
154 }
155 return status;
156 }
157
158 void Task::suspend(Task::SuspensionType suspensionType) {
159 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
160 this->m_delegate.suspend(suspensionType);
161 this->m_lock.lock();
162 this->m_state = (suspensionType == Task::SuspensionType::INTENTIONAL) ? State::SUSPENDED_INTENTIONALLY
163 : State::SUSPENDED_UNINTENTIONALLY;
164 this->m_lock.unlock();
165 }
166
167 void Task::resume() {
168 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
169 this->m_delegate.resume();
170 }
171
172 bool Task::isCooperative() {
173 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
174 return this->m_delegate.isCooperative();
175 }
176
177 TaskString Task::getName() {
178 Os::ScopeLock lock(this->m_lock);
179 return this->m_name;
180 }
181
182 FwTaskPriorityType Task::getPriority() {
183 Os::ScopeLock lock(this->m_lock);
184 return this->m_priority;
185 }
186
187 TaskHandle* Task::getHandle() {
188 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
189 return this->m_delegate.getHandle();
190 }
191
192 FwSizeType Task::getNumTasks() {
193 Task::s_taskMutex.lock();
194 FwSizeType num_tasks = Task::s_numTasks;
195 Task::s_taskMutex.unlock();
196 return num_tasks;
197 }
198
199 1 Os::TaskInterface::Status Task::_delay(const Fw::TimeInterval& interval) {
200 1 FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
201 1 return this->m_delegate._delay(interval);
202 }
203
204 1 Os::TaskInterface::Status Task::delay(const Fw::TimeInterval& interval) {
205 1 return Task::getSingleton()._delay(interval);
206 }
207
208 void Task::init() {
209 // Force trigger on the fly singleton setup
210 (void)Task::getSingleton();
211 }
212
213 1 Task& Task::getSingleton() {
214
3/7
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
1 static Task s_singleton;
215 1 return s_singleton;
216 }
217
218 void Task::registerTaskRegistry(TaskRegistry* registry) {
219 Task::s_taskRegistry = registry;
220 }
221 } // namespace Os
222