1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-12 01:53:07 +03:00

Wakeup delayed scheduling (#6485)

* Fix for scheduling recurrent functions while inside scheduled function

* Check that fn are valid. Invoking invalid functions throws otherwise.

* 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.

* Timer reset reliability fix.

* Shrink interrupts-locked regions.

Add check for periodic yield to scheduled functions run-loop.

* Ordered, more predictable, scheduling. Before, it had different ordering compared to

FastScheduler as well as sequential calls from loop().

* Optional, for the paranoid: revert changes to (non-recurrent) schedule_function() / run_scheduled_functions().

* Comment

* Adapt one-line ifs to general style in same source file.

* Fix wakeupToken handling - don't respond to toggle, but to different value

vs. that at registering function with scheduler.

* Reword comment.

* Putting aside std::atomic concerns, use a callback for scheduler alarming.

In the future, async future's .then() might take advantage of this direction.

* Drop atomic include, align function type syntax.

* Reduce flash use.

* Prefer const ref over call by value plus std::move().
This commit is contained in:
Dirk O. Kaar
2019-10-28 12:55:00 +01:00
committed by Develo
parent e4c6a7a73a
commit 6e51ef0cc8
2 changed files with 61 additions and 26 deletions

View File

@ -24,16 +24,18 @@ struct recurrent_fn_t
recurrent_fn_t* mNext = nullptr; recurrent_fn_t* mNext = nullptr;
mRecFuncT mFunc; mRecFuncT mFunc;
esp8266::polledTimeout::periodicFastUs callNow; esp8266::polledTimeout::periodicFastUs callNow;
recurrent_fn_t (esp8266::polledTimeout::periodicFastUs interval): callNow(interval) { } std::function<bool(void)> alarm = nullptr;
recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { }
}; };
static recurrent_fn_t* rFirst = nullptr; // fifo not needed static recurrent_fn_t* rFirst = nullptr;
static recurrent_fn_t* rLast = nullptr;
// Returns a pointer to an unused sched_fn_t, // Returns a pointer to an unused sched_fn_t,
// or if none are available allocates a new one, // or if none are available allocates a new one,
// or nullptr if limit is reached // or nullptr if limit is reached
IRAM_ATTR // called from ISR IRAM_ATTR // called from ISR
static scheduled_fn_t* get_fn_unsafe () static scheduled_fn_t* get_fn_unsafe()
{ {
scheduled_fn_t* result = nullptr; scheduled_fn_t* result = nullptr;
// try to get an item from unused items list // try to get an item from unused items list
@ -52,7 +54,7 @@ static scheduled_fn_t* get_fn_unsafe ()
return result; 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->mFunc = nullptr; // special overload in c++ std lib
fn->mNext = sUnused; fn->mNext = sUnused;
@ -60,8 +62,11 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn)
} }
IRAM_ATTR // (not only) called from ISR IRAM_ATTR // (not only) called from ISR
bool schedule_function (const std::function<void(void)>& fn) bool schedule_function(const std::function<void(void)>& fn)
{ {
if (!fn)
return false;
esp8266::InterruptLock lockAllInterruptsInThisScope; esp8266::InterruptLock lockAllInterruptsInThisScope;
scheduled_fn_t* item = get_fn_unsafe(); scheduled_fn_t* item = get_fn_unsafe();
@ -80,27 +85,37 @@ bool schedule_function (const std::function<void(void)>& fn)
return true; return true;
} }
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us) bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
uint32_t repeat_us, const std::function<bool(void)>& alarm)
{ {
assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s) assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
esp8266::InterruptLock lockAllInterruptsInThisScope; if (!fn)
return false;
recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us); recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us);
if (!item) if (!item)
return false; return false;
item->mFunc = fn; item->mFunc = fn;
item->alarm = alarm;
if (rFirst) esp8266::InterruptLock lockAllInterruptsInThisScope;
item->mNext = rFirst;
rFirst = item; if (rLast)
{
rLast->mNext = item;
}
else
{
rFirst = item;
}
rLast = item;
return true; return true;
} }
void run_scheduled_functions () void run_scheduled_functions()
{ {
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
@ -128,15 +143,18 @@ 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: // Note to the reader:
// There is no exposed API to remove a scheduled function: // There is no exposed API to remove a scheduled function:
// Scheduled functions are removed only from this function, and // Scheduled functions are removed only from this function, and
// its purpose is that it is never called from an interrupt // its purpose is that it is never called from an interrupt
// (always on cont stack). // (always on cont stack).
if (!rFirst) auto current = rFirst;
if (!current)
return; return;
static bool fence = false; static bool fence = false;
@ -153,26 +171,35 @@ void run_scheduled_recurrent_functions ()
} }
recurrent_fn_t* prev = nullptr; recurrent_fn_t* prev = nullptr;
recurrent_fn_t* current = rFirst; // prevent scheduling of new functions during this run
auto stop = rLast;
while (current) bool done;
do
{ {
if (current->callNow && !current->mFunc()) done = current == stop;
const bool wakeup = current->alarm && current->alarm();
bool callNow = current->callNow;
if ((wakeup || callNow) && !current->mFunc())
{ {
// remove function from stack // remove function from stack
esp8266::InterruptLock lockAllInterruptsInThisScope; esp8266::InterruptLock lockAllInterruptsInThisScope;
auto to_ditch = current; auto to_ditch = current;
// removing rLast
if (rLast == current)
rLast = prev;
current = current->mNext;
if (prev) if (prev)
{ {
current = current->mNext;
prev->mNext = current; prev->mNext = current;
} }
else else
{ {
rFirst = rFirst->mNext; rFirst = current;
current = rFirst;
} }
delete(to_ditch); delete(to_ditch);
@ -182,7 +209,15 @@ void run_scheduled_recurrent_functions ()
prev = current; prev = current;
current = current->mNext; current = current->mNext;
} }
}
if (yieldNow)
{
// because scheduled functions might last too long for watchdog etc,
// this is yield() in cont stack:
esp_schedule();
cont_yield(g_pcont);
}
} while (current && !done);
fence = false; fence = false;
} }

View File

@ -10,7 +10,7 @@
// in user stack (called CONT stack) without the common restrictions from // in user stack (called CONT stack) without the common restrictions from
// system context. Details are below. // 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. // user code in CONT stack on a regular basis.
// It has been introduced with ethernet service in mind, it can also be used // 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()`. // for all libraries in the need of a regular `libdaemon_handlestuff()`.
@ -58,14 +58,14 @@ void run_scheduled_functions();
// functions. However a user function returning false will cancel itself. // functions. However a user function returning false will cancel itself.
// * Long running operations or yield() or delay() are not allowed in the // * Long running operations or yield() or delay() are not allowed in the
// recurrent function. // recurrent function.
// * A recurrent function currently must not schedule another recurrent // * If alarm is used, anytime during scheduling when it returns true,
// functions. // any remaining delay from repeat_us is disregarded, and fn is executed.
bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us); uint32_t repeat_us, const std::function<bool(void)>& alarm = nullptr);
// Test recurrence and run recurrent scheduled functions. // Test recurrence and run recurrent scheduled functions.
// (internally called at every `yield()` and `loop()`) // (internally called at every `yield()` and `loop()`)
void run_scheduled_recurrent_functions (); void run_scheduled_recurrent_functions();
#endif // ESP_SCHEDULE_H #endif // ESP_SCHEDULE_H