mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-09 03:41:41 +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:
parent
e4c6a7a73a
commit
6e51ef0cc8
@ -24,16 +24,18 @@ struct recurrent_fn_t
|
||||
recurrent_fn_t* mNext = nullptr;
|
||||
mRecFuncT mFunc;
|
||||
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,
|
||||
// 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
|
||||
@ -52,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;
|
||||
@ -60,8 +62,11 @@ static void recycle_fn_unsafe (scheduled_fn_t* fn)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
scheduled_fn_t* item = get_fn_unsafe();
|
||||
@ -80,27 +85,37 @@ bool schedule_function (const std::function<void(void)>& fn)
|
||||
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)
|
||||
|
||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||
if (!fn)
|
||||
return false;
|
||||
|
||||
recurrent_fn_t* item = new (std::nothrow) recurrent_fn_t(repeat_us);
|
||||
if (!item)
|
||||
return false;
|
||||
|
||||
item->mFunc = fn;
|
||||
item->alarm = alarm;
|
||||
|
||||
if (rFirst)
|
||||
item->mNext = rFirst;
|
||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||
|
||||
rFirst = 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
|
||||
|
||||
@ -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:
|
||||
// There is no exposed API to remove a scheduled function:
|
||||
// Scheduled functions are removed only from this function, and
|
||||
// its purpose is that it is never called from an interrupt
|
||||
// (always on cont stack).
|
||||
|
||||
if (!rFirst)
|
||||
auto current = rFirst;
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
static bool fence = false;
|
||||
@ -153,26 +171,35 @@ void run_scheduled_recurrent_functions ()
|
||||
}
|
||||
|
||||
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
|
||||
esp8266::InterruptLock lockAllInterruptsInThisScope;
|
||||
|
||||
auto to_ditch = current;
|
||||
|
||||
// removing rLast
|
||||
if (rLast == current)
|
||||
rLast = prev;
|
||||
|
||||
current = current->mNext;
|
||||
if (prev)
|
||||
{
|
||||
current = current->mNext;
|
||||
prev->mNext = current;
|
||||
}
|
||||
else
|
||||
{
|
||||
rFirst = rFirst->mNext;
|
||||
current = rFirst;
|
||||
rFirst = current;
|
||||
}
|
||||
|
||||
delete(to_ditch);
|
||||
@ -182,7 +209,15 @@ void run_scheduled_recurrent_functions ()
|
||||
prev = current;
|
||||
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;
|
||||
}
|
||||
|
@ -10,7 +10,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()`.
|
||||
@ -58,14 +58,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.
|
||||
|
||||
bool schedule_recurrent_function_us (const std::function<bool(void)>& fn, uint32_t repeat_us);
|
||||
// * 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<bool(void)>& fn,
|
||||
uint32_t repeat_us, const std::function<bool(void)>& alarm = 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user