From c4e5d14e0375708d1c073bd66fa07bc5acb34ea7 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 17 Jul 2019 00:40:06 +0200 Subject: [PATCH 01/15] Fix for scheduling recurrent functions while inside scheduled function --- cores/esp8266/Schedule.cpp | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index ed39736e55..1f5c92500a 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -27,7 +27,7 @@ struct recurrent_fn_t recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { } }; -static recurrent_fn_t* rFirst = nullptr; // fifo not needed +static recurrent_fn_t rFirst(0); // fifo not needed // Returns a pointer to an unused sched_fn_t, // or if none are available allocates a new one, @@ -92,10 +92,8 @@ bool schedule_recurrent_function_us (const std::function& fn, uint32 item->mFunc = fn; - if (rFirst) - item->mNext = rFirst; - - rFirst = item; + item->mNext = rFirst.mNext; + rFirst.mNext = item; return true; } @@ -136,7 +134,8 @@ void run_scheduled_recurrent_functions () // its purpose is that it is never called from an interrupt // (always on cont stack). - if (!rFirst) + recurrent_fn_t* current = rFirst.mNext; + if (!current) return; static bool fence = false; @@ -152,10 +151,9 @@ void run_scheduled_recurrent_functions () fence = true; } - recurrent_fn_t* prev = nullptr; - recurrent_fn_t* current = rFirst; + recurrent_fn_t* prev = &rFirst; - while (current) + do { if (current->callNow && !current->mFunc()) { @@ -164,16 +162,11 @@ void run_scheduled_recurrent_functions () auto to_ditch = current; - if (prev) - { - current = current->mNext; - prev->mNext = current; - } - else - { - rFirst = rFirst->mNext; - current = rFirst; - } + // current function recursively scheduled something + if (prev->mNext != current) prev = prev->mNext; + + current = current->mNext; + prev->mNext = current; delete(to_ditch); } @@ -183,6 +176,7 @@ void run_scheduled_recurrent_functions () current = current->mNext; } } + while (current); fence = false; } From b81dfbbe9167f860bc7f555cbb8b4161bd431c7f Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 17 Jul 2019 09:24:53 +0200 Subject: [PATCH 02/15] Check that fn are valid. Invoking invalid functions throws otherwise. --- cores/esp8266/Schedule.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 1f5c92500a..bfad24887c 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -62,6 +62,9 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn) IRAM_ATTR // (not only) called from ISR bool schedule_function (const std::function& fn) { + if (!fn) + return false; + esp8266::InterruptLock lockAllInterruptsInThisScope; scheduled_fn_t* item = get_fn_unsafe(); @@ -84,6 +87,9 @@ bool schedule_recurrent_function_us (const std::function& fn, uint32 { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) + if (!fn) + return false; + esp8266::InterruptLock lockAllInterruptsInThisScope; recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us); From d6b314e499714665950ebc01f096545654b993b4 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Thu, 29 Aug 2019 14:23:10 +0200 Subject: [PATCH 03/15] Added wakeup token to scheduler. Toggling the token value breaks a scheduled function out from a delayed execution and makes it run on the next scheduler iteration. --- cores/esp8266/Schedule.cpp | 17 +++++++++++++---- cores/esp8266/Schedule.h | 7 +++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index bfad24887c..1fdf5e7a19 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -24,6 +24,8 @@ struct recurrent_fn_t recurrent_fn_t* mNext = nullptr; mRecFuncT mFunc; esp8266::polledTimeout::periodicFastUs callNow; + const std::atomic* wakeupToken = nullptr; + bool wakeupTokenCmp = false; recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { } }; @@ -83,7 +85,8 @@ bool schedule_function (const std::function& fn) return true; } -bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us) +bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us, + const std::atomic* wakeupToken) { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) @@ -97,6 +100,8 @@ bool schedule_recurrent_function_us (const std::function& fn, uint32 return false; item->mFunc = fn; + item->wakeupToken = wakeupToken; + item->wakeupTokenCmp = wakeupToken && wakeupToken->load(); item->mNext = rFirst.mNext; rFirst.mNext = item; @@ -161,15 +166,19 @@ void run_scheduled_recurrent_functions () do { - if (current->callNow && !current->mFunc()) + const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); + const bool wakeup = current->wakeupTokenCmp != wakeupToken; + if (wakeup) current->wakeupTokenCmp = wakeupToken; + + if ((wakeup || current->callNow) && !current->mFunc()) { // remove function from stack esp8266::InterruptLock lockAllInterruptsInThisScope; auto to_ditch = current; - // current function recursively scheduled something - if (prev->mNext != current) prev = prev->mNext; + // current function recursively scheduled something + if (prev->mNext != current) prev = prev->mNext; current = current->mNext; prev->mNext = current; diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 50fc88fc8b..898f35ced1 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -2,6 +2,7 @@ #define ESP_SCHEDULE_H #include +#include #define SCHEDULED_FN_MAX_COUNT 32 @@ -60,8 +61,10 @@ void run_scheduled_functions(); // recurrent function. // * A recurrent function currently must not schedule another recurrent // functions. - -bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us); +// * If a wakeupToken is used, if its value toggles, any remaining +// delay is disregarded, and the lambda runs on the next scheduler iteration. +bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us, + const std::atomic* wakeupToken = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`) From 3ce09a71ca38cd5e4c21344fc9e33ef516c52332 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 3 Sep 2019 13:05:22 +0200 Subject: [PATCH 04/15] Timer reset reliability fix. --- cores/esp8266/Schedule.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 1fdf5e7a19..67af71815c 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -169,8 +169,9 @@ void run_scheduled_recurrent_functions () const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); const bool wakeup = current->wakeupTokenCmp != wakeupToken; if (wakeup) current->wakeupTokenCmp = wakeupToken; + bool callNow = current->callNow; - if ((wakeup || current->callNow) && !current->mFunc()) + if ((wakeup || callNow) && !current->mFunc()) { // remove function from stack esp8266::InterruptLock lockAllInterruptsInThisScope; From 93be83ace40aae7108ea3ee3c2515d39a54abb0d Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 10 Sep 2019 20:04:47 +0200 Subject: [PATCH 05/15] Shrink interrupts-locked regions. Add check for periodic yield to scheduled functions run-loop. --- cores/esp8266/Schedule.cpp | 41 +++++++++++++++++++++++--------------- cores/esp8266/Schedule.h | 10 ++++------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 67af71815c..19345d30af 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -26,7 +26,7 @@ struct recurrent_fn_t esp8266::polledTimeout::periodicFastUs callNow; const std::atomic* wakeupToken = nullptr; bool wakeupTokenCmp = false; - recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { } + recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { } }; static recurrent_fn_t rFirst(0); // fifo not needed @@ -35,7 +35,7 @@ static recurrent_fn_t rFirst(0); // fifo not needed // or if none are available allocates a new one, // or nullptr if limit is reached IRAM_ATTR // called from ISR -static scheduled_fn_t* get_fn_unsafe () +static scheduled_fn_t* get_fn_unsafe() { scheduled_fn_t* result = nullptr; // try to get an item from unused items list @@ -54,7 +54,7 @@ static scheduled_fn_t* get_fn_unsafe () return result; } -static void recycle_fn_unsafe (scheduled_fn_t* fn) +static void recycle_fn_unsafe(scheduled_fn_t* fn) { fn->mFunc = nullptr; // special overload in c++ std lib fn->mNext = sUnused; @@ -62,7 +62,7 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn) } IRAM_ATTR // (not only) called from ISR -bool schedule_function (const std::function& fn) +bool schedule_function(const std::function& fn) { if (!fn) return false; @@ -85,7 +85,7 @@ bool schedule_function (const std::function& fn) return true; } -bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us, +bool schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, const std::atomic* wakeupToken) { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) @@ -93,8 +93,6 @@ bool schedule_recurrent_function_us (const std::function& fn, uint32 if (!fn) return false; - esp8266::InterruptLock lockAllInterruptsInThisScope; - recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us); if (!item) return false; @@ -103,13 +101,15 @@ bool schedule_recurrent_function_us (const std::function& fn, uint32 item->wakeupToken = wakeupToken; item->wakeupTokenCmp = wakeupToken && wakeupToken->load(); + esp8266::InterruptLock lockAllInterruptsInThisScope; + item->mNext = rFirst.mNext; rFirst.mNext = item; return true; } -void run_scheduled_functions () +void run_scheduled_functions() { esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms @@ -124,9 +124,9 @@ void run_scheduled_functions () sFirst = sFirst->mNext; if (!sFirst) sLast = nullptr; - recycle_fn_unsafe(to_recycle); - } - + recycle_fn_unsafe(to_recycle); + } + if (yieldNow) { // because scheduled function are allowed to last: @@ -137,8 +137,10 @@ void run_scheduled_functions () } } -void run_scheduled_recurrent_functions () +void run_scheduled_recurrent_functions() { + esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms + // Note to the reader: // There is no exposed API to remove a scheduled function: // Scheduled functions are removed only from this function, and @@ -178,8 +180,8 @@ void run_scheduled_recurrent_functions () auto to_ditch = current; - // current function recursively scheduled something - if (prev->mNext != current) prev = prev->mNext; + // current function recursively scheduled something + if (prev->mNext != current) prev = prev->mNext; current = current->mNext; prev->mNext = current; @@ -191,8 +193,15 @@ void run_scheduled_recurrent_functions () prev = current; current = current->mNext; } - } - while (current); + + if (yieldNow) + { + // because scheduled function might last too long for watchdog etc, + // this is yield() in cont stack: + esp_schedule(); + cont_yield(g_pcont); + } + } while (current); fence = false; } diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 898f35ced1..7caaa5fc5f 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -11,7 +11,7 @@ // in user stack (called CONT stack) without the common restrictions from // system context. Details are below. -// The purpose of recurrent scheduled function is to independantly execute +// The purpose of recurrent scheduled function is to independently execute // user code in CONT stack on a regular basis. // It has been introduced with ethernet service in mind, it can also be used // for all libraries in the need of a regular `libdaemon_handlestuff()`. @@ -37,7 +37,7 @@ // * Run the lambda only once next time. // * A scheduled function can schedule a function. -bool schedule_function (const std::function& fn); +bool schedule_function(const std::function & fn); // Run all scheduled functions. // Use this function if your are not using `loop`, @@ -59,16 +59,14 @@ void run_scheduled_functions(); // functions. However a user function returning false will cancel itself. // * Long running operations or yield() or delay() are not allowed in the // recurrent function. -// * A recurrent function currently must not schedule another recurrent -// functions. // * If a wakeupToken is used, if its value toggles, any remaining // delay is disregarded, and the lambda runs on the next scheduler iteration. -bool schedule_recurrent_function_us (const std::function& fn, uint32_t repeat_us, +bool schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, const std::atomic* wakeupToken = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`) -void run_scheduled_recurrent_functions (); +void run_scheduled_recurrent_functions(); #endif // ESP_SCHEDULE_H From 28c02222ecdf4d8143b40259822a9d32229b368c Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 11 Sep 2019 10:40:59 +0200 Subject: [PATCH 06/15] Ordered, more predictable, scheduling. Before, it had different ordering compared to FastScheduler as well as sequential calls from loop(). --- cores/esp8266/Schedule.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 19345d30af..1bcd79bffa 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -29,7 +29,8 @@ struct recurrent_fn_t recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { } }; -static recurrent_fn_t rFirst(0); // fifo not needed +static recurrent_fn_t rFirst(0); +static recurrent_fn_t* rLast = &rFirst; // Returns a pointer to an unused sched_fn_t, // or if none are available allocates a new one, @@ -98,13 +99,14 @@ bool schedule_recurrent_function_us(const std::function& fn, uint32_ return false; item->mFunc = fn; + item->mNext = nullptr; item->wakeupToken = wakeupToken; item->wakeupTokenCmp = wakeupToken && wakeupToken->load(); esp8266::InterruptLock lockAllInterruptsInThisScope; - item->mNext = rFirst.mNext; - rFirst.mNext = item; + rLast->mNext = item; + rLast = item; return true; } @@ -124,9 +126,9 @@ void run_scheduled_functions() sFirst = sFirst->mNext; if (!sFirst) sLast = nullptr; - recycle_fn_unsafe(to_recycle); - } - + recycle_fn_unsafe(to_recycle); + } + if (yieldNow) { // because scheduled function are allowed to last: @@ -147,7 +149,7 @@ void run_scheduled_recurrent_functions() // its purpose is that it is never called from an interrupt // (always on cont stack). - recurrent_fn_t* current = rFirst.mNext; + auto current = rFirst.mNext; if (!current) return; @@ -164,10 +166,14 @@ void run_scheduled_recurrent_functions() fence = true; } - recurrent_fn_t* prev = &rFirst; + auto prev = &rFirst; + // prevent scheduling of new functions during this run + auto stop = rLast; + bool done; do { + done = current == stop; const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); const bool wakeup = current->wakeupTokenCmp != wakeupToken; if (wakeup) current->wakeupTokenCmp = wakeupToken; @@ -180,11 +186,10 @@ void run_scheduled_recurrent_functions() auto to_ditch = current; - // current function recursively scheduled something - if (prev->mNext != current) prev = prev->mNext; - current = current->mNext; prev->mNext = current; + // removing rLast + if (!current) rLast = prev; delete(to_ditch); } @@ -201,7 +206,7 @@ void run_scheduled_recurrent_functions() esp_schedule(); cont_yield(g_pcont); } - } while (current); + } while (current && !done); fence = false; } From fc2ef796fbff7caa332d5338a99f23877ebef1f8 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Wed, 11 Sep 2019 11:17:25 +0200 Subject: [PATCH 07/15] Optional, for the paranoid: revert changes to (non-recurrent) schedule_function() / run_scheduled_functions(). --- cores/esp8266/Schedule.cpp | 13 +++++++------ cores/esp8266/Schedule.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 1bcd79bffa..0654bfd449 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -36,7 +36,7 @@ static recurrent_fn_t* rLast = &rFirst; // or if none are available allocates a new one, // or nullptr if limit is reached IRAM_ATTR // called from ISR -static scheduled_fn_t* get_fn_unsafe() +static scheduled_fn_t* get_fn_unsafe () { scheduled_fn_t* result = nullptr; // try to get an item from unused items list @@ -55,7 +55,7 @@ static scheduled_fn_t* get_fn_unsafe() return result; } -static void recycle_fn_unsafe(scheduled_fn_t* fn) +static void recycle_fn_unsafe (scheduled_fn_t* fn) { fn->mFunc = nullptr; // special overload in c++ std lib fn->mNext = sUnused; @@ -63,7 +63,7 @@ static void recycle_fn_unsafe(scheduled_fn_t* fn) } IRAM_ATTR // (not only) called from ISR -bool schedule_function(const std::function& fn) +bool schedule_function (const std::function& fn) { if (!fn) return false; @@ -111,7 +111,7 @@ bool schedule_recurrent_function_us(const std::function& fn, uint32_ return true; } -void run_scheduled_functions() +void run_scheduled_functions () { esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms @@ -186,10 +186,11 @@ void run_scheduled_recurrent_functions() auto to_ditch = current; + // removing rLast + if (rLast == current) rLast = prev; + current = current->mNext; prev->mNext = current; - // removing rLast - if (!current) rLast = prev; delete(to_ditch); } diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 7caaa5fc5f..5083576112 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -37,7 +37,7 @@ // * Run the lambda only once next time. // * A scheduled function can schedule a function. -bool schedule_function(const std::function & fn); +bool schedule_function (const std::function& fn); // Run all scheduled functions. // Use this function if your are not using `loop`, From 61cd2489292326c1400603b196b9fedde1390fa6 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 16 Sep 2019 09:57:21 +0200 Subject: [PATCH 08/15] Comment --- cores/esp8266/Schedule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 0654bfd449..63346172f1 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -202,7 +202,7 @@ void run_scheduled_recurrent_functions() if (yieldNow) { - // because scheduled function might last too long for watchdog etc, + // because scheduled functions might last too long for watchdog etc, // this is yield() in cont stack: esp_schedule(); cont_yield(g_pcont); From 46d1b34e0076ad40c9532b708e03174dc35f0347 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 29 Sep 2019 16:36:15 +0200 Subject: [PATCH 09/15] Adapt one-line ifs to general style in same source file. --- cores/esp8266/Schedule.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 63346172f1..0e78ad682f 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -176,7 +176,8 @@ void run_scheduled_recurrent_functions() done = current == stop; const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); const bool wakeup = current->wakeupTokenCmp != wakeupToken; - if (wakeup) current->wakeupTokenCmp = wakeupToken; + if (wakeup) + current->wakeupTokenCmp = wakeupToken; bool callNow = current->callNow; if ((wakeup || callNow) && !current->mFunc()) @@ -187,7 +188,8 @@ void run_scheduled_recurrent_functions() auto to_ditch = current; // removing rLast - if (rLast == current) rLast = prev; + if (rLast == current) + rLast = prev; current = current->mNext; prev->mNext = current; From 72b28d548dadc4290ba214c192d80fade3f0a4ed Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 30 Sep 2019 14:49:31 +0200 Subject: [PATCH 10/15] Fix wakeupToken handling - don't respond to toggle, but to different value vs. that at registering function with scheduler. --- cores/esp8266/Schedule.cpp | 2 -- cores/esp8266/Schedule.h | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 0e78ad682f..f00a3701f3 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -176,8 +176,6 @@ void run_scheduled_recurrent_functions() done = current == stop; const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); const bool wakeup = current->wakeupTokenCmp != wakeupToken; - if (wakeup) - current->wakeupTokenCmp = wakeupToken; bool callNow = current->callNow; if ((wakeup || callNow) && !current->mFunc()) diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 5083576112..85d5a0a42f 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -59,8 +59,8 @@ void run_scheduled_functions(); // functions. However a user function returning false will cancel itself. // * Long running operations or yield() or delay() are not allowed in the // recurrent function. -// * If a wakeupToken is used, if its value toggles, any remaining -// delay is disregarded, and the lambda runs on the next scheduler iteration. +// * If a wakeupToken is used, anytime during scheduling when its value differs from that +// during this call, any remaining delay from repeat_us is disregarded, and fn is executed. bool schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, const std::atomic* wakeupToken = nullptr); From 8a531b7fe30632d98a6b4f52a2f2012fb757e8e7 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 30 Sep 2019 23:09:22 +0200 Subject: [PATCH 11/15] Reword comment. --- cores/esp8266/Schedule.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 85d5a0a42f..2ed53f7706 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -59,10 +59,11 @@ void run_scheduled_functions(); // functions. However a user function returning false will cancel itself. // * Long running operations or yield() or delay() are not allowed in the // recurrent function. -// * If a wakeupToken is used, anytime during scheduling when its value differs from that -// during this call, any remaining delay from repeat_us is disregarded, and fn is executed. -bool schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, - const std::atomic* wakeupToken = nullptr); +// * If a wakeupToken is used, anytime during scheduling when its value differs +// from the original one in this call, any remaining delay from repeat_us is +// disregarded, and fn is executed. +bool schedule_recurrent_function_us(const std::function& fn, + uint32_t repeat_us, const std::atomic* wakeupToken = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`) From 88302e86df0577b747d29a876e3003faf054d00d Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 1 Oct 2019 17:50:31 +0200 Subject: [PATCH 12/15] Putting aside std::atomic concerns, use a callback for scheduler alarming. In the future, async future's .then() might take advantage of this direction. --- cores/esp8266/Schedule.cpp | 13 +++++-------- cores/esp8266/Schedule.h | 7 +++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index f00a3701f3..0832159717 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -24,8 +24,7 @@ struct recurrent_fn_t recurrent_fn_t* mNext = nullptr; mRecFuncT mFunc; esp8266::polledTimeout::periodicFastUs callNow; - const std::atomic* wakeupToken = nullptr; - bool wakeupTokenCmp = false; + std::function alarm = nullptr; recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { } }; @@ -86,8 +85,8 @@ bool schedule_function (const std::function& fn) return true; } -bool schedule_recurrent_function_us(const std::function& fn, uint32_t repeat_us, - const std::atomic* wakeupToken) +bool schedule_recurrent_function_us(const std::function& fn, + uint32_t repeat_us, std::function alarm) { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) @@ -100,8 +99,7 @@ bool schedule_recurrent_function_us(const std::function& fn, uint32_ item->mFunc = fn; item->mNext = nullptr; - item->wakeupToken = wakeupToken; - item->wakeupTokenCmp = wakeupToken && wakeupToken->load(); + item->alarm = std::move(alarm); esp8266::InterruptLock lockAllInterruptsInThisScope; @@ -174,8 +172,7 @@ void run_scheduled_recurrent_functions() do { done = current == stop; - const bool wakeupToken = current->wakeupToken && current->wakeupToken->load(); - const bool wakeup = current->wakeupTokenCmp != wakeupToken; + const bool wakeup = current->alarm && current->alarm(); bool callNow = current->callNow; if ((wakeup || callNow) && !current->mFunc()) diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 2ed53f7706..4c4ef3d54b 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -59,11 +59,10 @@ void run_scheduled_functions(); // functions. However a user function returning false will cancel itself. // * Long running operations or yield() or delay() are not allowed in the // recurrent function. -// * If a wakeupToken is used, anytime during scheduling when its value differs -// from the original one in this call, any remaining delay from repeat_us is -// disregarded, and fn is executed. +// * If alarm is used, anytime during scheduling when it returns true, +// any remaining delay from repeat_us is disregarded, and fn is executed. bool schedule_recurrent_function_us(const std::function& fn, - uint32_t repeat_us, const std::atomic* wakeupToken = nullptr); + uint32_t repeat_us, std::function alarm = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`) From 2812b989ceb8677736717a75f2341a2336c7fa34 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 1 Oct 2019 18:48:14 +0200 Subject: [PATCH 13/15] Drop atomic include, align function type syntax. --- cores/esp8266/Schedule.cpp | 4 ++-- cores/esp8266/Schedule.h | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 0832159717..1021c2ae30 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -24,7 +24,7 @@ struct recurrent_fn_t recurrent_fn_t* mNext = nullptr; mRecFuncT mFunc; esp8266::polledTimeout::periodicFastUs callNow; - std::function alarm = nullptr; + std::function alarm = nullptr; recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { } }; @@ -86,7 +86,7 @@ bool schedule_function (const std::function& fn) } bool schedule_recurrent_function_us(const std::function& fn, - uint32_t repeat_us, std::function alarm) + uint32_t repeat_us, std::function alarm) { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 4c4ef3d54b..65c771f91f 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -2,7 +2,6 @@ #define ESP_SCHEDULE_H #include -#include #define SCHEDULED_FN_MAX_COUNT 32 @@ -62,7 +61,7 @@ void run_scheduled_functions(); // * If alarm is used, anytime during scheduling when it returns true, // any remaining delay from repeat_us is disregarded, and fn is executed. bool schedule_recurrent_function_us(const std::function& fn, - uint32_t repeat_us, std::function alarm = nullptr); + uint32_t repeat_us, std::function alarm = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`) From 52f9dcf420ca797f2afd3e52e3f2ebdf1c224392 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 11 Oct 2019 14:33:04 +0200 Subject: [PATCH 14/15] Reduce flash use. --- cores/esp8266/Schedule.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 1021c2ae30..47a73c61e3 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -28,14 +28,14 @@ struct recurrent_fn_t recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { } }; -static recurrent_fn_t rFirst(0); -static recurrent_fn_t* rLast = &rFirst; +static recurrent_fn_t* rFirst = nullptr; +static recurrent_fn_t* rLast = nullptr; // Returns a pointer to an unused sched_fn_t, // or if none are available allocates a new one, // or nullptr if limit is reached IRAM_ATTR // called from ISR -static scheduled_fn_t* get_fn_unsafe () +static scheduled_fn_t* get_fn_unsafe() { scheduled_fn_t* result = nullptr; // try to get an item from unused items list @@ -54,7 +54,7 @@ static scheduled_fn_t* get_fn_unsafe () return result; } -static void recycle_fn_unsafe (scheduled_fn_t* fn) +static void recycle_fn_unsafe(scheduled_fn_t* fn) { fn->mFunc = nullptr; // special overload in c++ std lib fn->mNext = sUnused; @@ -62,7 +62,7 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn) } IRAM_ATTR // (not only) called from ISR -bool schedule_function (const std::function& fn) +bool schedule_function(const std::function& fn) { if (!fn) return false; @@ -98,18 +98,24 @@ bool schedule_recurrent_function_us(const std::function& fn, return false; item->mFunc = fn; - item->mNext = nullptr; item->alarm = std::move(alarm); esp8266::InterruptLock lockAllInterruptsInThisScope; - rLast->mNext = item; + if (rLast) + { + rLast->mNext = item; + } + else + { + rFirst = item; + } rLast = item; return true; } -void run_scheduled_functions () +void run_scheduled_functions() { esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms @@ -147,7 +153,7 @@ void run_scheduled_recurrent_functions() // its purpose is that it is never called from an interrupt // (always on cont stack). - auto current = rFirst.mNext; + auto current = rFirst; if (!current) return; @@ -164,7 +170,7 @@ void run_scheduled_recurrent_functions() fence = true; } - auto prev = &rFirst; + recurrent_fn_t* prev = nullptr; // prevent scheduling of new functions during this run auto stop = rLast; @@ -187,7 +193,14 @@ void run_scheduled_recurrent_functions() rLast = prev; current = current->mNext; - prev->mNext = current; + if (prev) + { + prev->mNext = current; + } + else + { + rFirst = current; + } delete(to_ditch); } From e12a369cb0b78af91895695ff41ae4d165bd8ee5 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 28 Oct 2019 10:00:33 +0100 Subject: [PATCH 15/15] Prefer const ref over call by value plus std::move(). --- cores/esp8266/Schedule.cpp | 4 ++-- cores/esp8266/Schedule.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 47a73c61e3..465521e518 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -86,7 +86,7 @@ bool schedule_function(const std::function& fn) } bool schedule_recurrent_function_us(const std::function& fn, - uint32_t repeat_us, std::function alarm) + uint32_t repeat_us, const std::function& alarm) { assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) @@ -98,7 +98,7 @@ bool schedule_recurrent_function_us(const std::function& fn, return false; item->mFunc = fn; - item->alarm = std::move(alarm); + item->alarm = alarm; esp8266::InterruptLock lockAllInterruptsInThisScope; diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 65c771f91f..a02241fa68 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -61,7 +61,7 @@ void run_scheduled_functions(); // * If alarm is used, anytime during scheduling when it returns true, // any remaining delay from repeat_us is disregarded, and fn is executed. bool schedule_recurrent_function_us(const std::function& fn, - uint32_t repeat_us, std::function alarm = nullptr); + uint32_t repeat_us, const std::function& alarm = nullptr); // Test recurrence and run recurrent scheduled functions. // (internally called at every `yield()` and `loop()`)