-
Notifications
You must be signed in to change notification settings - Fork 13.3k
add regular scheduled functions, now also callable on yield()
#6039
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
Changes from all commits
1674c72
4f09a64
b6564c2
545dd35
aa4f87e
896b686
fac39ed
6e48831
b9bf95b
37128a2
36ac7dd
499d2ea
19955d8
fc9ff2a
97fde51
cce44be
83ab383
8f1953e
d2e5fbc
58cfb7b
c042640
9854ad0
e42d104
c064a58
8f022c6
e2ad457
16c7e62
7982a7f
65603a3
24474c8
d9c2270
df839c2
40dbcc1
8e06c30
a98cf9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,131 @@ | ||
|
||
#include <assert.h> | ||
|
||
#include "Schedule.h" | ||
#include "PolledTimeout.h" | ||
#include "interrupts.h" | ||
|
||
typedef std::function<bool(void)> mFuncT; | ||
|
||
struct scheduled_fn_t | ||
{ | ||
scheduled_fn_t* mNext; | ||
std::function<void(void)> mFunc; | ||
scheduled_fn_t* mNext = nullptr; | ||
mFuncT mFunc; | ||
esp8266::polledTimeout::periodicFastUs callNow; | ||
|
||
scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { } | ||
}; | ||
|
||
static scheduled_fn_t* sFirst = 0; | ||
static scheduled_fn_t* sLast = 0; | ||
static scheduled_fn_t* sFirst = nullptr; | ||
static scheduled_fn_t* sLast = nullptr; | ||
|
||
static scheduled_fn_t* sFirstUnused = 0; | ||
static scheduled_fn_t* sLastUnused = 0; | ||
static scheduled_fn_t* sUnused = nullptr; | ||
|
||
static int sCount = 0; | ||
|
||
static scheduled_fn_t* get_fn() { | ||
scheduled_fn_t* result = NULL; | ||
IRAM_ATTR // called from ISR | ||
static scheduled_fn_t* get_fn_unsafe() | ||
{ | ||
scheduled_fn_t* result = nullptr; | ||
// try to get an item from unused items list | ||
if (sFirstUnused) { | ||
result = sFirstUnused; | ||
sFirstUnused = result->mNext; | ||
if (sFirstUnused == NULL) { | ||
sLastUnused = NULL; | ||
} | ||
if (sUnused) | ||
{ | ||
result = sUnused; | ||
sUnused = sUnused->mNext; | ||
result->mNext = nullptr; | ||
} | ||
// if no unused items, and count not too high, allocate a new one | ||
else if (sCount != SCHEDULED_FN_MAX_COUNT) { | ||
else if (sCount < SCHEDULED_FN_MAX_COUNT) | ||
{ | ||
result = new scheduled_fn_t; | ||
result->mNext = NULL; | ||
++sCount; | ||
} | ||
return result; | ||
} | ||
|
||
static void recycle_fn(scheduled_fn_t* fn) | ||
static void recycle_fn_unsafe(scheduled_fn_t* fn) | ||
{ | ||
if (!sLastUnused) { | ||
sFirstUnused = fn; | ||
} | ||
else { | ||
sLastUnused->mNext = fn; | ||
} | ||
fn->mNext = NULL; | ||
sLastUnused = fn; | ||
fn->mFunc = nullptr; // special overload in c++ std lib | ||
fn->mNext = sUnused; | ||
sUnused = fn; | ||
} | ||
|
||
bool schedule_function(std::function<void(void)> fn) | ||
IRAM_ATTR // (not only) called from ISR | ||
bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us) | ||
{ | ||
scheduled_fn_t* item = get_fn(); | ||
if (!item) { | ||
assert(repeat_us < decltype(scheduled_fn_t::callNow)::neverExpires); //~26800000us (26.8s) | ||
|
||
InterruptLock lockAllInterruptsInThisScope; | ||
|
||
scheduled_fn_t* item = get_fn_unsafe(); | ||
if (!item) | ||
return false; | ||
} | ||
|
||
if (repeat_us) | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
item->callNow.reset(repeat_us); | ||
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
item->mFunc = fn; | ||
item->mNext = NULL; | ||
if (!sFirst) { | ||
sFirst = item; | ||
} | ||
else { | ||
if (sFirst) | ||
sLast->mNext = item; | ||
} | ||
else | ||
sFirst = item; | ||
sLast = item; | ||
|
||
return true; | ||
} | ||
|
||
IRAM_ATTR // (not only) called from ISR | ||
bool schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us) | ||
{ | ||
return schedule_function_us(std::function<bool(void)>(fn), repeat_us); | ||
} | ||
|
||
IRAM_ATTR // called from ISR | ||
bool schedule_function(std::function<void(void)>&& fn) | ||
{ | ||
return schedule_function_us([fn]() { fn(); return false; }, 0); | ||
} | ||
|
||
IRAM_ATTR // called from ISR | ||
bool schedule_function(const std::function<void(void)>& fn) | ||
{ | ||
return schedule_function(std::function<void(void)>(fn)); | ||
} | ||
|
||
void run_scheduled_functions() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO: This linked-list implementation is not - probably never was - preemption safe, generally a compiler will keep the values of all the pointers in registers, even on the single-core ESP8266 an IRQ will not flush the registers but just push them to the stack and restore them, therefore any IRQ that's scheduling functions fails during an ongoing run_scheduled_functions(). Blocking IRQs during the complete execution of run_scheduled_functions makes it thread/IRQ safe, but I don't think this is permissible from an IRQ performance POV. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I must step back on this. I think the compiler will not / must not optimize a variable into registers when is it not declared locally.
d-a-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
scheduled_fn_t* rFirst = sFirst; | ||
sFirst = NULL; | ||
sLast = NULL; | ||
while (rFirst) { | ||
scheduled_fn_t* item = rFirst; | ||
rFirst = item->mNext; | ||
item->mFunc(); | ||
item->mFunc = std::function<void(void)>(); | ||
recycle_fn(item); | ||
// 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). | ||
|
||
scheduled_fn_t* lastRecurring = nullptr; | ||
scheduled_fn_t* toCall = sFirst; | ||
while (toCall) | ||
{ | ||
scheduled_fn_t* item = toCall; | ||
toCall = toCall->mNext; | ||
if (item->callNow) | ||
{ | ||
if (item->mFunc()) | ||
{ | ||
lastRecurring = item; | ||
} | ||
else | ||
{ | ||
InterruptLock lockAllInterruptsInThisScope; | ||
|
||
if (sFirst == item) | ||
sFirst = sFirst->mNext; | ||
else if (lastRecurring) | ||
lastRecurring->mNext = item->mNext; | ||
|
||
if (sLast == item) | ||
sLast = lastRecurring; | ||
|
||
recycle_fn_unsafe(item); | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.