diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 4e6a0780e0..495caafd27 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -4,6 +4,7 @@ #include "Schedule.h" #include "PolledTimeout.h" #include "interrupts.h" +#include "coredecls.h" typedef std::function mFuncT; @@ -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 @@ -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&& fn, uint32_t repeat_us) +bool schedule_function_us(std::function&& fn, uint32_t repeat_us, schedule_e policy) { assert(repeat_us < decltype(scheduled_fn_t::callNow)::neverExpires); //~26800000us (26.8s) @@ -64,8 +64,9 @@ bool schedule_function_us(std::function&& 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 @@ -76,24 +77,24 @@ bool schedule_function_us(std::function&& fn, uint32_t repeat_us) } IRAM_ATTR // (not only) called from ISR -bool schedule_function_us(const std::function& fn, uint32_t repeat_us) +bool schedule_function_us(const std::function& fn, uint32_t repeat_us, schedule_e policy) { - return schedule_function_us(std::function(fn), repeat_us); + return schedule_function_us(std::function(fn), repeat_us, policy); } IRAM_ATTR // called from ISR -bool schedule_function(std::function&& fn) +bool schedule_function(std::function&& 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& fn) +bool schedule_function(const std::function& fn, schedule_e policy) { - return schedule_function(std::function(fn)); + return schedule_function(std::function(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: @@ -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()) { @@ -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; diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 089205edc8..6c5fca7ea5 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -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&& fn); -bool schedule_function(const std::function& fn); +bool schedule_function(std::function&& fn, + schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP); +bool schedule_function(const std::function& fn, + schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP); // * Run the lambda periodically about every microseconds until // it returns false. // * Note that it may be more than 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&& fn, uint32_t repeat_us); -bool schedule_function_us(const std::function& fn, uint32_t repeat_us); +bool schedule_function_us(std::function&& fn, + uint32_t repeat_us, + schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP); +bool schedule_function_us(const std::function& 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 diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 7ab1e56bf7..795507686a 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -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(); @@ -124,6 +129,7 @@ static void loop_wrapper() { setup_done = true; } loop(); + run_scheduled_functions(SCHEDULED_FUNCTION_ONCE_PER_LOOP); esp_schedule(); }