From 5ac4d3949cd7430e0d4c994bbc528e8e4fa91337 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 22 May 2023 15:23:51 +1000 Subject: Generalise worker tasks, and centralise task priorities + stacks Includes making the display driver use a worker task for flushes, so that our double buffering actually does something useful /facepalm --- src/tasks/tasks.hpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) (limited to 'src/tasks/tasks.hpp') diff --git a/src/tasks/tasks.hpp b/src/tasks/tasks.hpp index 47668aea..9f37131e 100644 --- a/src/tasks/tasks.hpp +++ b/src/tasks/tasks.hpp @@ -1,7 +1,107 @@ #pragma once +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" +#include "freertos/projdefs.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "span.hpp" + +namespace tasks { + +/* + * Enumeration of every task (basically a thread) started within the firmware. + * These are centralised so that it is easier to reason about the relative + * priorities of tasks, as well as the amount and location of memory allocated + * to each one. + */ +enum class Type { + // The main UI task. This runs the LVGL main loop. + kUi, + // Task for flushing graphics buffers to the display. + kUiFlush, + // The main audio pipeline task. + kAudio, + // Task for flushing PCM samples to the current output. + kAudioDrain, + // Task for running database queries. + kDatabase, +}; + +template +auto Name() -> std::string; +template +auto AllocateStack() -> cpp::span; +template +auto Priority() -> UBaseType_t; +template +auto WorkerQueueSize() -> std::size_t; + +auto PersistentMain(void* fn) -> void; + +template +auto StartPersistent(const std::function& fn) -> void { + StaticTask_t* task_buffer = new StaticTask_t; + cpp::span stack = AllocateStack(); + xTaskCreateStatic(&PersistentMain, Name().c_str(), stack.size(), + new std::function(fn), Priority(), + stack.data(), task_buffer); +} + +class Worker { + private: + Worker(const std::string& name, + cpp::span stack, + std::size_t queue_size, + UBaseType_t priority); + + StackType_t* stack_; + QueueHandle_t queue_; + std::atomic is_task_running_; + StaticTask_t task_buffer_; + TaskHandle_t task_; + + struct WorkItem { + std::function* fn; + bool quit; + }; + + public: + template + static auto Start() -> Worker* { + return new Worker(Name(), AllocateStack(), WorkerQueueSize(), + Priority()); + } + + static auto Main(void* instance); + + /* + * Schedules the given function to be executed on the worker task, and + * asynchronously returns the result as a future. + */ + template + auto Dispatch(const std::function& fn) -> std::future { + std::shared_ptr> promise = + std::make_shared>(); + WorkItem item{ + .fn = new std::function([=]() { promise->set_value(std::invoke(fn)); }), + .quit = false, + }; + xQueueSend(queue_, &item, portMAX_DELAY); + return promise->get_future(); + } + + ~Worker(); +}; + +/* Specialisation of Evaluate for functions that return nothing. */ +template <> +auto Worker::Dispatch(const std::function& fn) -> std::future; -extern const UBaseType_t kTaskPriorityLvgl; -extern const UBaseType_t kTaskPriorityAudioPipeline; -extern const UBaseType_t kTaskPriorityAudioDrain; +} // namespace tasks -- cgit v1.2.3