#include #include #include #include namespace Fw { class ActiveComponentExitSerializableBuffer : public Fw::SerializeBufferBase { public: DEPRECATED(FwSizeType getBuffCapacity() const, "Use getCapacity() instead"); FwSizeType getCapacity() const { return sizeof(m_buff); } U8* getBuffAddr() { return m_buff; } const U8* getBuffAddr() const { return m_buff; } private: U8 m_buff[sizeof(ActiveComponentBase::ACTIVE_COMPONENT_EXIT)]; }; ActiveComponentBase::ActiveComponentBase(const char* name) : QueuedComponentBase(name) {} ActiveComponentBase::~ActiveComponentBase() {} void ActiveComponentBase::init(FwEnumStoreType instance) { QueuedComponentBase::init(instance); } #if FW_OBJECT_TO_STRING == 1 const char* ActiveComponentBase::getToStringFormatString() { return "ActComp: %s"; } #endif void ActiveComponentBase::start(FwTaskPriorityType priority, FwSizeType stackSize, FwSizeType cpuAffinity, FwTaskIdType identifier) { Os::TaskString taskName; #if FW_OBJECT_NAMES == 1 taskName = this->getObjName(); #else (void)taskName.format("ActComp_%" PRI_FwSizeType, Os::Task::getNumTasks()); #endif // Cooperative threads tasks externalize the task loop, and as such use the state machine as their task function // Standard multithreading tasks use the task loop to respectively call the state machine Os::Task::taskRoutine routine = (m_task.isCooperative()) ? this->s_taskStateMachine : this->s_taskLoop; Os::Task::Arguments arguments(taskName, routine, this, priority, stackSize, cpuAffinity, identifier); Os::Task::Status status = this->m_task.start(arguments); FW_ASSERT(status == Os::Task::Status::OP_OK, static_cast(status)); } void ActiveComponentBase::exit() { ActiveComponentExitSerializableBuffer exitBuff; SerializeStatus stat = exitBuff.serializeFrom(static_cast(ACTIVE_COMPONENT_EXIT)); FW_ASSERT(FW_SERIALIZE_OK == stat, static_cast(stat)); (void)this->m_queue.send(exitBuff, 0, Os::Queue::BlockingType::NONBLOCKING); } Os::Task::Status ActiveComponentBase::join() { return this->m_task.join(); } Os::Task::Status ActiveComponentBase::join(void** pointer) { return this->m_task.join(); } void ActiveComponentBase::s_taskStateMachine(void* component_pointer) { FW_ASSERT(component_pointer != nullptr); // cast void* back to active component ActiveComponentBase* component = static_cast(component_pointer); // Each invocation of this function runs a single stage of the thread lifecycle. This has moved the thread // while loop to the top level such that it can be replaced by something else (e.g. cooperative thread // dispatcher) and is not intrinsic to this code. switch (component->m_stage) { // The first stage the active component triggers the "preamble" call before moving into the dispatching // stage of the component thread. case Lifecycle::CREATED: component->preamble(); component->m_stage = Lifecycle::DISPATCHING; break; // The second stage of the active component triggers the dispatching loop dispatching messages until an // exit message is received. case Lifecycle::DISPATCHING: if (component->dispatch() == MsgDispatchStatus::MSG_DISPATCH_EXIT) { component->m_stage = Lifecycle::FINALIZING; } break; // The second-to-last stage is where the finalizer is called. This will transition to the final stage // automatically after the finalizer is called case Lifecycle::FINALIZING: component->finalizer(); component->m_stage = Lifecycle::DONE; break; // The last stage does nothing, cooperative tasks live here forever, threaded tasks exit on this condition case Lifecycle::DONE: break; default: FW_ASSERT(0); break; } } void ActiveComponentBase::s_taskLoop(void* component_pointer) { FW_ASSERT(component_pointer != nullptr); ActiveComponentBase* component = static_cast(component_pointer); // A non-cooperative task switching implementation is just a while-loop around the active component // state-machine. Here the while loop is at top-level. while (component->m_stage != ActiveComponentBase::Lifecycle::DONE) { ActiveComponentBase::s_taskStateMachine(component); } } ActiveComponentBase::MsgDispatchStatus ActiveComponentBase::dispatch() { // Cooperative tasks should return rather than block when no messages are available if (this->m_task.isCooperative() and m_queue.getMessagesAvailable() == 0) { return MsgDispatchStatus::MSG_DISPATCH_EMPTY; } return this->doDispatch(); } void ActiveComponentBase::preamble() {} void ActiveComponentBase::finalizer() {} } // namespace Fw