Skip to content

Wakeup delayed scheduling #6485

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Oct 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 55 additions & 20 deletions cores/esp8266/Schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,16 +54,19 @@ 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;
sUnused = 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();
Expand All @@ -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

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
}
12 changes: 6 additions & 6 deletions cores/esp8266/Schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
Expand Down Expand Up @@ -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