- Notifications
You must be signed in to change notification settings - Fork13.3k
delay / esp_delay: transparently manage recurrent scheduled functions#8802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from6 commits
bde2c4c0effb78508214a0c8f705a1e43b8341c3d14d17f1b563f4a801240fc4f5eb32File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -35,6 +35,8 @@ static scheduled_fn_t* sLast = nullptr; | ||
| static scheduled_fn_t* sUnused = nullptr; | ||
| static int sCount = 0; | ||
| uint32_t recurrent_max_grain_mS = 0; | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| typedef std::function<bool(void)> mRecFuncT; | ||
| struct recurrent_fn_t | ||
| { | ||
| @@ -130,9 +132,37 @@ bool schedule_recurrent_function_us(const std::function<bool(void)>& fn, | ||
| } | ||
| rLast = item; | ||
| // grain needs to be recomputed | ||
| recurrent_max_grain_mS = 0; | ||
| return true; | ||
| } | ||
| void update_recurrent_grain () | ||
| { | ||
| if (recurrent_max_grain_mS == 0) | ||
| { | ||
| if (rFirst) | ||
| { | ||
| uint32_t recurrent_max_grain_uS = rFirst->callNow.getTimeout(); | ||
| for (auto it = rFirst->mNext; it; it = it->mNext) | ||
| recurrent_max_grain_uS = compute_gcd(recurrent_max_grain_uS, it->callNow.getTimeout()); | ||
| if (recurrent_max_grain_uS) | ||
| // round to the upper millis | ||
| recurrent_max_grain_mS = recurrent_max_grain_uS <= 1000? 1: (recurrent_max_grain_uS + 999) / 1000; | ||
| } | ||
| #ifdef DEBUG_ESP_CORE | ||
| static uint32_t last_grain = 0; | ||
| if (recurrent_max_grain_mS != last_grain) | ||
| { | ||
| ::printf(":rsf %u->%u\n", last_grain, recurrent_max_grain_mS); | ||
| last_grain = recurrent_max_grain_mS; | ||
| } | ||
| #endif | ||
| } | ||
| } | ||
| void run_scheduled_functions() | ||
| { | ||
| // prevent scheduling of new functions during this run | ||
| @@ -226,6 +256,9 @@ void run_scheduled_recurrent_functions() | ||
| } | ||
| delete(to_ditch); | ||
| // grain needs to be recomputed | ||
| recurrent_max_grain_mS = 0; | ||
| } | ||
| else | ||
| { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -39,6 +39,13 @@ | ||
| // scheduled function happen more often: every yield() (vs every loop()), | ||
| // and time resolution is microsecond (vs millisecond). Details are below. | ||
| // recurrent_max_grain_mS is used by delay() to let a chance to all | ||
| // recurrent functions to accomplish their duty per their timing | ||
| // requirement. | ||
| // When it is 0, update_recurrent_grain() can be called to recalculate it. | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| externuint32_t recurrent_max_grain_mS; | ||
| voidupdate_recurrent_grain (); | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| // scheduled functions called once: | ||
| // | ||
| // * internal queue is FIFO. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -165,10 +165,19 @@ extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_de | ||
| bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) { | ||
| uint32_t expired = millis() - start_ms; | ||
| if (expired >= timeout_ms) { | ||
| return true; // expired | ||
| } | ||
| // possibly recompute recurrent scheduled functions common timing grain | ||
| update_recurrent_grain(); | ||
| // update intvl_ms according to this grain | ||
| uint32_t ms = compute_gcd(intvl_ms, recurrent_max_grain_mS); | ||
| // recurrent scheduled functions will be called from esp_delay()->esp_suspend() | ||
| esp_delay(ms? std::min((timeout_ms - expired), ms): (timeout_ms - expired)); | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| return false; // expiration must be checked again | ||
| } | ||
| extern "C" void __yield() { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| #pragma once | ||
| #include "core_esp8266_features.h" | ||
| @@ -55,14 +54,15 @@ inline void esp_suspend(T&& blocked) { | ||
| // Try to delay until timeout_ms has expired since start_ms. | ||
| // Returns true if timeout_ms has completely expired on entry. | ||
| // Otherwise returns false after delaying for the relative | ||
| // remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter | ||
| // and possibly amended by recurrent scheduled functions timing grain. | ||
| // The delay may be asynchronously cancelled, before that timeout is reached. | ||
| bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms); | ||
| // This overload of esp_delay() delays for a duration of at most timeout_ms milliseconds. | ||
| // Whenever it is resumed, as well asat mostevery intvl_ms millisconds and depending on | ||
| //recurrent scheduled functions, it performsthe blocked callback, and if that returns true, | ||
| //it keeps delaying for the remainderof the original timeout_ms period. | ||
| template <typename T> | ||
| inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) { | ||
| const auto start_ms = millis(); | ||
| @@ -78,6 +78,18 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { | ||
| esp_delay(timeout_ms, std::forward<T>(blocked), timeout_ms); | ||
| } | ||
| // Greatest Common Divisor Euclidian algorithm | ||
| // one entry may be 0, the other is returned | ||
| template <typename T> | ||
| auto compute_gcd (T a, T b) | ||
| { | ||
| while (b) | ||
| { | ||
| auto t = b; | ||
| b = a % b; | ||
| a = t; | ||
| } | ||
| return a; | ||
| } | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| #endif //__cplusplus | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -451,9 +451,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { | ||
| //tasks to wait correctly. | ||
| constexpr unsigned int timeoutValue = 1000; //1 second | ||
| if(can_yield()) { | ||
| // The final argument, intvl_ms, to esp_delaydetermines at least | ||
| //how frequently wifi_get_opmode() is checked | ||
d-a-v marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; },100); | ||
| //if at this point mode still hasn't been reached, give up | ||
| if(wifi_get_opmode() != (uint8) m) { | ||
| @@ -642,11 +642,8 @@ static int hostByNameImpl(const char* aHostname, IPAddress& aResult, uint32_t ti | ||
| // We need to wait for c/b to fire *or* we exit on our own timeout | ||
| // (which also requires us to notify the c/b that it is supposed to delete the pending obj) | ||
| case ERR_INPROGRESS: | ||
| // esp_delay will be interrupted by found callback | ||
mcspr marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| esp_delay(timeout_ms, [&]() { return !pending->done; }); | ||
| if (pending->done) { | ||
| if ((pending->addr).isSet()) { | ||