| 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 |