Skip to content

Commit 28085b6

Browse files
committed
remove scheduled functions complexity overhead, change recurrent functions api
1 parent fc77f2e commit 28085b6

File tree

3 files changed

+98
-88
lines changed

3 files changed

+98
-88
lines changed

cores/esp8266/Schedule.cpp

+83-66
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,30 @@
66
#include "interrupts.h"
77
#include "coredecls.h"
88

9-
typedef std::function<bool(void)> mFuncT;
9+
typedef std::function<void(void)> mSchedFuncT;
10+
typedef std::function<bool(void)> mRecFuncT;
1011

1112
struct scheduled_fn_t
1213
{
1314
scheduled_fn_t* mNext = nullptr;
14-
mFuncT mFunc;
15-
esp8266::polledTimeout::periodicFastUs callNow;
16-
schedule_e policy;
17-
18-
scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
15+
mSchedFuncT mFunc;
1916
};
2017

2118
static scheduled_fn_t* sFirst = nullptr;
2219
static scheduled_fn_t* sLast = nullptr;
2320
static scheduled_fn_t* sUnused = nullptr;
2421
static int sCount = 0;
2522

23+
struct recurrent_fn_t
24+
{
25+
recurrent_fn_t* mNext = nullptr;
26+
mRecFuncT mFunc;
27+
esp8266::polledTimeout::periodicFastUs callNow;
28+
recurrent_fn_t(esp8266::polledTimeout::periodicFastUs interval) : callNow(interval) { }
29+
};
30+
31+
static recurrent_fn_t* rFirst = nullptr; // stack, fifo not needed
32+
2633
IRAM_ATTR // called from ISR
2734
static scheduled_fn_t* get_fn_unsafe()
2835
{
@@ -33,7 +40,6 @@ static scheduled_fn_t* get_fn_unsafe()
3340
result = sUnused;
3441
sUnused = sUnused->mNext;
3542
result->mNext = nullptr;
36-
result->callNow.reset(esp8266::polledTimeout::periodicFastUs::alwaysExpired);
3743
}
3844
// if no unused items, and count not too high, allocate a new one
3945
else if (sCount < SCHEDULED_FN_MAX_COUNT)
@@ -52,19 +58,14 @@ static void recycle_fn_unsafe(scheduled_fn_t* fn)
5258
}
5359

