1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-30 16:24:09 +03:00

delay / esp_delay: transparently manage recurrent scheduled functions (#8802)

Recurrent scheduled functions will always be running in background.

esp_delay()'s interval (intvl_ms) is internally kept to its highest value allowing to honor recurrent scheduled functions requirements.

It transparently allows to keep with the arduino and nonos-sdk trivial programming way and still use background services or drivers running regularly.
This commit is contained in:
david gauchard
2023-01-14 22:25:57 +01:00
committed by GitHub
parent e1c4a6c8e6
commit 39080e317e
8 changed files with 74 additions and 30 deletions

View File

@ -17,6 +17,7 @@
*/
#include <assert.h>
#include <numeric>
#include "Schedule.h"
#include "PolledTimeout.h"
@ -34,6 +35,7 @@ static scheduled_fn_t* sFirst = nullptr;
static scheduled_fn_t* sLast = nullptr;
static scheduled_fn_t* sUnused = nullptr;
static int sCount = 0;
static uint32_t recurrent_max_grain_mS = 0;
typedef std::function<bool(void)> mRecFuncT;
struct recurrent_fn_t
@ -130,9 +132,39 @@ 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;
}
uint32_t compute_scheduled_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 = 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;
}
#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
}
return recurrent_max_grain_mS;
}
void run_scheduled_functions()
{
// prevent scheduling of new functions during this run
@ -226,6 +258,9 @@ void run_scheduled_recurrent_functions()
}
delete(to_ditch);
// grain needs to be recomputed
recurrent_max_grain_mS = 0;
}
else
{

View File

@ -39,6 +39,11 @@
// scheduled function happen more often: every yield() (vs every loop()),
// and time resolution is microsecond (vs millisecond). Details are below.
// 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_scheduled_recurrent_grain ();
// scheduled functions called once:
//
// * internal queue is FIFO.

View File

@ -22,6 +22,9 @@
//This may be used to change user task stack size:
//#define CONT_STACKSIZE 4096
#include <numeric>
#include <Arduino.h>
#include "Schedule.h"
extern "C" {
@ -165,10 +168,18 @@ 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;
return true; // expired
}
esp_delay(std::min((timeout_ms - expired), intvl_ms));
return false;
// compute greatest chunked delay with respect to scheduled recurrent functions
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 ?
std::min((timeout_ms - expired), grain_ms):
(timeout_ms - expired));
return false; // expiration must be checked again
}
extern "C" void __yield() {

View File

@ -34,7 +34,9 @@ static uint32_t micros_overflow_count = 0;
#define REPEAT 1
void __delay(unsigned long ms) {
esp_delay(ms);
// Use API letting recurrent scheduled functions run in background
// but stay blocked in delay until ms is expired.
esp_delay(ms, [](){ return true; });
}
void delay(unsigned long ms) __attribute__ ((weak, alias("__delay")));

View File

@ -1,6 +1,5 @@
#ifndef __COREDECLS_H
#define __COREDECLS_H
#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.
// 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 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 <typename T>
inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) {
const auto start_ms = millis();
@ -79,5 +79,3 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked) {
}
#endif // __cplusplus
#endif // __COREDECLS_H