fprime/Os/Task.cpp
Ian Brault 2b65cc83cf
Add new Fw::ConstStringBase type for strings backed by immutable string literals (#4269)
* Add new Fw::StringBase type StaticString for strings backed my immutable literals

* Spellcheck fix

* Add disclaimer comment about use of StaticString

* Refactor the StringBase interface into an immutable ConstStringBase abstract base class and the now mutable StringBase class

* Rename StaticString to ConstExternalString and inherit from ConstStringBase

* Fix typo

* Change references from StringBase to ConstStringBase where applicable

* Updates following review meeting: add missing deserialize function and add new error status, move length function implementation into ConstStringBase so it is not pure virtual

* Clang format fix

* Additional clang-format fixes

* Fix the copy-assignment operator for StringBase not being correctly evaluated

* Clang format fix

* Explicitly delete the Serializable assignment operator and provide a skeleton implementation for RawTimeInterface to appease the compiler

* Revert "Explicitly delete the Serializable assignment operator and provide a skeleton implementation for RawTimeInterface to appease the compiler"

This reverts commit 086d7bcd3ca9c4f6e553d7fc34d0d126a69a165b.

* Move ConstStringBase to separate hpp/cpp files, plus other pull request feedback

* Clang format fix

* Update length implementation for ConstStringBase and ConstExternalString

* Improved asserts in ConstExternalString constructor

Co-authored-by: Rob Bocchino <bocchino@icloud.com>

* Fixed ConstStringBase length implementation

Co-authored-by: Rob Bocchino <bocchino@icloud.com>

* Clang format fix

* Add some UTs for ConstExternalString, fix non-overridden interfaces, and fix ConstStringBase::maxLength asserting for zero capacity strings

* Spell-check fix for ConstExternalString UTs

* Revise length implementation in ConstStringBase

If the capacity is zero, return zero

* Format

---------

Co-authored-by: Ian Brault <ian.r.brault@jpl.nasa.gov>
Co-authored-by: Rob Bocchino <bocchino@icloud.com>
Co-authored-by: Rob Bocchino <bocchino@jpl.nasa.gov>
Co-authored-by: M Starch <LeStarch@googlemail.com>
2025-11-07 09:50:05 -08:00

217 lines
7.3 KiB
C++

// ======================================================================
// \title Os/Task.cpp
// \brief common function implementation for Os::Task
// ======================================================================
#include <Fw/Types/Assert.hpp>
#include <Os/Task.hpp>
namespace Os {
TaskInterface::Arguments::Arguments(const Fw::ConstStringBase& name,
const Os::TaskInterface::taskRoutine routine,
void* const routine_argument,
const FwTaskPriorityType priority,
const FwSizeType stackSize,
const FwSizeType cpuAffinity,
const FwTaskIdType identifier)
: m_name(name),
m_routine(routine),
m_routine_argument(routine_argument),
m_priority(priority),
m_stackSize(stackSize),
m_cpuAffinity(cpuAffinity),
m_identifier(identifier) {
FW_ASSERT(routine != nullptr);
}
Task::TaskRoutineWrapper::TaskRoutineWrapper(Task& self) : m_task(self) {}
void Task::TaskRoutineWrapper::run(void* wrapper_pointer) {
FW_ASSERT(wrapper_pointer != nullptr);
TaskRoutineWrapper& wrapper = *reinterpret_cast<TaskRoutineWrapper*>(wrapper_pointer);
FW_ASSERT(wrapper.m_user_function != nullptr);
wrapper.m_task.m_lock.lock();
Task::State state = wrapper.m_task.m_state;
wrapper.m_task.m_lock.unlock();
FW_ASSERT(state != Task::State::NOT_STARTED);
// Run once start code
if (state == Task::State::STARTING) {
wrapper.m_task.m_lock.lock();
wrapper.m_task.m_state = Task::State::RUNNING;
wrapper.m_task.m_lock.unlock();
wrapper.m_task.onStart();
}
// Call user function supplying the user argument
wrapper.m_user_function(wrapper.m_user_argument);
}
void Task::TaskRoutineWrapper::invoke() {
TaskRoutineWrapper::run(this);
}
TaskRegistry* Task::s_taskRegistry = nullptr;
FwSizeType Task::s_numTasks = 0;
Mutex Task::s_taskMutex;
bool TaskInterface::isCooperative() {
return false;
}
Task::Task() : m_wrapper(*this), m_handle_storage(), m_delegate(*TaskInterface::getDelegate(m_handle_storage)) {}
Task::~Task() {
// If a registry has been registered and the task has been started then remove task from the registry
if ((Task::s_taskRegistry != nullptr) && this->m_registered) {
Task::s_taskRegistry->removeTask(this);
}
m_delegate.~TaskInterface();
}
void Task::suspend() {
this->suspend(Task::SuspensionType::UNINTENTIONAL);
}
Task::State Task::getState() {
Task::State state;
this->m_lock.lock();
state = this->m_state;
this->m_lock.unlock();
return state;
}
Task::Status Task::start(const Fw::ConstStringBase& name,
const taskRoutine routine,
void* const arg,
const FwTaskPriorityType priority,
const ParamType stackSize,
const ParamType cpuAffinity,
const ParamType identifier) {
FW_ASSERT(routine != nullptr);
return this->start(
Task::Arguments(name, routine, arg, priority, stackSize, cpuAffinity, static_cast<FwTaskIdType>(identifier)));
}
Task::Status Task::start(const Task::Arguments& arguments) {
Task::init();
// init call above is to ensure singleton is initialized in a thread-safe
// manner and such that the address sanitizer does not inadvertently
// result in a stack overflow when multiple calls to getSingleton are made
// simultaneously from different threads. (As was observed in UT runs.)
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
FW_ASSERT(arguments.m_routine != nullptr);
this->m_name = arguments.m_name;
this->m_state = State::STARTING;
Arguments wrapped_arguments = arguments;
// Intercept routine and argument with the local wrapper
this->m_wrapper.m_user_function = arguments.m_routine;
this->m_wrapper.m_user_argument = arguments.m_routine_argument;
wrapped_arguments.m_routine = Task::TaskRoutineWrapper::run;
wrapped_arguments.m_routine_argument = &this->m_wrapper;
Task::Status status = this->m_delegate.start(wrapped_arguments);
if (status == Task::Status::OP_OK) {
Task::m_lock.lock();
this->m_priority = wrapped_arguments.m_priority;
Task::m_lock.unlock();
Task::s_taskMutex.lock();
Task::s_numTasks++;
Task::s_taskMutex.unlock();
// If a registry has been registered, register task to it
if (Task::s_taskRegistry) {
Task::s_taskRegistry->addTask(this);
this->m_registered = true;
}
}
return status;
}
void Task::onStart() {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
this->m_delegate.onStart();
}
void Task::invokeRoutine() {
this->m_wrapper.invoke();
}
Task::Status Task::join() {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
Task::Status status = Task::Status::INVALID_STATE;
Task::State state = this->getState();
if (state == Task::RUNNING || state == STARTING) {
status = this->m_delegate.join();
this->m_lock.lock();
if (status == Task::Status::OP_OK) {
this->m_state = Task::State::EXITED;
} else {
this->m_state = Task::State::UNKNOWN;
}
this->m_lock.unlock();
}
return status;
}
void Task::suspend(Task::SuspensionType suspensionType) {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
this->m_delegate.suspend(suspensionType);
this->m_lock.lock();
this->m_state = (suspensionType == Task::SuspensionType::INTENTIONAL) ? State::SUSPENDED_INTENTIONALLY
: State::SUSPENDED_UNINTENTIONALLY;
this->m_lock.unlock();
}
void Task::resume() {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
this->m_delegate.resume();
}
bool Task::isCooperative() {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
return this->m_delegate.isCooperative();
}
FwTaskPriorityType Task::getPriority() {
Os::ScopeLock lock(this->m_lock);
return this->m_priority;
}
TaskHandle* Task::getHandle() {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
return this->m_delegate.getHandle();
}
FwSizeType Task::getNumTasks() {
Task::s_taskMutex.lock();
FwSizeType num_tasks = Task::s_numTasks;
Task::s_taskMutex.unlock();
return num_tasks;
}
Os::TaskInterface::Status Task::_delay(Fw::TimeInterval interval) {
FW_ASSERT(&this->m_delegate == reinterpret_cast<TaskInterface*>(&this->m_handle_storage[0]));
return this->m_delegate._delay(interval);
}
Os::TaskInterface::Status Task::delay(Fw::TimeInterval interval) {
return Task::getSingleton()._delay(interval);
}
void Task::init() {
// Force trigger on the fly singleton setup
(void)Task::getSingleton();
}
Task& Task::getSingleton() {
static Task s_singleton;
return s_singleton;
}
void Task::registerTaskRegistry(TaskRegistry* registry) {
Task::s_taskRegistry = registry;
}
} // namespace Os