From bde2c4c5d33d68059cf7d7818a3907a07024b63e Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 11 Jan 2023 00:28:35 +0100 Subject: [PATCH 1/8] wip --- cores/esp8266/Schedule.cpp | 27 +++++++++++++++++++++++++++ cores/esp8266/Schedule.h | 7 +++++++ cores/esp8266/core_esp8266_wiring.cpp | 12 +++++++++++- cores/esp8266/coredecls.h | 17 +++++++++++++---- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 279e78ff6c..4fd353b188 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -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; + typedef std::function mRecFuncT; struct recurrent_fn_t { @@ -130,9 +132,31 @@ bool schedule_recurrent_function_us(const std::function& 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; + } + if (recurrent_max_grain_mS == 0) + // no recurrent function, set grain to max + recurrent_max_grain_mS = std::numeric_limits::max(); + } +} + void run_scheduled_functions() { // prevent scheduling of new functions during this run @@ -226,6 +250,9 @@ void run_scheduled_recurrent_functions() } delete(to_ditch); + + // grain needs to be recomputed + recurrent_max_grain_mS = 0; } else { diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index da86e5b7f5..83e1a90a8d 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -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 +extern uint32_t recurrent_max_grain_mS; +void update_recurrent_grain (); + // scheduled functions called once: // // * internal queue is FIFO. diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index 40a996d560..ba5c36a0fd 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -34,7 +34,17 @@ static uint32_t micros_overflow_count = 0; #define REPEAT 1 void __delay(unsigned long ms) { - esp_delay(ms); + + // default "userland" delay() will call recurrent scheduled functions + // based on best effort according to their respective requirements + + if (recurrent_max_grain_mS == 0) + update_recurrent_grain(); + const auto start_ms = millis(); + do + { + run_scheduled_recurrent_functions(); + } while (!esp_try_delay(start_ms, ms, recurrent_max_grain_mS)); } void delay(unsigned long ms) __attribute__ ((weak, alias("__delay"))); diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 1e2776d71b..34c6a57cf1 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -1,6 +1,5 @@ -#ifndef __COREDECLS_H -#define __COREDECLS_H +#pragma once #include "core_esp8266_features.h" @@ -76,6 +75,16 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { esp_delay(timeout_ms, std::forward(blocked), timeout_ms); } -#endif // __cplusplus +template +auto compute_gcd (T a, T b) +{ + while (b) + { + auto t = b; + b = a % b; + a = t; + } + return a; +} -#endif // __COREDECLS_H +#endif // __cplusplus From 0effb78cdee6f9b517c70d90372816757d57b32c Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 11 Jan 2023 23:35:27 +0100 Subject: [PATCH 2/8] wip --- cores/esp8266/core_esp8266_main.cpp | 13 ++++++++++++- cores/esp8266/core_esp8266_wiring.cpp | 13 ++----------- cores/esp8266/coredecls.h | 5 +++-- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index b73d3d3a89..9db8aa09a6 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -162,11 +162,22 @@ extern "C" void __esp_delay(unsigned long ms) { extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_delay"))); -bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) { +bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, uint32_t intvl_ms) { uint32_t expired = millis() - start_ms; if (expired >= timeout_ms) { return true; } + + if (intvl_ms == 0) + { + // run_recurrent_scheduled_functions() is called from esp_delay()->esp_suspend() + // intvl_ms is set according to recurrent schedule functions + // intervals based on their respective requirements + if (recurrent_max_grain_mS == 0) + update_recurrent_grain(); + intvl_ms = recurrent_max_grain_mS; + } + esp_delay(std::min((timeout_ms - expired), intvl_ms)); return false; } diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index ba5c36a0fd..b27d52a431 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -34,17 +34,8 @@ static uint32_t micros_overflow_count = 0; #define REPEAT 1 void __delay(unsigned long ms) { - - // default "userland" delay() will call recurrent scheduled functions - // based on best effort according to their respective requirements - - if (recurrent_max_grain_mS == 0) - update_recurrent_grain(); - const auto start_ms = millis(); - do - { - run_scheduled_recurrent_functions(); - } while (!esp_try_delay(start_ms, ms, recurrent_max_grain_mS)); + // use API letting recurrent scheduled functions run in background + esp_delay(ms, [](){ return true; }); } void delay(unsigned long ms) __attribute__ ((weak, alias("__delay"))); diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 34c6a57cf1..9a0f4c92ad 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -54,7 +54,8 @@ inline void esp_suspend(T&& blocked) { // Otherwise returns false after delaying for the relative // remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter. // 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); +// intvl_ms==0 will adapt to recurrent scheduled functions and run them accordingly +bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, 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 as every intvl_ms millisconds, it performs @@ -72,7 +73,7 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t int // it keeps delaying for the remainder of the original timeout_ms period. template inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { - esp_delay(timeout_ms, std::forward(blocked), timeout_ms); + esp_delay(timeout_ms, std::forward(blocked), 0); } template diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index be482737a8..0bf8473ece 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -646,7 +646,7 @@ static int hostByNameImpl(const char* aHostname, IPAddress& aResult, uint32_t ti esp_delay(timeout_ms, [&]() { return !pending->done; - }, 10); + }); if (pending->done) { if ((pending->addr).isSet()) { From 508214ac192ad0dd84317d426a6fcd8d5dfb24c7 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Fri, 13 Jan 2023 22:13:40 +0100 Subject: [PATCH 3/8] debug --- cores/esp8266/Schedule.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 4fd353b188..76bbc392ee 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -155,6 +155,15 @@ void update_recurrent_grain () // no recurrent function, set grain to max recurrent_max_grain_mS = std::numeric_limits::max(); } + +#if 1 + static uint32_t last_grain = 0; + if (recurrent_max_grain_mS != last_grain) + { + ::printf("grain4rsf: %u -> %u\n", last_grain, recurrent_max_grain_mS); + last_grain = recurrent_max_grain_mS; + } +#endif } void run_scheduled_functions() From 0c8f70554db985b5ff12297f7e6bc31b3fb8331e Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Sat, 14 Jan 2023 15:51:11 +0100 Subject: [PATCH 4/8] simplify api --- cores/esp8266/Schedule.cpp | 19 +++++++-------- cores/esp8266/core_esp8266_main.cpp | 24 +++++++++---------- cores/esp8266/core_esp8266_wiring.cpp | 1 + cores/esp8266/coredecls.h | 16 +++++++------ .../ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 13 ++++------ .../ESP8266WiFi/src/ESP8266WiFiMulti.cpp | 4 ++-- .../ESP8266WiFi/src/include/ClientContext.h | 6 ++--- 7 files changed, 38 insertions(+), 45 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 76bbc392ee..6069599ea0 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -151,19 +151,16 @@ void update_recurrent_grain () // round to the upper millis recurrent_max_grain_mS = recurrent_max_grain_uS <= 1000? 1: (recurrent_max_grain_uS + 999) / 1000; } - if (recurrent_max_grain_mS == 0) - // no recurrent function, set grain to max - recurrent_max_grain_mS = std::numeric_limits::max(); - } -#if 1 - static uint32_t last_grain = 0; - if (recurrent_max_grain_mS != last_grain) - { - ::printf("grain4rsf: %u -> %u\n", last_grain, recurrent_max_grain_mS); - last_grain = recurrent_max_grain_mS; - } +#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() diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 9db8aa09a6..3b3f8383da 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -162,24 +162,22 @@ extern "C" void __esp_delay(unsigned long ms) { extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_delay"))); -bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, uint32_t intvl_ms) { +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; + return true; // expired } - if (intvl_ms == 0) - { - // run_recurrent_scheduled_functions() is called from esp_delay()->esp_suspend() - // intvl_ms is set according to recurrent schedule functions - // intervals based on their respective requirements - if (recurrent_max_grain_mS == 0) - update_recurrent_grain(); - intvl_ms = recurrent_max_grain_mS; - } + // possibly recompute recurrent scheduled function 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)); - esp_delay(std::min((timeout_ms - expired), intvl_ms)); - return false; + return false; // expiration must be checked again } extern "C" void __yield() { diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index b27d52a431..daf67c3299 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -35,6 +35,7 @@ static uint32_t micros_overflow_count = 0; void __delay(unsigned long ms) { // use API letting recurrent scheduled functions run in background + // but stay blocked in delay until ms is expired esp_delay(ms, [](){ return true; }); } diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 9a0f4c92ad..a51f7e37d8 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -52,15 +52,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. +// remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter +// and possibly amended by recurrent scheduled fuctions timing grain. // The delay may be asynchronously cancelled, before that timeout is reached. -// intvl_ms==0 will adapt to recurrent scheduled functions and run them accordingly -bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, uint32_t intvl_ms); +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 as every intvl_ms millisconds, it performs -// the blocked callback, and if that returns true, it keeps delaying for the remainder -// of the original timeout_ms period. +// Whenever it is resumed, as well as at most every intvl_ms millisconds and depending on +// recurrent scheduled functions, it performs the blocked callback, and if that returns true, +// it keeps delaying for the remainder of the original timeout_ms period. template inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) { const auto start_ms = millis(); @@ -73,9 +73,11 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t int // it keeps delaying for the remainder of the original timeout_ms period. template inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { - esp_delay(timeout_ms, std::forward(blocked), 0); + esp_delay(timeout_ms, std::forward(blocked), timeout_ms); } +// Greatest Common Divisor Euclidian algorithm +// one entry may be 0, the other is returned template auto compute_gcd (T a, T b) { diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 0bf8473ece..5501128aef 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -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_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. - esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; }, 5); + // The final argument, intvl_ms, to esp_delay determines at least + // how frequently wifi_get_opmode() is checked + 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: - // Re-check every 10ms, we expect this to happen fast - esp_delay(timeout_ms, - [&]() { - return !pending->done; - }); + // esp_delay will be interrupted by found callback + esp_delay(timeout_ms, [&]() { return !pending->done; }); if (pending->done) { if ((pending->addr).isSet()) { diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index dada704ab1..2bc4d02391 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -90,7 +90,7 @@ static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs) [&status]() { status = WiFi.status(); return status != WL_CONNECTED && status != WL_CONNECT_FAILED; - }, 0); + }, 100); // Check status if (status == WL_CONNECTED) { @@ -242,7 +242,7 @@ int8_t ESP8266WiFiMulti::startScan() [&scanResult]() { scanResult = WiFi.scanComplete(); return scanResult < 0; - }, 0); + }, 100); // Check for scan timeout which may occur when scan does not report completion if (scanResult < 0) { DEBUG_WIFI_MULTI("[WIFIM] Scan timeout\n"); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 7fcad3678a..5c579c8848 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -144,8 +144,7 @@ class ClientContext _connect_pending = true; _op_start_time = millis(); // will resume on timeout or when _connected or _notify_error fires - // give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - esp_delay(_timeout_ms, [this]() { return this->_connect_pending; }, 1); + esp_delay(_timeout_ms, [this]() { return this->_connect_pending; }); _connect_pending = false; if (!_pcb) { DEBUGV(":cabrt\r\n"); @@ -485,8 +484,7 @@ class ClientContext _send_waiting = true; // will resume on timeout or when _write_some_from_cb or _notify_error fires - // give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - esp_delay(_timeout_ms, [this]() { return this->_send_waiting; }, 1); + esp_delay(_timeout_ms, [this]() { return this->_send_waiting; }); _send_waiting = false; } while(true); From a1e43b81f4c78496b6085c58b435c701b0318648 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Sat, 14 Jan 2023 16:06:00 +0100 Subject: [PATCH 5/8] fix comments --- cores/esp8266/Schedule.h | 2 +- cores/esp8266/core_esp8266_main.cpp | 2 +- cores/esp8266/core_esp8266_wiring.cpp | 4 ++-- cores/esp8266/coredecls.h | 2 +- libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 83e1a90a8d..3095286b6e 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -42,7 +42,7 @@ // 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 +// When it is 0, update_recurrent_grain() can be called to recalculate it. extern uint32_t recurrent_max_grain_mS; void update_recurrent_grain (); diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 3b3f8383da..b8fd16d218 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -168,7 +168,7 @@ bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uin return true; // expired } - // possibly recompute recurrent scheduled function common timing grain + // possibly recompute recurrent scheduled functions common timing grain update_recurrent_grain(); // update intvl_ms according to this grain diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index daf67c3299..3054d39338 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -34,8 +34,8 @@ static uint32_t micros_overflow_count = 0; #define REPEAT 1 void __delay(unsigned long ms) { - // use API letting recurrent scheduled functions run in background - // but stay blocked in delay until ms is expired + // Use API letting recurrent scheduled functions run in background + // but stay blocked in delay until ms is expired. esp_delay(ms, [](){ return true; }); } diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index a51f7e37d8..87ab5f0b45 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -53,7 +53,7 @@ inline void esp_suspend(T&& blocked) { // 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 fuctions timing grain. +// 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); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index 2bc4d02391..a2bbb919bc 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -84,8 +84,8 @@ static void printWiFiStatus(wl_status_t status) static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs) { wl_status_t status = WL_CONNECT_FAILED; - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. + // The final argument, intvl_ms, to esp_delay determines + // the max ms interval at which status is checked esp_delay(connectTimeoutMs, [&status]() { status = WiFi.status(); @@ -236,8 +236,8 @@ int8_t ESP8266WiFiMulti::startScan() WiFi.scanNetworks(true); // Wait for WiFi scan change or timeout - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. + // The final argument, intvl_ms, to esp_delay determines + // the max ms interval at which status is checked esp_delay(WIFI_SCAN_TIMEOUT_MS, [&scanResult]() { scanResult = WiFi.scanComplete(); From 4d17f1b8084d87144b362ec9796507adb888ed08 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Sat, 14 Jan 2023 21:09:16 +0100 Subject: [PATCH 6/8] fixes per review --- cores/esp8266/Schedule.cpp | 10 ++++++---- cores/esp8266/Schedule.h | 10 ++++------ cores/esp8266/core_esp8266_main.cpp | 14 ++++++++------ cores/esp8266/coredecls.h | 14 -------------- libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp | 5 ++--- 5 files changed, 20 insertions(+), 33 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 6069599ea0..767a1b8f82 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -17,6 +17,7 @@ */ #include +#include #include "Schedule.h" #include "PolledTimeout.h" @@ -34,8 +35,7 @@ static scheduled_fn_t* sFirst = nullptr; static scheduled_fn_t* sLast = nullptr; static scheduled_fn_t* sUnused = nullptr; static int sCount = 0; - -uint32_t recurrent_max_grain_mS = 0; +static uint32_t recurrent_max_grain_mS = 0; typedef std::function mRecFuncT; struct recurrent_fn_t @@ -138,7 +138,7 @@ bool schedule_recurrent_function_us(const std::function& fn, return true; } -void update_recurrent_grain () +uint32_t compute_recurrent_grain () { if (recurrent_max_grain_mS == 0) { @@ -146,7 +146,7 @@ void update_recurrent_grain () { 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()); + recurrent_max_grain_uS = std::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; @@ -161,6 +161,8 @@ void update_recurrent_grain () } #endif } + + return recurrent_max_grain_mS; } void run_scheduled_functions() diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 3095286b6e..f9ae45b8e2 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -39,12 +39,10 @@ // 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. -extern uint32_t recurrent_max_grain_mS; -void update_recurrent_grain (); +// compute_recurrent_grain() is used by delay() to give a chance to all +// recurrent functions to run per their timing requirement. + +uint32_t compute_recurrent_grain (); // scheduled functions called once: // diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index b8fd16d218..b991eb8e0f 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -22,6 +22,9 @@ //This may be used to change user task stack size: //#define CONT_STACKSIZE 4096 + +#include + #include #include "Schedule.h" extern "C" { @@ -168,14 +171,13 @@ bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uin 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); + // compute greatest chunked delay with respect to scheduled recurrent functions + uint32_t grain_ms = std::gcd(intvl_ms, compute_recurrent_grain()); // recurrent scheduled functions will be called from esp_delay()->esp_suspend() - esp_delay(ms? std::min((timeout_ms - expired), ms): (timeout_ms - expired)); + esp_delay(grain_ms > 0 ? + std::min((timeout_ms - expired), grain_ms): + (timeout_ms - expired)); return false; // expiration must be checked again } diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index c060c7bb1e..73af6d8cf1 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -78,18 +78,4 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { esp_delay(timeout_ms, std::forward(blocked), timeout_ms); } -// Greatest Common Divisor Euclidian algorithm -// one entry may be 0, the other is returned -template -auto compute_gcd (T a, T b) -{ - while (b) - { - auto t = b; - b = a % b; - a = t; - } - return a; -} - #endif // __cplusplus diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 5501128aef..f64e6d3ac5 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -451,8 +451,7 @@ 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_delay determines at least - // how frequently wifi_get_opmode() is checked + // check opmode every 100ms or give up after timeout esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; }, 100); //if at this point mode still hasn't been reached, give up @@ -642,7 +641,7 @@ 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 + // sleep until dns_found_callback is called or timeout is reached esp_delay(timeout_ms, [&]() { return !pending->done; }); if (pending->done) { From 01240fc5673339f93362735436f49ed0bd74658c Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Sat, 14 Jan 2023 21:22:25 +0100 Subject: [PATCH 7/8] update comments --- libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index a2bbb919bc..bcd433e1b1 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -84,8 +84,8 @@ static void printWiFiStatus(wl_status_t status) static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs) { wl_status_t status = WL_CONNECT_FAILED; - // The final argument, intvl_ms, to esp_delay determines - // the max ms interval at which status is checked + // Wait for WiFi to connect + // stop waiting upon status checked every 100ms or when timeout is reached esp_delay(connectTimeoutMs, [&status]() { status = WiFi.status(); @@ -236,8 +236,7 @@ int8_t ESP8266WiFiMulti::startScan() WiFi.scanNetworks(true); // Wait for WiFi scan change or timeout - // The final argument, intvl_ms, to esp_delay determines - // the max ms interval at which status is checked + // stop waiting upon status checked every 100ms or when timeout is reached esp_delay(WIFI_SCAN_TIMEOUT_MS, [&scanResult]() { scanResult = WiFi.scanComplete(); From 4f5eb32ca0bedddf7c4d0da8f166178635b67a1d Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Sat, 14 Jan 2023 21:31:20 +0100 Subject: [PATCH 8/8] rename global function --- cores/esp8266/Schedule.cpp | 2 +- cores/esp8266/Schedule.h | 6 +++--- cores/esp8266/core_esp8266_main.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 767a1b8f82..f6c650fcf9 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -138,7 +138,7 @@ bool schedule_recurrent_function_us(const std::function& fn, return true; } -uint32_t compute_recurrent_grain () +uint32_t compute_scheduled_recurrent_grain () { if (recurrent_max_grain_mS == 0) { diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index f9ae45b8e2..362d15b5f3 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -39,10 +39,10 @@ // scheduled function happen more often: every yield() (vs every loop()), // and time resolution is microsecond (vs millisecond). Details are below. -// compute_recurrent_grain() is used by delay() to give a chance to all -// recurrent functions to run per their timing requirement. +// compute_scheduled_recurrent_grain() is used by delay() to give a chance to +// all recurrent functions to run per their timing requirement. -uint32_t compute_recurrent_grain (); +uint32_t compute_scheduled_recurrent_grain (); // scheduled functions called once: // diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index b991eb8e0f..ccacc95e01 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -172,7 +172,7 @@ bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uin } // compute greatest chunked delay with respect to scheduled recurrent functions - uint32_t grain_ms = std::gcd(intvl_ms, compute_recurrent_grain()); + uint32_t grain_ms = std::gcd(intvl_ms, compute_scheduled_recurrent_grain()); // recurrent scheduled functions will be called from esp_delay()->esp_suspend() esp_delay(grain_ms > 0 ?