Skip to content

scheduled functions: calls from yield are now optional #6158

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 8 commits into from
May 29, 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
41 changes: 29 additions & 12 deletions cores/esp8266/Schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Schedule.h"
#include "PolledTimeout.h"
#include "interrupts.h"
#include "coredecls.h"

typedef std::function<bool(void)> mFuncT;

Expand All @@ -12,15 +13,14 @@ struct scheduled_fn_t
scheduled_fn_t* mNext = nullptr;
mFuncT mFunc;
esp8266::polledTimeout::periodicFastUs callNow;
schedule_e policy;

scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
};

static scheduled_fn_t* sFirst = nullptr;
static scheduled_fn_t* sLast = nullptr;

static scheduled_fn_t* sUnused = nullptr;

static int sCount = 0;

IRAM_ATTR // called from ISR
Expand Down Expand Up @@ -52,7 +52,7 @@ static void recycle_fn_unsafe(scheduled_fn_t* fn)
}

IRAM_ATTR // (not only) called from ISR
bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us)
bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us, schedule_e policy)
{
assert(repeat_us < decltype(scheduled_fn_t::callNow)::neverExpires); //~26800000us (26.8s)

Expand All @@ -64,8 +64,9 @@ bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us)

if (repeat_us)
item->callNow.reset(repeat_us);

item->policy = policy;
item->mFunc = fn;

if (sFirst)
sLast->mNext = item;
else
Expand All @@ -76,24 +77,24 @@ bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us)
}

IRAM_ATTR // (not only) called from ISR
bool schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us)
bool schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
{
return schedule_function_us(std::function<bool(void)>(fn), repeat_us);
return schedule_function_us(std::function<bool(void)>(fn), repeat_us, policy);
}

IRAM_ATTR // called from ISR
bool schedule_function(std::function<void(void)>&& fn)
bool schedule_function(std::function<void(void)>&& fn, schedule_e policy)
{
return schedule_function_us([fn]() { fn(); return false; }, 0);
return schedule_function_us([fn]() { fn(); return false; }, 0, policy);
}

IRAM_ATTR // called from ISR
bool schedule_function(const std::function<void(void)>& fn)
bool schedule_function(const std::function<void(void)>& fn, schedule_e policy)
{
return schedule_function(std::function<void(void)>(fn));
return schedule_function(std::function<void(void)>(fn), policy);
}

void run_scheduled_functions()
void run_scheduled_functions(schedule_e policy)
{
// Note to the reader:
// There is no exposed API to remove a scheduled function:
Expand All @@ -110,13 +111,22 @@ void run_scheduled_functions()
fence = true;
}

esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
scheduled_fn_t* lastRecurring = nullptr;
scheduled_fn_t* nextCall = sFirst;
while (nextCall)
{
scheduled_fn_t* toCall = nextCall;
nextCall = nextCall->mNext;
if (toCall->callNow)

// run scheduled function:
// - when its schedule policy allows it anytime
// - or if we are called at loop() time
// and
// - its time policy allows it
if ( ( toCall->policy == SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS
|| policy == SCHEDULED_FUNCTION_ONCE_PER_LOOP)
&& toCall->callNow)
{
if (toCall->mFunc())
{
Expand All @@ -142,6 +152,13 @@ void run_scheduled_functions()
else
// function stays in list
lastRecurring = toCall;

if (policy == SCHEDULED_FUNCTION_ONCE_PER_LOOP && yieldNow)
{
// this is yield() in cont stack:
esp_schedule();
cont_yield(g_pcont);
}
}

fence = false;
Expand Down
22 changes: 17 additions & 5 deletions cores/esp8266/Schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,33 @@

#define SCHEDULED_FN_MAX_COUNT 32

enum schedule_e
{
SCHEDULED_FUNCTION_ONCE_PER_LOOP,
SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS
};

// * Run the lambda only once next time
//bool schedule_function(std::function<void(void)>&& fn);
bool schedule_function(const std::function<void(void)>& fn);
bool schedule_function(std::function<void(void)>&& fn,
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
bool schedule_function(const std::function<void(void)>& fn,
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);

// * Run the lambda periodically about every <repeat_us> microseconds until
// it returns false.
// * Note that it may be more than <repeat_us> microseconds between calls if
// `yield` is not called frequently, and therefore should not be used for
// timing critical operations.
//bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us);
bool schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us);
bool schedule_function_us(std::function<bool(void)>&& fn,
uint32_t repeat_us,
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
bool schedule_function_us(const std::function<bool(void)>& fn,
uint32_t repeat_us,
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);

// Run all scheduled functions.
// Use this function if your are not using `loop`, or `loop` does not return
// on a regular basis.
void run_scheduled_functions();
void run_scheduled_functions(schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);

#endif //ESP_SCHEDULE_H
12 changes: 9 additions & 3 deletions cores/esp8266/core_esp8266_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,27 @@ void preloop_update_frequency() {
}


static inline void esp_yield_within_cont() __attribute__((always_inline));
static void esp_yield_within_cont() {
cont_yield(g_pcont);
run_scheduled_functions(SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS);
}

extern "C" void esp_yield() {
if (cont_can_yield(g_pcont)) {
cont_yield(g_pcont);
esp_yield_within_cont();
}
}

extern "C" void esp_schedule() {
// always on CONT stack here
run_scheduled_functions();
ets_post(LOOP_TASK_PRIORITY, 0, 0);
}

extern "C" void __yield() {
if (cont_can_yield(g_pcont)) {
esp_schedule();
cont_yield(g_pcont); //esp_yield();
esp_yield_within_cont();
}
else {
panic();
Expand All @@ -124,6 +129,7 @@ static void loop_wrapper() {
setup_done = true;
}
loop();
run_scheduled_functions(SCHEDULED_FUNCTION_ONCE_PER_LOOP);
esp_schedule();
}

Expand Down