/** * @file task.hpp * @author Gustice * @brief Task Wrapper * * @copyright Copyright (c) 2024 */ #pragma once #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include #include #include #define DEFAULT_STACKSIZE 0x1000uL namespace fos { /** * @brief Base class for tasks */ class TaskBase { protected: TaskHandle_t _handle; static void disposeFrameConditionally() { if constexpr (INCLUDE_vTaskDelete != 0) { vTaskDelete(NULL); // NOTE: This only compiles if symbol INCLUDE_vTaskDelete is defined } } Semaphore _finished; public: /// @brief Priority type (redefinition for convenience) using Priority = unsigned short; /// @brief Tick type (redefinition for convenience) using Tick = TickType_t; /// @brief Definition for endless waittime static constexpr Tick MaxDelay = portMAX_DELAY; /// @brief Maximum delay static constexpr int MaximumDelay = portMAX_DELAY; /// @brief Default Stack size static constexpr size_t DefaultStackSize = DEFAULT_STACKSIZE; /// @brief Lowest priority for new tasks static constexpr Priority MIN_PRIORITY = tskIDLE_PRIORITY + 1; /// @brief Highest priority for new tasks static constexpr Priority MAX_PRIORITY = configMAX_PRIORITIES; /// @brief Kill a task /// @note It can lead to undefined behaviour when one task deletes another task on a /// multi-core system. Hence a task should clear itself. void kill() { // NOTE: this leads to undefined behavior if interferes with Destructor vTaskDelete(_handle); } /// @brief Join threads /// @param timeout timeout to wait for join else kill /// @return joined successfully bool join(Tick timeout = MaxDelay) { auto success = _finished.take(timeout); if(not success) { kill(); } return success; } /// @brief Get task state /// @return FreeRTOS Task state eTaskState getState() { return eTaskGetState(_handle); } /// @brief Get task handle /// @return FreeRTOS Task handle TaskHandle_t getHandle() { return _handle; } /// @brief Delay function /// @param msToDelay Milliseconds to delay /// @note The minimal resolution for the delay is tick based static void delayMs(unsigned int msToDelay) { // pdMS_TO_TICKS(_timeout) vTaskDelay(msToDelay / portTICK_PERIOD_MS); } /// @brief Delay function /// @param ticksToDelay Ticks to delay static void delayTicks(unsigned int ticksToDelay) { vTaskDelay(ticksToDelay); } virtual ~TaskBase() { kill(); } }; /** * @brief Simple task without task parameter */ class Task : public TaskBase { private: /// @brief Signature for task-implementation-callback using TaskSignature = void (*)(); static void TaskFrame(void *_this) { auto &task = *reinterpret_cast(_this); task._call(); // Tasks shall not return, however if this happens dispose task disposeFrameConditionally(); task._finished.give(); } TaskSignature _call; public: /// @brief Constructor for task without passed argument /// @param call Function pointer to implementation /// @param name Name of task /// @param stack Stacksize if not default /// @param prio Priority if not default Task(TaskSignature call, const char *name, size_t stack = DefaultStackSize, Priority prio = MIN_PRIORITY) : _call{call} { auto ret = xTaskCreate(TaskFrame, name, stack, this, MIN_PRIORITY, &_handle); assert(pdPASS == ret && "Task create must finish successfully"); } }; /** * @brief Task with task parameter * @tparam T Type of task parameter on call */ template class TaskT : public TaskBase { private: /// @brief Signature for task-implementation-callback using TaskSignature = void (*)(T); template struct Param { TaskSignature call; const TS &arg; }; Param _param; static void TaskFrame(void *_this) { auto &task = *reinterpret_cast(_this); auto ¶m = task._param; param.call(param.arg); // Tasks shall not return, however if this happens dispose task disposeFrameConditionally(); task._finished.give(); } public: /// @brief Constructor for task with passed argument /// @param call Function pointer to implementation /// @param param Parameter ot pass task implementation /// @param name Name of task /// @param stack Stacksize if not default /// @param prio Priority if not default TaskT(TaskSignature call, const T ¶m, const char *name, size_t stack = DefaultStackSize, Priority prio = MIN_PRIORITY) : _param{call, param} { auto ret = xTaskCreate(TaskFrame, name, stack, this, MIN_PRIORITY, &_handle); assert(pdPASS == ret && "Task create must finish successfully"); } }; class Timing { public: static uint32_t getTickCount() { return xTaskGetTickCount(); } }; } // namespace fos