8#if FL_PLATFORM_HAS_LARGE_MEMORY
11#define DEBUG_PRINT(x) do {} while(0)
12#define DEBUG_PRINTLN(x) do {} while(0)
88#define AUTORESEARCH_DECLINE_AWAIT_ON_RP(name) \
90 fl::json r = fl::json::object(); \
91 r.set("success", false); \
92 r.set("supported", false); \
93 r.set("backend", "null"); \
95 "RP2xxx selects the null coroutine backend; " name \
96 " would block forever in await() and wedge the board " \
97 "until the watchdog resets it"); \
101#define AUTORESEARCH_DECLINE_AWAIT_ON_RP(name) do { } while (0)
104 remote.bind(
"testCoroutineBasic", [](
const fl::json&
args) -> fl::json {
111 fl::task::CoroutineConfig cfg;
112 cfg.
func = [&task_ran, &task_completed]() {
113 task_ran.store(
true);
115 task_completed.store(
true);
117 cfg.
name =
"test_basic";
121 while (
t.isRunning() && (
millis() - start) < 2000) {
125 bool passed = task_ran.load() && task_completed.load() && !
t.isRunning();
126 r.
set(
"success", passed);
127 r.
set(
"taskRan", task_ran.load());
128 r.
set(
"taskCompleted", task_completed.load());
129 r.
set(
"isRunning",
t.isRunning());
130 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
135 remote.bind(
"testCoroutineStop", [](
const fl::json&
args) -> fl::json {
141 fl::task::CoroutineConfig cfg;
142 cfg.
func = [&task_started]() {
143 task_started.store(
true);
148 cfg.
name =
"test_stop";
152 while (!task_started.load() && (
millis() - start) < 2000) {
156 bool was_running =
t.isRunning();
158 bool stopped = !
t.isRunning();
160 bool passed = task_started.load() && was_running && stopped;
161 r.
set(
"success", passed);
162 r.
set(
"taskStarted", task_started.load());
163 r.
set(
"wasRunning", was_running);
164 r.
set(
"stopped", stopped);
165 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
170 remote.bind(
"testCoroutineConcurrent", [](
const fl::json&
args) -> fl::json {
174 const int NUM_TASKS = 3;
177 for (
int i = 0; i < NUM_TASKS; i++) {
178 task_flags[i].
store(
false);
181 fl::task::Handle tasks[NUM_TASKS];
182 for (
int i = 0; i < NUM_TASKS; i++) {
183 fl::task::CoroutineConfig cfg;
184 cfg.
func = [i, &task_flags, &completed_count]() {
186 task_flags[i].store(
true);
187 completed_count.fetch_add(1);
189 cfg.
name =
"test_concurrent";
194 while (completed_count.load() < NUM_TASKS && (
millis() - start) < 3000) {
198 bool all_completed =
true;
199 bool all_stopped =
true;
200 for (
int i = 0; i < NUM_TASKS; i++) {
201 if (!task_flags[i].load()) all_completed =
false;
202 if (tasks[i].isRunning()) all_stopped =
false;
205 bool passed = all_completed && all_stopped && (completed_count.load() == NUM_TASKS);
206 r.
set(
"success", passed);
207 r.
set(
"completedCount",
static_cast<int64_t
>(completed_count.load()));
208 r.
set(
"allCompleted", all_completed);
209 r.
set(
"allStopped", all_stopped);
210 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
217 remote.bind(
"testCoroutineAwait", [](
const fl::json&
args) -> fl::json {
219 AUTORESEARCH_DECLINE_AWAIT_ON_RP(
"testCoroutineAwait");
232 fl::task::CoroutineConfig consumer_cfg;
233 consumer_cfg.
func = [promise_ptr, &consumer_started, &consumer_finished,
234 &consumer_value, &consumer_ok]() {
235 consumer_started.store(
true);
239 consumer_ok.store(
true);
240 consumer_value.store(
result.value());
242 consumer_finished.store(
true);
244 consumer_cfg.
name =
"await_consumer";
251 fl::task::CoroutineConfig producer_cfg;
252 producer_cfg.
func = [promise_ptr, &producer_started, &producer_finished]() {
253 producer_started.store(
true);
255 promise_ptr->complete_with_value(42);
256 producer_finished.store(
true);
258 producer_cfg.
name =
"await_producer";
263 while ((!consumer_finished.load() || producer.isRunning()) && (
millis() - start) < 5000) {
268 bool passed = consumer_started.load() && consumer_finished.load()
269 && consumer_ok.load() && (consumer_value.load() == 42)
270 && producer_started.load() && producer_finished.load();
271 r.
set(
"success", passed);
272 r.
set(
"consumerStarted", consumer_started.load());
273 r.
set(
"consumerFinished", consumer_finished.load());
274 r.
set(
"consumerOk", consumer_ok.load());
275 r.
set(
"consumerValue",
static_cast<int64_t
>(consumer_value.load()));
276 r.
set(
"producerStarted", producer_started.load());
277 r.
set(
"producerFinished", producer_finished.load());
278 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
284 remote.bind(
"testCoroutineAwaitError", [](
const fl::json&
args) -> fl::json {
286 AUTORESEARCH_DECLINE_AWAIT_ON_RP(
"testCoroutineAwaitError");
295 fl::task::CoroutineConfig consumer_cfg;
296 consumer_cfg.
func = [promise_ptr, &consumer_finished, &got_error]() {
299 got_error.store(
true);
301 consumer_finished.store(
true);
303 consumer_cfg.
name =
"await_err_consumer";
309 fl::task::CoroutineConfig producer_cfg;
310 producer_cfg.
func = [promise_ptr, &producer_finished]() {
312 promise_ptr->complete_with_error(fl::task::Error(
"test error"));
313 producer_finished.store(
true);
315 producer_cfg.
name =
"await_err_producer";
319 while ((!consumer_finished.load() || producer.isRunning()) && (
millis() - start) < 5000) {
323 bool passed = consumer_finished.load() && got_error.load() && producer_finished.load();
324 r.
set(
"success", passed);
325 r.
set(
"consumerFinished", consumer_finished.load());
326 r.
set(
"gotError", got_error.load());
327 r.
set(
"producerFinished", producer_finished.load());
328 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
335 remote.bind(
"testCoroutinePromiseCallbacks", [](
const fl::json&
args) -> fl::json {
346 promise_ptr->then([&then_called, &then_value](
const int& val) {
347 then_called.store(
true);
348 then_value.store(val);
350 promise_ptr->catch_([&catch_called](
const fl::task::Error&) {
351 catch_called.store(
true);
355 fl::task::CoroutineConfig producer_cfg;
356 producer_cfg.
func = [promise_ptr, &producer_finished]() {
358 promise_ptr->complete_with_value(99);
359 producer_finished.store(
true);
361 producer_cfg.
name =
"cb_producer";
365 while (producer.isRunning() && (
millis() - start) < 5000) {
367 promise_ptr->update();
369 promise_ptr->update();
372 bool passed = then_called.load() && (then_value.load() == 99)
373 && !catch_called.load() && producer_finished.load();
374 r.
set(
"success", passed);
375 r.
set(
"thenCalled", then_called.load());
376 r.
set(
"thenValue",
static_cast<int64_t
>(then_value.load()));
377 r.
set(
"catchCalled", catch_called.load());
378 r.
set(
"producerFinished", producer_finished.load());
379 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
384 remote.bind(
"testCoroutinePromiseCatchCallback", [](
const fl::json&
args) -> fl::json {
393 promise_ptr->then([&then_called](
const int&) {
394 then_called.store(
true);
396 promise_ptr->catch_([&catch_called](
const fl::task::Error&) {
397 catch_called.store(
true);
401 fl::task::CoroutineConfig producer_cfg;
402 producer_cfg.
func = [promise_ptr, &producer_finished]() {
404 promise_ptr->complete_with_error(fl::task::Error(
"rejection test"));
405 producer_finished.store(
true);
407 producer_cfg.
name =
"catch_producer";
411 while (producer.isRunning() && (
millis() - start) < 5000) {
413 promise_ptr->update();
415 promise_ptr->update();
418 bool passed = !then_called.load() && catch_called.load() && producer_finished.load();
419 r.
set(
"success", passed);
420 r.
set(
"thenCalled", then_called.load());
421 r.
set(
"catchCalled", catch_called.load());
422 r.
set(
"producerFinished", producer_finished.load());
423 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
429 remote.bind(
"testCoroutineChainedAwait", [](
const fl::json&
args) -> fl::json {
431 AUTORESEARCH_DECLINE_AWAIT_ON_RP(
"testCoroutineChainedAwait");
443 fl::task::CoroutineConfig cfg_c;
444 cfg_c.
func = [p2, &final_value, &chain_complete, &c_done]() {
447 final_value.store(
result.value());
450 chain_complete.store(
true);
452 cfg_c.
name =
"chain_c";
456 fl::task::CoroutineConfig cfg_b;
457 cfg_b.
func = [p1, p2, &b_done]() {
461 p2->complete_with_value(
result.value() * 10);
463 p2->complete_with_error(
result.error());
467 cfg_b.
name =
"chain_b";
471 fl::task::CoroutineConfig cfg_a;
472 cfg_a.
func = [p1, &a_done]() {
474 p1->complete_with_value(7);
477 cfg_a.
name =
"chain_a";
481 while (!chain_complete.load() && (
millis() - start) < 5000) {
486 bool passed = chain_complete.load() && (final_value.load() == 70)
487 && a_done.load() && b_done.load() && c_done.load();
488 r.
set(
"success", passed);
489 r.
set(
"finalValue",
static_cast<int64_t
>(final_value.load()));
490 r.
set(
"aDone", a_done.load());
491 r.
set(
"bDone", b_done.load());
492 r.
set(
"cDone", c_done.load());
493 r.
set(
"durationMs",
static_cast<int64_t
>(
millis() - start));
498 remote.bind(
"testCoroutineAll", [
this](
const fl::json&
args) -> fl::json {
503 r.
set(
"success",
false);
504 r.
set(
"supported",
false);
505 r.
set(
"backend",
"null");
506 r.
set(
"reason",
"RP2xxx currently selects the generic Arduino null coroutine backend");
507 r.
set(
"passed",
static_cast<int64_t
>(0));
508 r.
set(
"failed",
static_cast<int64_t
>(0));
509 r.
set(
"total",
static_cast<int64_t
>(0));
514 const char* test_names[] = {
515 "testCoroutineBasic",
517 "testCoroutineConcurrent",
518 "testCoroutineAwait",
519 "testCoroutineAwaitError",
520 "testCoroutinePromiseCallbacks",
521 "testCoroutinePromiseCatchCallback",
522 "testCoroutineChainedAwait"
524 const int num_tests = 8;
530 for (
int i = 0; i < num_tests; i++) {
532 auto bound =
mRemote->get<fl::json(
const fl::json&)>(test_names[i]);
534 fl::json test_result = bound.value()(empty_args);
536 if (test_result.
contains(
"success")) {
537 auto val = test_result[
"success"].
as_bool();
538 success = val.has_value() && val.value();
545 results.
set(test_names[i], test_result);
549 err.
set(
"success",
false);
550 err.
set(
"error",
"Method not found");
551 results.
set(test_names[i], err);
555 r.
set(
"success", failed == 0);
556 r.
set(
"passed",
static_cast<int64_t
>(passed));
557 r.
set(
"failed",
static_cast<int64_t
>(failed));
558 r.
set(
"total",
static_cast<int64_t
>(num_tests));
559 r.
set(
"results", results);
Full PARLIO encode bench for ESP32-P4 (post-byte-LUT, #2526 landed).
PARLIO ISR-streaming functional validation (#2548 deep-dive follow-up).
Issue #2994 repro: compounded per-sequence timing drift vs 3.10.3.
Wave8 expansion micro-benchmark for #2526.
fl::unique_ptr< fl::Remote > remote
fl::net::ble — BLE GATT transport layer for JSON-RPC
SPI encoder configuration for clocked LED chipsets.
void bindAsyncMethods(fl::Remote &remote)
fl::unique_ptr< fl::Remote > mRemote
void store(T value, memory_order=memory_order_seq_cst) FL_NO_EXCEPT
JSON-RPC server with scheduling support.
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
fl::optional< bool > as_bool() const FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static Promise< T > create() FL_NO_EXCEPT
Create a pending Promise.
Task executor — runs registered task runners and manages the run loop.
Platform-abstracted heap memory query functions.
FastLED's Elegant JSON Library: fl::json
Handle coroutine(const CoroutineConfig &config)
Creates and starts an OS-level coroutine task.
PromiseResult< T > await(Promise< T > p, fl::u32 timeout_ms=fl::platforms::kAwaitDefaultTimeoutMs) FL_NO_EXCEPT
Await promise completion in a coroutine (Trampoline to platform implementation)
function< void()> func
Function executed by the coroutine, copied at creation time.
string name
Diagnostic name reported by the platform. Defaults to "task".
void delay(u32 ms, bool run_async=true) FL_NO_EXCEPT
Public delay wrapper that keeps bare Arduino delay() preferred after using fl::delay; while still all...
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Promise-based fluent API for FastLED - standalone async primitives.
Umbrella header for SIMD subsystem.