185 lines
5.7 KiB
C++
185 lines
5.7 KiB
C++
/**
|
|
* @file task.hpp
|
|
* @author Gustice
|
|
* @brief Task Wrapper
|
|
*
|
|
* @copyright Copyright (c) 2024
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include <cassert>
|
|
#include <string_view>
|
|
#include <Semaphore.hpp>
|
|
#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<Task *>(_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 <typename T>
|
|
class TaskT : public TaskBase {
|
|
private:
|
|
/// @brief Signature for task-implementation-callback
|
|
using TaskSignature = void (*)(T);
|
|
|
|
template <typename TS>
|
|
struct Param {
|
|
TaskSignature call;
|
|
const TS &arg;
|
|
};
|
|
Param<T> _param;
|
|
|
|
static void TaskFrame(void *_this) {
|
|
auto &task = *reinterpret_cast<TaskT *>(_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
|