forked from esp8266/Arduino
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSchedule.cpp
165 lines (139 loc) · 4.47 KB
/
Schedule.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <assert.h>
#include "Schedule.h"
#include "PolledTimeout.h"
#include "interrupts.h"
#include "coredecls.h"
typedef std::function<bool(void)> mFuncT;
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
static scheduled_fn_t* get_fn_unsafe()
{
scheduled_fn_t* result = nullptr;
// try to get an item from unused items list
if (sUnused)
{
result = sUnused;
sUnused = sUnused->mNext;
result->mNext = nullptr;
result->callNow.reset(esp8266::polledTimeout::periodicFastUs::alwaysExpired);
}
// if no unused items, and count not too high, allocate a new one
else if (sCount < SCHEDULED_FN_MAX_COUNT)
{
result = new scheduled_fn_t;
++sCount;
}
return result;
}
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_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)
esp8266::InterruptLock lockAllInterruptsInThisScope;
scheduled_fn_t* item = get_fn_unsafe();
if (!item)
return false;
if (repeat_us)
item->callNow.reset(repeat_us);
item->policy = policy;
item->mFunc = fn;
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, schedule_e policy)
{
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, schedule_e policy)
{
return schedule_function_us([fn]() { fn(); return false; }, 0, policy);
}
IRAM_ATTR // called from ISR
bool schedule_function(const std::function<void(void)>& fn, schedule_e policy)
{
return schedule_function(std::function<void(void)>(fn), policy);
}
void run_scheduled_functions(schedule_e policy)
{
// 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).
static bool fence = false;
{
esp8266::InterruptLock lockAllInterruptsInThisScope;
if (fence)
// prevent recursive calls from yield()
return;
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;
// 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())
{
// function stays in list
lastRecurring = toCall;
}
else
{
// function removed from list
esp8266::InterruptLock lockAllInterruptsInThisScope;
if (sFirst == toCall)
sFirst = sFirst->mNext;
else if (lastRecurring)
lastRecurring->mNext = toCall->mNext;
if (sLast == toCall)
sLast = lastRecurring;
recycle_fn_unsafe(toCall);
}
}
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;
}