diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-05-22 15:23:51 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-05-22 15:23:51 +1000 |
| commit | 5ac4d3949cd7430e0d4c994bbc528e8e4fa91337 (patch) | |
| tree | dcd30c252e45afa20f084e1a58cbabd861ca299c /src/tasks/tasks.hpp | |
| parent | b320a6a863cf1c10dc79254af41f573730935564 (diff) | |
| download | tangara-fw-5ac4d3949cd7430e0d4c994bbc528e8e4fa91337.tar.gz | |
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
Diffstat (limited to 'src/tasks/tasks.hpp')
| -rw-r--r-- | src/tasks/tasks.hpp | 106 |
1 files changed, 103 insertions, 3 deletions
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 <atomic> +#include <functional> +#include <future> +#include <memory> +#include <string> + +#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 <Type t> +auto Name() -> std::string; +template <Type t> +auto AllocateStack() -> cpp::span<StackType_t>; +template <Type t> +auto Priority() -> UBaseType_t; +template <Type t> +auto WorkerQueueSize() -> std::size_t; + +auto PersistentMain(void* fn) -> void; + +template <Type t> +auto StartPersistent(const std::function<void(void)>& fn) -> void { + StaticTask_t* task_buffer = new StaticTask_t; + cpp::span<StackType_t> stack = AllocateStack<t>(); + xTaskCreateStatic(&PersistentMain, Name<t>().c_str(), stack.size(), + new std::function<void(void)>(fn), Priority<t>(), + stack.data(), task_buffer); +} + +class Worker { + private: + Worker(const std::string& name, + cpp::span<StackType_t> stack, + std::size_t queue_size, + UBaseType_t priority); + + StackType_t* stack_; + QueueHandle_t queue_; + std::atomic<bool> is_task_running_; + StaticTask_t task_buffer_; + TaskHandle_t task_; + + struct WorkItem { + std::function<void(void)>* fn; + bool quit; + }; + + public: + template <Type t> + static auto Start() -> Worker* { + return new Worker(Name<t>(), AllocateStack<t>(), WorkerQueueSize<t>(), + Priority<t>()); + } + + 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 <typename T> + auto Dispatch(const std::function<T(void)>& fn) -> std::future<T> { + std::shared_ptr<std::promise<T>> promise = + std::make_shared<std::promise<T>>(); + 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<void(void)>& fn) -> std::future<void>; -extern const UBaseType_t kTaskPriorityLvgl; -extern const UBaseType_t kTaskPriorityAudioPipeline; -extern const UBaseType_t kTaskPriorityAudioDrain; +} // namespace tasks |
