/* * Copyright 2023 jacqueline * * SPDX-License-Identifier: GPL-3.0-only */ #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, // TODO. kFileStreamer, // The main audio pipeline task. kAudio, // TODO kMixer, // Task for running database queries. kDatabase, // Task for internal database operations kDatabaseBackground, }; 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); } template auto StartPersistent(BaseType_t core, const std::function& fn) -> void { StaticTask_t* task_buffer = new StaticTask_t; cpp::span stack = AllocateStack(); xTaskCreateStaticPinnedToCore(&PersistentMain, Name().c_str(), stack.size(), new std::function(fn), Priority(), stack.data(), task_buffer, core); } 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(); Worker(const Worker&) = delete; Worker& operator=(const Worker&) = delete; }; /* Specialisation of Evaluate for functions that return nothing. */ template <> auto Worker::Dispatch(const std::function& fn) -> std::future; } // namespace tasks