Skip to content

Commit 5eb6a7f

Browse files
committed
Add mechanism for posting functions to the main loop (#2082)
* Add mechanism for posting functions to the main loop (#1064) * Fix indentation, add note that API is not stable
1 parent 6bb8e11 commit 5eb6a7f

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

Diff for: cores/esp8266/Schedule.cpp

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "Schedule.h"
2+
3+
struct scheduled_fn_t
4+
{
5+
scheduled_fn_t* mNext;
6+
std::function<void(void)> mFunc;
7+
};
8+
9+
static scheduled_fn_t* sFirst = 0;
10+
static scheduled_fn_t* sLast = 0;
11+
12+
static scheduled_fn_t* sFirstUnused = 0;
13+
static scheduled_fn_t* sLastUnused = 0;
14+
15+
static int sCount = 0;
16+
17+
static void init_lists()
18+
{
19+
if (sCount != 0) {
20+
return;
21+
}
22+
while (sCount < SCHEDULED_FN_INITIAL_COUNT) {
23+
scheduled_fn_t* it = new scheduled_fn_t;
24+
if (sCount == 0) {
25+
sFirstUnused = it;
26+
}
27+
else {
28+
sLastUnused->mNext = it;
29+
}
30+
sLastUnused = it;
31+
++sCount;
32+
}
33+
sLastUnused->mNext = NULL;
34+
}
35+
36+
static scheduled_fn_t* get_fn() {
37+
scheduled_fn_t* result = NULL;
38+
// try to get an item from unused items list
39+
if (sFirstUnused) {
40+
result = sFirstUnused;
41+
sFirstUnused = result->mNext;
42+
if (sFirstUnused == NULL) {
43+
sLastUnused = NULL;
44+
}
45+
}
46+
// if no unused items, and count not too high, allocate a new one
47+
else if (sCount != SCHEDULED_FN_MAX_COUNT) {
48+
result = new scheduled_fn_t;
49+
result->mNext = NULL;
50+
++sCount;
51+
}
52+
return result;
53+
}
54+
55+
static void recycle_fn(scheduled_fn_t* fn)
56+
{
57+
if (!sLastUnused) {
58+
sFirstUnused = fn;
59+
}
60+
else {
61+
sLastUnused->mNext = fn;
62+
}
63+
fn->mNext = NULL;
64+
sLastUnused = fn;
65+
}
66+
67+
bool schedule_function(std::function<void(void)> fn)
68+
{
69+
scheduled_fn_t* item = get_fn();
70+
if (!item) {
71+
return false;
72+
}
73+
item->mFunc = fn;
74+
item->mNext = NULL;
75+
if (!sFirst) {
76+
sFirst = item;
77+
}
78+
else {
79+
sLast->mNext = item;
80+
}
81+
sLast = item;
82+
return true;
83+
}
84+
85+
void run_scheduled_functions()
86+
{
87+
while (sFirst) {
88+
scheduled_fn_t* item = sFirst;
89+
sFirst = item->mNext;
90+
if (sFirst == NULL) {
91+
sLast = NULL;
92+
}
93+
item->mFunc();
94+
item->mFunc = std::function<void(void)>();
95+
recycle_fn(item);
96+
}
97+
}

Diff for: cores/esp8266/Schedule.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef ESP_SCHEDULE_H
2+
#define ESP_SCHEDULE_H
3+
4+
#include <functional>
5+
6+
#define SCHEDULED_FN_MAX_COUNT 32
7+
#define SCHEDULED_FN_INITIAL_COUNT 4
8+
9+
// Warning
10+
// This API is not considered stable.
11+
// Function signatures will change.
12+
// You have been warned.
13+
14+
// Run given function next time `loop` function returns,
15+
// or `run_scheduled_functions` is called.
16+
// Use std::bind to pass arguments to a function, or call a class member function.
17+
// Note: there is no mechanism for cancelling scheduled functions.
18+
// Keep that in mind when binding functions to objects which may have short lifetime.
19+
// Returns false if the number of scheduled functions exceeds SCHEDULED_FN_MAX_COUNT.
20+
bool schedule_function(std::function<void(void)> fn);
21+
22+
// Run all scheduled functions.
23+
// Use this function if your are not using `loop`, or `loop` does not return
24+
// on a regular basis.
25+
void run_scheduled_functions();
26+
27+
#endif //ESP_SCHEDULE_H

Diff for: cores/esp8266/core_esp8266_main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
//This may be used to change user task stack size:
2424
//#define CONT_STACKSIZE 4096
2525
#include <Arduino.h>
26+
#include "Schedule.h"
2627
extern "C" {
2728
#include "ets_sys.h"
2829
#include "os_type.h"
@@ -119,6 +120,7 @@ static void loop_wrapper() {
119120
setup_done = true;
120121
}
121122
loop();
123+
run_scheduled_functions();
122124
esp_schedule();
123125
}
124126

Diff for: tests/device/test_schedule/test_schedule.ino

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include <BSTest.h>
2+
#include <test_config.h>
3+
#include <Schedule.h>
4+
5+
BS_ENV_DECLARE();
6+
7+
void setup()
8+
{
9+
Serial.begin(115200);
10+
BS_RUN(Serial);
11+
}
12+
13+
14+
TEST_CASE("scheduled functions are executed", "[schedule]")
15+
{
16+
bool executed = false;
17+
CHECK(schedule_function([&](){
18+
executed = true;
19+
}));
20+
run_scheduled_functions();
21+
CHECK(executed);
22+
}
23+
24+
TEST_CASE("scheduled functions are executed in correct order", "[schedule]")
25+
{
26+
int counter = 0;
27+
auto fn = [&](int id) {
28+
CHECK(id == counter);
29+
++counter;
30+
};
31+
for (int i = 0; i < 8; ++i) {
32+
schedule_function(std::bind(fn, i));
33+
}
34+
run_scheduled_functions();
35+
}
36+
37+
TEST_CASE("functions are only executed once", "[schedule]")
38+
{
39+
int counter = 0;
40+
auto fn = [&](){
41+
++counter;
42+
};
43+
schedule_function(fn);
44+
schedule_function(fn);
45+
schedule_function(fn);
46+
run_scheduled_functions();
47+
CHECK(counter == 3);
48+
counter = 0;
49+
run_scheduled_functions();
50+
CHECK(counter == 0);
51+
}
52+
53+
TEST_CASE("can schedule SCHEDULED_FN_MAX_COUNT functions", "[schedule]")
54+
{
55+
int counter = 0;
56+
auto fn = [&](int id) {
57+
CHECK(id == counter);
58+
CHECK(id < SCHEDULED_FN_MAX_COUNT);
59+
++counter;
60+
};
61+
int i;
62+
for (i = 0; i < SCHEDULED_FN_MAX_COUNT; ++i) {
63+
CHECK(schedule_function(std::bind(fn, i)));
64+
}
65+
CHECK(!schedule_function(std::bind(fn, i)));
66+
run_scheduled_functions();
67+
CHECK(counter == SCHEDULED_FN_MAX_COUNT);
68+
counter = 0;
69+
for (i = 0; i < SCHEDULED_FN_MAX_COUNT; ++i) {
70+
CHECK(schedule_function(std::bind(fn, i)));
71+
}
72+
CHECK(!schedule_function(std::bind(fn, i)));
73+
run_scheduled_functions();
74+
CHECK(counter == SCHEDULED_FN_MAX_COUNT);
75+
}
76+
77+
void loop(){}

0 commit comments

Comments
 (0)