FastLED 3.10.6
Loading...
Searching...
No Matches
executor.cpp.hpp
Go to the documentation of this file.
1#include "fl/task/executor.h"
2#include "fl/stl/functional.h"
3#include "fl/stl/singleton.h"
4#include "fl/stl/scope_exit.h"
5#include "fl/stl/algorithm.h"
6#include "fl/task/task.h"
7#include "fl/stl/chrono.h"
8#include "fl/log/log.h"
9#include "fl/log/log.h"
10
11#include "fl/stl/new.h"
12#include "fl/system/yield.h"
13#include "platforms/coroutine_runtime.h"
14
15namespace fl {
16namespace task {
17
18namespace detail {
19
20
26} // namespace detail
27
31
33 if (r && fl::find(mRunners.begin(), mRunners.end(), r) == mRunners.end()) {
34 mRunners.push_back(r);
35 }
36}
37
39 auto it = fl::find(mRunners.begin(), mRunners.end(), r);
40 if (it != mRunners.end()) {
41 mRunners.erase(it);
42 }
43}
44
46 // Update all registered runners
47 for (auto* r : mRunners) {
48 if (r) {
49 r->update();
50 }
51 }
52}
53
55 for (const auto* r : mRunners) {
56 if (r && r->has_active_tasks()) {
57 return true;
58 }
59 }
60 return false;
61}
62
64 size_t total = 0;
65 for (const auto* r : mRunners) {
66 if (r) {
67 total += r->active_task_count();
68 }
69 }
70 return total;
71}
72
73// Public API functions
74
75void run(fl::u32 microseconds, ExecFlags flags) {
76 // Re-entrancy guard: detect if run is called from within run
78 if (running) {
79 FL_WARN_ONCE("task::run re-entrancy detected, skipping nested call");
80 return;
81 }
82 running = true;
83 auto guard = fl::make_scope_exit([&running]() { running = false; });
84
85 const bool do_tasks = flags & ExecFlags::TASKS;
86 const bool do_coroutines = flags & ExecFlags::COROUTINES;
87 const bool do_system = flags & ExecFlags::SYSTEM;
88
89 // Calculate start time with rollover protection
90 fl::u32 begin_time = fl::micros();
91
92 // Lambda to get elapsed time (rollover-safe)
93 auto elapsed = [begin_time]() {
94 return fl::micros() - begin_time;
95 };
96
97 // Lambda to get remaining time until deadline expires
98 auto remaining = [elapsed, microseconds]() -> fl::u32 {
99 fl::u32 e = elapsed();
100 if (e >= microseconds) {
101 return 0;
102 }
103 return microseconds - e;
104 };
105
106 // Lambda to check if deadline has expired
107 auto expired = [remaining]() {
108 return remaining() == 0;
109 };
110
111 do {
112 // TASKS: Scheduler (fl::task timers) + Executor (fetch, HTTP server, audio)
113 if (do_tasks) {
116 }
117
118 // SYSTEM: OS-level yield.
119 //
120 // When the caller provided a non-zero microseconds budget we treat
121 // this as an explicit spin-wait on hardware (typical DMA / driver
122 // wait loops). In that case we MUST use a deep yield (>= 1 FreeRTOS
123 // tick on ESP32) so that lower-priority network tasks — WiFi,
124 // Ethernet lwIP, etc. — actually get CPU time. Previously this
125 // path was gated on an active WiFi mode check, which missed
126 // Ethernet-only deployments and also the pre-connection window
127 // where `esp_wifi_get_mode` still returns WIFI_MODE_NULL. That
128 // regression manifested as the ESP32-S3 I2S-vs-websockets
129 // starvation reported in https://github.com/FastLED/FastLED/issues/2254
130 // (Issue 1).
131 //
132 // When microseconds == 0 the caller wants the cheapest possible
133 // yield (we are between other pumped subsystems and will loop
134 // again immediately), so we keep the lightweight fl::yield() path.
135 //
136 // The sleep is clamped to whatever is left of the caller's budget,
137 // mirroring the COROUTINES branch below. This used to be a hardcoded
138 // pumpCoroutines(1000) that ignored `microseconds` entirely, so a
139 // caller asking for run(250, SYSTEM) slept 1000 us and overshot its
140 // own deadline by 750 us on every platform where pumpCoroutines
141 // honors the requested duration (host/stub -- CoroutineRunner::run).
142 //
143 // ESP32 behavior is unchanged: pumpCoroutines() there converts us to
144 // FreeRTOS ticks and floors at one tick for ANY non-zero request
145 // (coroutine_esp32.impl.hpp, the `if (ticks == 0) ticks = 1` branch
146 // added for #2254), so 250 and 1000 both yield exactly one tick. The
147 // deep-yield guarantee #2254 depends on still holds.
148 if (do_system) {
149 if (microseconds > 0) {
150 const fl::u32 time_left = remaining();
151 if (time_left) {
152 fl::platforms::ICoroutineRuntime::instance().pumpCoroutines(
153 fl::min(1000u, time_left));
154 }
155 } else {
156 fl::yield();
157 }
158 }
159
160 // COROUTINES: Platform cooperative coroutines (pumpCoroutines)
161 if (do_coroutines) {
162 auto time_left = remaining();
163 if (time_left) {
164 fl::u32 sleep_us = fl::min(1000u, time_left);
165 fl::platforms::ICoroutineRuntime::instance().pumpCoroutines(sleep_us);
166 }
167 }
168 } while (!expired());
169}
170
171size_t active_tasks() {
173}
174
175bool has_tasks() {
177}
178
179
180} // namespace task
181} // namespace fl
FastLED chrono implementation - duration types for time measurements.
static T & instance() FL_NO_EXCEPT
Definition singleton.h:41
static T & instance() FL_NO_EXCEPT
Definition singleton.h:133
fl::vector< Runner * > mRunners
Definition executor.h:121
bool has_active_tasks() const
Check if there are any active tasks.
void update_all()
Update all registered runners.
void register_runner(Runner *r)
Register a runner.
size_t total_active_tasks() const
Get total number of active tasks across all runners.
static Executor & instance()
void unregister_runner(Runner *r)
Unregister a runner.
Task executor (singleton) — manages registered runners.
Definition executor.h:101
Generic task runner interface.
Definition executor.h:86
static Scheduler & instance()
Task executor — runs registered task runners and manages the run loop.
#define FL_WARN_ONCE(...)
Definition log.h:442
Centralized logging categories for FastLED hardware interfaces and subsystems.
int & await_depth_tls()
Get reference to thread-local await recursion depth.
bool has_tasks()
Check if any systems have active tasks.
void run(fl::u32 microseconds, ExecFlags flags)
Run selected task subsystems.
size_t active_tasks()
Get the number of active tasks across all systems.
ExecFlags
Flags controlling which subsystems run() pumps.
Definition executor.h:64
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NO_EXCEPT
Definition math.h:71
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
Iterator find(Iterator first, Iterator last, const T &value) FL_NO_EXCEPT
Definition algorithm.h:212
scope_exit< decay_t< EF > > make_scope_exit(EF &&f)
Factory for C++11/14 (no CTAD).
Definition scope_exit.h:48
void yield()
Yield to the platform scheduler.
Definition yield.cpp.hpp:31
Base definition for an LED controller.
Definition crgb.hpp:179
RAII scope guard — executes a callable on scope exit.
Platform-aware cooperative yield for FastLED.