| 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 | 22 | 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 | 22 | const FwTaskIdType identifier) | |
| 17 | 22 | : m_name(name), | |
| 18 | 22 | m_routine(routine), | |
| 19 | 22 | m_routine_argument(routine_argument), | |
| 20 | 22 | m_priority(priority), | |
| 21 | 22 | m_stackSize(stackSize), | |
| 22 | 22 | m_cpuAffinity(cpuAffinity), | |
| 23 | 22 | m_identifier(identifier) { | |
| 24 | 22 | FW_ASSERT(routine != nullptr); | |
| 25 | 22 | } | |
| 26 | |||
| 27 | 23 | Task::TaskRoutineWrapper::TaskRoutineWrapper(Task& self) : m_task(self) {} | |
| 28 | |||
| 29 | 22 | void Task::TaskRoutineWrapper::run(void* wrapper_pointer) { | |
| 30 | 22 | FW_ASSERT(wrapper_pointer != nullptr); | |
| 31 | 22 | TaskRoutineWrapper& wrapper = *reinterpret_cast<TaskRoutineWrapper*>(wrapper_pointer); | |
| 32 | 22 | FW_ASSERT(wrapper.m_user_function != nullptr); | |
| 33 | |||
| 34 | 22 | wrapper.m_task.m_lock.lock(); | |
| 35 | 22 | Task::State state = wrapper.m_task.m_state; | |
| 36 | 22 | wrapper.m_task.m_lock.unlock(); | |
| 37 | 22 | FW_ASSERT(state != Task::State::NOT_STARTED); | |
| 38 | // Run once start code | ||
| 39 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | if (state == Task::State::STARTING) { |
| 40 | 22 | wrapper.m_task.m_lock.lock(); | |
| 41 | 22 | wrapper.m_task.m_state = Task::State::RUNNING; | |
| 42 | 22 | wrapper.m_task.m_lock.unlock(); | |
| 43 | 22 | wrapper.m_task.onStart(); | |
| 44 | } | ||
| 45 | |||
| 46 | // Call user function supplying the user argument | ||
| 47 | 22 | wrapper.m_user_function(wrapper.m_user_argument); | |
| 48 | 22 | } | |
| 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 | 6070 | bool TaskInterface::isCooperative() { | |
| 59 | 6070 | return false; | |
| 60 | } | ||
| 61 | |||
| 62 |
5/5✓ Branch 2 taken 23 times.
✓ Branch 5 taken 23 times.
✓ Branch 8 taken 920 times.
✓ Branch 9 taken 23 times.
✓ Branch 11 taken 23 times.
|
943 | Task::Task() : m_wrapper(*this), m_handle_storage(), m_delegate(*TaskInterface::getDelegate(m_handle_storage)) {} |
| 63 | |||
| 64 | 46 | Task::~Task() { | |
| 65 | // If a registry has been registered and the task has been started then remove task from the registry | ||
| 66 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
46 | if ((Task::s_taskRegistry != nullptr) && this->m_registered) { |
| 67 | ✗ | Task::s_taskRegistry->removeTask(this); | |
| 68 | } | ||
| 69 | 46 | m_delegate.~TaskInterface(); | |
| 70 | 46 | } | |
| 71 | |||
| 72 | ✗ | void Task::suspend() { | |
| 73 | ✗ | this->suspend(Task::SuspensionType::UNINTENTIONAL); | |
| 74 | ✗ | } | |
| 75 | |||
| 76 | 24 | Task::State Task::getState() const { | |
| 77 | Task::State state; | ||
| 78 | 24 | this->m_lock.lock(); | |
| 79 | 24 | state = this->m_state; | |
| 80 | 24 | this->m_lock.unlock(); | |
| 81 | 24 | 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 | 22 | Task::Status Task::start(const Task::Arguments& arguments) { | |
| 97 |
1/1✓ Branch 1 taken 22 times.
|
22 | 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 | 22 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 103 | 22 | FW_ASSERT(arguments.m_routine != nullptr); | |
| 104 |
1/1✓ Branch 1 taken 22 times.
|
22 | this->m_name = arguments.m_name; |
| 105 | 22 | this->m_state = State::STARTING; | |
| 106 | |||
| 107 |
1/1✓ Branch 1 taken 22 times.
|
22 | Arguments wrapped_arguments = arguments; |
| 108 | // Intercept routine and argument with the local wrapper | ||
| 109 | 22 | this->m_wrapper.m_user_function = arguments.m_routine; | |
| 110 | 22 | this->m_wrapper.m_user_argument = arguments.m_routine_argument; | |
| 111 | 22 | wrapped_arguments.m_routine = Task::TaskRoutineWrapper::run; | |
| 112 | 22 | wrapped_arguments.m_routine_argument = &this->m_wrapper; | |
| 113 | |||
| 114 |
1/1✓ Branch 1 taken 22 times.
|
22 | Task::Status status = this->m_delegate.start(wrapped_arguments); |
| 115 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | if (status == Task::Status::OP_OK) { |
| 116 |
1/1✓ Branch 1 taken 22 times.
|
22 | Task::m_lock.lock(); |
| 117 | 22 | this->m_priority = wrapped_arguments.m_priority; | |
| 118 |
1/1✓ Branch 1 taken 22 times.
|
22 | Task::m_lock.unlock(); |
| 119 |
1/1✓ Branch 1 taken 22 times.
|
22 | Task::s_taskMutex.lock(); |
| 120 | 22 | Task::s_numTasks++; | |
| 121 |
1/1✓ Branch 1 taken 22 times.
|
22 | Task::s_taskMutex.unlock(); |
| 122 | |||
| 123 | // If a registry has been registered, register task to it | ||
| 124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (Task::s_taskRegistry) { |
| 125 | ✗ | Task::s_taskRegistry->addTask(this); | |
| 126 | ✗ | this->m_registered = true; | |
| 127 | } | ||
| 128 | } | ||
| 129 | 22 | return status; | |
| 130 | 22 | } | |
| 131 | |||
| 132 | 22 | void Task::onStart() { | |
| 133 | 22 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 134 | 22 | this->m_delegate.onStart(); | |
| 135 | 22 | } | |
| 136 | |||
| 137 | ✗ | void Task::invokeRoutine() { | |
| 138 | ✗ | this->m_wrapper.invoke(); | |
| 139 | ✗ | } | |
| 140 | |||
| 141 | 22 | Task::Status Task::join() { | |
| 142 | 22 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 143 | 22 | Task::Status status = Task::Status::INVALID_STATE; | |
| 144 | 22 | Task::State state = this->getState(); | |
| 145 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
22 | if (state == Task::RUNNING || state == STARTING) { |
| 146 | 22 | status = this->m_delegate.join(); | |
| 147 | 22 | this->m_lock.lock(); | |
| 148 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | if (status == Task::Status::OP_OK) { |
| 149 | 22 | this->m_state = Task::State::EXITED; | |
| 150 | } else { | ||
| 151 | ✗ | this->m_state = Task::State::UNKNOWN; | |
| 152 | } | ||
| 153 | 22 | this->m_lock.unlock(); | |
| 154 | } | ||
| 155 | 22 | 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 | 6067 | bool Task::isCooperative() { | |
| 173 | 6067 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 174 | 6067 | return this->m_delegate.isCooperative(); | |
| 175 | } | ||
| 176 | |||
| 177 | 2 | TaskString Task::getName() { | |
| 178 |
1/1✓ Branch 1 taken 2 times.
|
2 | Os::ScopeLock lock(this->m_lock); |
| 179 |
1/1✓ Branch 1 taken 2 times.
|
4 | return this->m_name; |
| 180 | 2 | } | |
| 181 | |||
| 182 | ✗ | FwTaskPriorityType Task::getPriority() { | |
| 183 | ✗ | Os::ScopeLock lock(this->m_lock); | |
| 184 | ✗ | return this->m_priority; | |
| 185 | ✗ | } | |
| 186 | |||
| 187 | 22 | TaskHandle* Task::getHandle() { | |
| 188 | 22 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 189 | 22 | 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 | 4798 | Os::TaskInterface::Status Task::_delay(const Fw::TimeInterval& interval) { | |
| 200 | 4798 | FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0])); | |
| 201 | 4798 | return this->m_delegate._delay(interval); | |
| 202 | } | ||
| 203 | |||
| 204 | 4798 | Os::TaskInterface::Status Task::delay(const Fw::TimeInterval& interval) { | |
| 205 | 4798 | return Task::getSingleton()._delay(interval); | |
| 206 | } | ||
| 207 | |||
| 208 | 23 | void Task::init() { | |
| 209 | // Force trigger on the fly singleton setup | ||
| 210 | 23 | (void)Task::getSingleton(); | |
| 211 | 23 | } | |
| 212 | |||
| 213 | 4821 | Task& Task::getSingleton() { | |
| 214 |
4/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4820 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
4821 | static Task s_singleton; |
| 215 | 4821 | return s_singleton; | |
| 216 | } | ||
| 217 | |||
| 218 | ✗ | void Task::registerTaskRegistry(TaskRegistry* registry) { | |
| 219 | ✗ | Task::s_taskRegistry = registry; | |
| 220 | ✗ | } | |
| 221 | } // namespace Os | ||
| 222 |