5460
IRAM_ATTR // (not only) called from ISR
55-
bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us, schedule_e policy)
61+
bool schedule_function_us(std::function<void(void)>&& fn)
5662
{
57-
assert(repeat_us < decltype(scheduled_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
58-
5963
InterruptLock lockAllInterruptsInThisScope;
6064

6165
scheduled_fn_t* item = get_fn_unsafe();
6266
if (!item)
6367
return false;
6468

65-
if (repeat_us)
66-
item->callNow.reset(repeat_us);
67-
item->policy = policy;
6869
item->mFunc = fn;
6970

7071
if (sFirst)
@@ -76,90 +77,106 @@ bool schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us, sc
7677
return true;
7778
}
7879

79-
IRAM_ATTR // (not only) called from ISR
80-
bool schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
80+
IRAM_ATTR // called from ISR
81+
bool schedule_function (const std::function<void(void)>& fn)
8182
{
82-
return schedule_function_us(std::function<bool(void)>(fn), repeat_us, policy);
83+
return schedule_function(std::function<void(void)>(fn));
8384
}
8485

85-
IRAM_ATTR // called from ISR
86-
bool schedule_function(std::function<void(void)>&& fn, schedule_e policy)
86+
IRAM_ATTR // (not only) called from ISR
87+
bool schedule_recurrent_function_us (std::function<bool(void)>&& fn, uint32_t repeat_us)
8788
{
88-
return schedule_function_us([fn]() { fn(); return false; }, 0, policy);
89+
assert(repeat_us < decltype(recurrent_fn_t::callNow)::neverExpires); //~26800000us (26.8s)
90+
91+
InterruptLock lockAllInterruptsInThisScope;
92+
93+
recurrent_fn_t* item = new recurrent_fn_t(repeat_us);
94+
if (!item)
95+
return false;
96+
97+
item->mFunc = fn;
98+
99+
if (rFirst)
100+
{
101+
item->mNext = rFirst;
102+
rFirst = item;
103+
}
104+
else
105+
rFirst = item;
106+
107+
return true;
89108
}
90109

91-
IRAM_ATTR // called from ISR
92-
bool schedule_function(const std::function<void(void)>& fn, schedule_e policy)
110+
void run_scheduled_functions()
93111
{
94-
return schedule_function(std::function<void(void)>(fn), policy);
112+
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
113+
114+
while (sFirst)
115+
{
116+
sFirst->mFunc();
117+
118+
{
119+
InterruptLock lockAllInterruptsInThisScope;
120+
121+
auto to_recycle = sFirst;
122+
sFirst = sFirst->mNext;
123+
if (!sFirst)
124+
sLast = nullptr;
125+
recycle_fn_unsafe(to_recycle);
126+
}
127+
128+
if (yieldNow)
129+
{
130+
// because scheduled function are allowed to last:
131+
// this is yield() in cont stack:
132+
esp_schedule();
133+
cont_yield(g_pcont);
134+
}
135+
}
95136
}
96137

97-
void run_scheduled_functions(schedule_e policy)
138+
void run_scheduled_recurrent_functions ()
98139
{
99140
// Note to the reader:
100141
// There is no exposed API to remove a scheduled function:
101142
// Scheduled functions are removed only from this function, and
102143
// its purpose is that it is never called from an interrupt
103144
// (always on cont stack).
104145

146+
if (!rFirst)
147+
return;
148+
105149
static bool fence = false;
106150
{
107151
InterruptLock lockAllInterruptsInThisScope;
108152
if (fence)
109153
// prevent recursive calls from yield()
154+
// (even if they are not allowed)
110155
return;
111156
fence = true;
112157
}
113158

114-
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
115-
scheduled_fn_t* lastRecurring = nullptr;
116-
scheduled_fn_t* nextCall = sFirst;
117-
while (nextCall)
118-
{
119-
scheduled_fn_t* toCall = nextCall;
120-
nextCall = nextCall->mNext;
121-
122-
// run scheduled function:
123-
// - when its schedule policy allows it anytime
124-
// - or if we are called at loop() time
125-
// and
126-
// - its time policy allows it
127-
if ( ( toCall->policy == SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS
128-
|| policy == SCHEDULED_FUNCTION_ONCE_PER_LOOP)
129-
&& toCall->callNow)
130-
{
131-
if (toCall->mFunc())
132-
{
133-
// function stays in list
134-
lastRecurring = toCall;
135-
}
136-
else
137-
{
138-
// function removed from list
139-
InterruptLock lockAllInterruptsInThisScope;
140-
141-
if (sFirst == toCall)
142-
sFirst = sFirst->mNext;
143-
else if (lastRecurring)
144-
lastRecurring->mNext = toCall->mNext;
159+
recurrent_fn_t* prev = nullptr;
160+
recurrent_fn_t* current = rFirst;
145161

146-
if (sLast == toCall)
147-
sLast = lastRecurring;
162+
while (current)
163+
if (current->callNow && !current->mFunc())
164+
{
165+
// remove function from stack
166+
InterruptLock lockAllInterruptsInThisScope;
148167

149-
recycle_fn_unsafe(toCall);
150-
}
168+
auto to_ditch = current;
169+
if (prev)
170+
prev->mNext = current = current->mNext;
171+
else
172+
current = rFirst = rFirst->mNext;
173+
delete(to_ditch);
151174
}
152175
else
153-
// function stays in list
154-
lastRecurring = toCall;
155-
156-
if (policy == SCHEDULED_FUNCTION_ONCE_PER_LOOP && yieldNow)
157176
{
158-
// this is yield() in cont stack:
159-
esp_schedule();
160-
cont_yield(g_pcont);
177+
prev = current;
178+
current = current->mNext;
161179
}
162-
}
163180

164181
fence = false;
165182
}

cores/esp8266/Schedule.h

+12-20
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,25 @@
2626

2727
#define SCHEDULED_FN_MAX_COUNT 32
2828

29-
enum schedule_e
30-
{
31-
SCHEDULED_FUNCTION_ONCE_PER_LOOP,
32-
SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS
33-
};
34-
3529
// * Run the lambda only once next time
36-
bool schedule_function(std::function<void(void)>&& fn,
37-
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
38-
bool schedule_function(const std::function<void(void)>& fn,
39-
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
30+
// * long running operations, yield() and delay() are allowed
31+
bool schedule_function(std::function<void(void)>&& fn);
32+
bool schedule_function(const std::function<void(void)>& fn);
33+
34+
// Run all scheduled functions.
35+
// Use this function if your are not using `loop`, or `loop` does not return
36+
// on a regular basis.
37+
void run_scheduled_functions();
4038

4139
// * Run the lambda periodically about every <repeat_us> microseconds until
4240
// it returns false.
4341
// * Note that it may be more than <repeat_us> microseconds between calls if
4442
// `yield` is not called frequently, and therefore should not be used for
4543
// timing critical operations.
46-
bool schedule_function_us(std::function<bool(void)>&& fn,
47-
uint32_t repeat_us,
48-
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
49-
bool schedule_function_us(const std::function<bool(void)>& fn,
50-
uint32_t repeat_us,
51-
schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
44+
// * long running operations, yield() and delay() are not wise
45+
// * No exposed API to remove a scheduled function
5246

53-
// Run all scheduled functions.
54-
// Use this function if your are not using `loop`, or `loop` does not return
55-
// on a regular basis.
56-
void run_scheduled_functions(schedule_e policy = SCHEDULED_FUNCTION_ONCE_PER_LOOP);
47+
bool schedule_recurrent_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us);
48+
void run_scheduled_recurrent_functions();
5749

5850
#endif //ESP_SCHEDULE_H

cores/esp8266/core_esp8266_main.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ void preloop_update_frequency() {
8787
static inline void esp_yield_within_cont() __attribute__((always_inline));
8888
static void esp_yield_within_cont() {
8989
cont_yield(g_pcont);
90-
run_scheduled_functions(SCHEDULED_FUNCTION_WITHOUT_YIELDELAYCALLS);
90+
run_scheduled_recurrent_functions();
9191
}
9292

9393
extern "C" void esp_yield() {
@@ -129,7 +129,8 @@ static void loop_wrapper() {
129129
setup_done = true;
130130
}
131131
loop();
132-
run_scheduled_functions(SCHEDULED_FUNCTION_ONCE_PER_LOOP);
132+
run_scheduled_functions();
133+
run_scheduled_recurrent_functions();
133134
esp_schedule();
134135
}
135136

0 commit comments

Comments
 (0)