mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
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
This commit is contained in:
parent
6bb8e1145b
commit
5eb6a7f449
97
cores/esp8266/Schedule.cpp
Normal file
97
cores/esp8266/Schedule.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include "Schedule.h"
|
||||||
|
|
||||||
|
struct scheduled_fn_t
|
||||||
|
{
|
||||||
|
scheduled_fn_t* mNext;
|
||||||
|
std::function<void(void)> mFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static scheduled_fn_t* sFirst = 0;
|
||||||
|
static scheduled_fn_t* sLast = 0;
|
||||||
|
|
||||||
|
static scheduled_fn_t* sFirstUnused = 0;
|
||||||
|
static scheduled_fn_t* sLastUnused = 0;
|
||||||
|
|
||||||
|
static int sCount = 0;
|
||||||
|
|
||||||
|
static void init_lists()
|
||||||
|
{
|
||||||
|
if (sCount != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (sCount < SCHEDULED_FN_INITIAL_COUNT) {
|
||||||
|
scheduled_fn_t* it = new scheduled_fn_t;
|
||||||
|
if (sCount == 0) {
|
||||||
|
sFirstUnused = it;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sLastUnused->mNext = it;
|
||||||
|
}
|
||||||
|
sLastUnused = it;
|
||||||
|
++sCount;
|
||||||
|
}
|
||||||
|
sLastUnused->mNext = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scheduled_fn_t* get_fn() {
|
||||||
|
scheduled_fn_t* result = NULL;
|
||||||
|
// try to get an item from unused items list
|
||||||
|
if (sFirstUnused) {
|
||||||
|
result = sFirstUnused;
|
||||||
|
sFirstUnused = result->mNext;
|
||||||
|
if (sFirstUnused == NULL) {
|
||||||
|
sLastUnused = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
result->mNext = NULL;
|
||||||
|
++sCount;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recycle_fn(scheduled_fn_t* fn)
|
||||||
|
{
|
||||||
|
if (!sLastUnused) {
|
||||||
|
sFirstUnused = fn;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sLastUnused->mNext = fn;
|
||||||
|
}
|
||||||
|
fn->mNext = NULL;
|
||||||
|
sLastUnused = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool schedule_function(std::function<void(void)> fn)
|
||||||
|
{
|
||||||
|
scheduled_fn_t* item = get_fn();
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
item->mFunc = fn;
|
||||||
|
item->mNext = NULL;
|
||||||
|
if (!sFirst) {
|
||||||
|
sFirst = item;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sLast->mNext = item;
|
||||||
|
}
|
||||||
|
sLast = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_scheduled_functions()
|
||||||
|
{
|
||||||
|
while (sFirst) {
|
||||||
|
scheduled_fn_t* item = sFirst;
|
||||||
|
sFirst = item->mNext;
|
||||||
|
if (sFirst == NULL) {
|
||||||
|
sLast = NULL;
|
||||||
|
}
|
||||||
|
item->mFunc();
|
||||||
|
item->mFunc = std::function<void(void)>();
|
||||||
|
recycle_fn(item);
|
||||||
|
}
|
||||||
|
}
|
27
cores/esp8266/Schedule.h
Normal file
27
cores/esp8266/Schedule.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef ESP_SCHEDULE_H
|
||||||
|
#define ESP_SCHEDULE_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#define SCHEDULED_FN_MAX_COUNT 32
|
||||||
|
#define SCHEDULED_FN_INITIAL_COUNT 4
|
||||||
|
|
||||||
|
// Warning
|
||||||
|
// This API is not considered stable.
|
||||||
|
// Function signatures will change.
|
||||||
|
// You have been warned.
|
||||||
|
|
||||||
|
// Run given function next time `loop` function returns,
|
||||||
|
// or `run_scheduled_functions` is called.
|
||||||
|
// Use std::bind to pass arguments to a function, or call a class member function.
|
||||||
|
// Note: there is no mechanism for cancelling scheduled functions.
|
||||||
|
// Keep that in mind when binding functions to objects which may have short lifetime.
|
||||||
|
// Returns false if the number of scheduled functions exceeds SCHEDULED_FN_MAX_COUNT.
|
||||||
|
bool schedule_function(std::function<void(void)> fn);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
#endif //ESP_SCHEDULE_H
|
@ -23,6 +23,7 @@
|
|||||||
//This may be used to change user task stack size:
|
//This may be used to change user task stack size:
|
||||||
//#define CONT_STACKSIZE 4096
|
//#define CONT_STACKSIZE 4096
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "Schedule.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "ets_sys.h"
|
#include "ets_sys.h"
|
||||||
#include "os_type.h"
|
#include "os_type.h"
|
||||||
@ -119,6 +120,7 @@ static void loop_wrapper() {
|
|||||||
setup_done = true;
|
setup_done = true;
|
||||||
}
|
}
|
||||||
loop();
|
loop();
|
||||||
|
run_scheduled_functions();
|
||||||
esp_schedule();
|
esp_schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
77
tests/device/test_schedule/test_schedule.ino
Normal file
77
tests/device/test_schedule/test_schedule.ino
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include <BSTest.h>
|
||||||
|
#include <test_config.h>
|
||||||
|
#include <Schedule.h>
|
||||||
|
|
||||||
|
BS_ENV_DECLARE();
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
BS_RUN(Serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("scheduled functions are executed", "[schedule]")
|
||||||
|
{
|
||||||
|
bool executed = false;
|
||||||
|
CHECK(schedule_function([&](){
|
||||||
|
executed = true;
|
||||||
|
}));
|
||||||
|
run_scheduled_functions();
|
||||||
|
CHECK(executed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("scheduled functions are executed in correct order", "[schedule]")
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
auto fn = [&](int id) {
|
||||||
|
CHECK(id == counter);
|
||||||
|
++counter;
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
schedule_function(std::bind(fn, i));
|
||||||
|
}
|
||||||
|
run_scheduled_functions();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("functions are only executed once", "[schedule]")
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
auto fn = [&](){
|
||||||
|
++counter;
|
||||||
|
};
|
||||||
|
schedule_function(fn);
|
||||||
|
schedule_function(fn);
|
||||||
|
schedule_function(fn);
|
||||||
|
run_scheduled_functions();
|
||||||
|
CHECK(counter == 3);
|
||||||
|
counter = 0;
|
||||||
|
run_scheduled_functions();
|
||||||
|
CHECK(counter == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can schedule SCHEDULED_FN_MAX_COUNT functions", "[schedule]")
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
auto fn = [&](int id) {
|
||||||
|
CHECK(id == counter);
|
||||||
|
CHECK(id < SCHEDULED_FN_MAX_COUNT);
|
||||||
|
++counter;
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < SCHEDULED_FN_MAX_COUNT; ++i) {
|
||||||
|
CHECK(schedule_function(std::bind(fn, i)));
|
||||||
|
}
|
||||||
|
CHECK(!schedule_function(std::bind(fn, i)));
|
||||||
|
run_scheduled_functions();
|
||||||
|
CHECK(counter == SCHEDULED_FN_MAX_COUNT);
|
||||||
|
counter = 0;
|
||||||
|
for (i = 0; i < SCHEDULED_FN_MAX_COUNT; ++i) {
|
||||||
|
CHECK(schedule_function(std::bind(fn, i)));
|
||||||
|
}
|
||||||
|
CHECK(!schedule_function(std::bind(fn, i)));
|
||||||
|
run_scheduled_functions();
|
||||||
|
CHECK(counter == SCHEDULED_FN_MAX_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){}
|
Loading…
x
Reference in New Issue
Block a user