|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <deque> |
| 4 | +#include <functional> |
| 5 | +#include <mutex> |
| 6 | +#include <set> |
| 7 | +#include <utility> |
| 8 | + |
| 9 | +#include "base_component.hpp" |
| 10 | +#include "task.hpp" |
| 11 | + |
| 12 | +namespace espp { |
| 13 | +/// This class implements a run queue for generic functions. |
| 14 | +/// |
| 15 | +/// The functions can be added to the queue with a priority. The functions are |
| 16 | +/// executed in the order of their priority, but the currently executing |
| 17 | +/// function will run to completion before the next function is executed. |
| 18 | +/// |
| 19 | +/// The RunQueue is implemented as a Task that runs the functions in the queue. |
| 20 | +/// The Task will run the highest priority function in the queue (if any) and |
| 21 | +/// will block indefinitely until either either a new function is added to the |
| 22 | +/// queue or the RunQueue is stopped. |
| 23 | +/// |
| 24 | +/// \note Function priorities are relative to each other and are only compared |
| 25 | +/// to other tasks in the specific RunQueue object. Different RunQueue |
| 26 | +/// objects may be active at the same time and the priorities of |
| 27 | +/// functions in one RunQueue are not compared to the priorities of |
| 28 | +/// functions in another. Similarly, the priorities of functions is not |
| 29 | +/// taken into account in the FreeRTOS scheduler - all functions in a |
| 30 | +/// given RunQueue will run with the same task priority as the |
| 31 | +/// RunQueue's task. |
| 32 | +/// |
| 33 | +/// Because the RunQueue is implemented as an espp::Task, you can customize |
| 34 | +/// its task configuration, such as priority, stack size, core id, etc. |
| 35 | +/// |
| 36 | +/// \note All functions within a RunQueue share the same task context, so you |
| 37 | +/// should set the stack size for the RunQueue accordingly. |
| 38 | +/// |
| 39 | +/// The RunQueue can be configured to start automatically or manually. If the |
| 40 | +/// RunQueue is configured to start automatically, it will start when it is |
| 41 | +/// constructed. If it is configured to start manually, it will not start until |
| 42 | +/// the start() method is called. |
| 43 | +/// |
| 44 | +/// The RunQueue is thread-safe and can be used from multiple threads. |
| 45 | +/// |
| 46 | +/// \warn Care should be taken to ensure that no functions in the queue are |
| 47 | +/// blocking on each other, and all functions in the queue must return |
| 48 | +/// (they cannot be infinite loops). |
| 49 | +/// |
| 50 | +/// \warn Functions take a long time to run may delay or prevent the execution |
| 51 | +/// of other functions in the queue. For this reason it's recommended to |
| 52 | +/// try to keep the functions as short-lived as possible, and to |
| 53 | +/// minimize the priorities of any functions which take longer to |
| 54 | +/// execute. |
| 55 | +/// |
| 56 | +/// \section runq_ex0 RunQueue Example |
| 57 | +/// \snippet runqueue_example.cpp runqueue example |
| 58 | +/// \section runq_ex1 Multiple RunQueues Example |
| 59 | +/// \snippet runqueue_example.cpp multiple runqueues example |
| 60 | +class RunQueue : public espp::BaseComponent { |
| 61 | +public: |
| 62 | + /// The type used to represent the priority of a function. |
| 63 | + using Priority = std::uint8_t; |
| 64 | + |
| 65 | + /// The minimum priority a function can have. |
| 66 | + static constexpr int MIN_PRIORITY = std::numeric_limits<Priority>::min(); |
| 67 | + |
| 68 | + /// The maximum priority a function can have. |
| 69 | + static constexpr int MAX_PRIORITY = std::numeric_limits<Priority>::max(); |
| 70 | + |
| 71 | + /// A function that takes no arguments and returns void. |
| 72 | + using Function = std::function<void(void)>; |
| 73 | + // typedef std::function<void(void)> Function; |
| 74 | + |
| 75 | + /// A pair of a priority and a function. |
| 76 | + struct PriorityFunction { |
| 77 | + Priority priority; ///< The priority of the function. |
| 78 | + Function function; ///< The function. |
| 79 | + }; |
| 80 | + |
| 81 | + /// Less than operator for PriorityFunction. |
| 82 | + /// \param lhs The left hand side of the comparison. |
| 83 | + /// \param rhs The right hand side of the comparison. |
| 84 | + /// \return True if the right hand side has a greater priority. |
| 85 | + friend bool operator<(const PriorityFunction &lhs, const PriorityFunction &rhs) { |
| 86 | + return lhs.priority < rhs.priority; |
| 87 | + } |
| 88 | + |
| 89 | + /// Greater than operator for PriorityFunction. |
| 90 | + /// \param lhs The left hand side of the comparison. |
| 91 | + /// \param rhs The right hand side of the comparison. |
| 92 | + /// \return True if the left hand side has a greater priority. |
| 93 | + friend bool operator>(const PriorityFunction &lhs, const PriorityFunction &rhs) { |
| 94 | + return lhs.priority > rhs.priority; |
| 95 | + } |
| 96 | + |
| 97 | + /// Configuration struct for the RunQueue |
| 98 | + struct Config { |
| 99 | + bool auto_start = true; ///< Whether the RunQueue should start automatically. |
| 100 | + espp::Task::BaseConfig task_config = {}; ///< The configuration for the runner task. |
| 101 | + espp::Logger::Verbosity log_level = |
| 102 | + espp::Logger::Verbosity::WARN; ///< The log level for the RunQueue. |
| 103 | + }; |
| 104 | + |
| 105 | + /// Construct a RunQueue. |
| 106 | + /// \param config The configuration for the RunQueue. |
| 107 | + explicit RunQueue(const Config &config); |
| 108 | + |
| 109 | + /// Destroy the RunQueue. |
| 110 | + ~RunQueue(); |
| 111 | + |
| 112 | + /// Get the number of functions in the queue. |
| 113 | + /// \return The number of functions in the queue. |
| 114 | + std::size_t queue_size() const; |
| 115 | + |
| 116 | + /// Get whether the run queue is running. |
| 117 | + /// \return True if the run queue is running. |
| 118 | + bool is_running() const; |
| 119 | + |
| 120 | + /// Start the run queue. |
| 121 | + void start(); |
| 122 | + |
| 123 | + /// Stop the run queue. |
| 124 | + void stop(); |
| 125 | + |
| 126 | + /// Add a function to the queue. |
| 127 | + /// \param function The function to add. |
| 128 | + /// \param priority The priority of the function. Defaults to MIN_PRIORITY. |
| 129 | + void add_function(const Function &function, Priority priority = MIN_PRIORITY); |
| 130 | + |
| 131 | +protected: |
| 132 | + /// Manage the run queue. |
| 133 | + /// \details This function is called by the runner task to manage the run |
| 134 | + /// queue. It will run the highest priority function in the queue |
| 135 | + /// (if any) and will return true if there are more functions to |
| 136 | + /// run. |
| 137 | + /// \return True if there are more functions to run. |
| 138 | + bool manage_queue(); |
| 139 | + |
| 140 | + bool task_fn(std::mutex &m, std::condition_variable &cv, bool &task_notified); |
| 141 | + |
| 142 | + std::unique_ptr<espp::Task> runner_; |
| 143 | + std::mutex queue_mutex_; |
| 144 | + std::condition_variable queue_cv_; |
| 145 | + bool queue_notified_ = false; |
| 146 | + std::multiset<PriorityFunction> priority_function_queue_; |
| 147 | +}; // class RunQueue |
| 148 | +} // namespace espp |
0 commit comments