Skip to content

Commit

Permalink
v3.7.0 - optional self-destruct of tasks on disable
Browse files Browse the repository at this point in the history
  • Loading branch information
arkhipenko committed Oct 10, 2022
1 parent 25b1db7 commit e7703d4
Show file tree
Hide file tree
Showing 14 changed files with 450 additions and 77 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Task Scheduler
### Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers
#### Version 3.6.2: 2022-10-04 [Latest updates](https://github.com/arkhipenko/TaskScheduler/wiki/Latest-Updates)
#### Version 3.7.0: 2022-10-10 [Latest updates](https://github.com/arkhipenko/TaskScheduler/wiki/Latest-Updates)

[![arduino-library-badge](https://www.ardu-badge.com/badge/TaskScheduler.svg?)](https://www.ardu-badge.com/TaskScheduler)[![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/arkhipenko/TaskScheduler)

Expand Down Expand Up @@ -34,7 +34,8 @@ _“Everybody who learns concurrency and thinks they understand it, ends up find
13. CPU load / idle statistics for time critical applications
14. Scheduling options with priority for original schedule (with and without catchup) and interval
15. Ability to pause/resume and enable/disable scheduling
15. Thread-safe scheduling while running under preemptive scheduler (i. e., FreeRTOS)
16. Thread-safe scheduling while running under preemptive scheduler (i. e., FreeRTOS)
17. Optional self-destruction of dynamically created tasks upon disable

Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization)

Expand All @@ -43,7 +44,7 @@ Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Ard
* Arduino Nano
* Arduino Micro
* ATtiny85
* ESP8266 (Node MCU v2.0)
* ESP8266
* ESP32
* Teensy (tested on Teensy 3.5)
* nRF52 (tested on nRF52832)
Expand Down
35 changes: 22 additions & 13 deletions examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@
- STM32 Maple Mini
*/


// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
// ----------------------------------------
// The following "defines" control library functionality at compile time,
// and should be used in the main sketch depending on the functionality required
//
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
// #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods
// #define _TASK_THREAD_SAFE // Enable additional checking for thread safety
// #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable
#include <TaskScheduler.h>

// Debug and Test options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,29 @@
*/


// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
// ----------------------------------------
// The following "defines" control library functionality at compile time,
// and should be used in the main sketch depending on the functionality required
//
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
// #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods
// #define _TASK_THREAD_SAFE // Enable additional checking for thread safety
// #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable

#include <TScheduler.hpp>

// Debug and Test options
Expand All @@ -40,30 +51,30 @@
#endif

// Scheduler
TaskScheduler ts;
TsScheduler ts;

/*
Approach 1: LED is driven by the boolean variable; false = OFF, true = ON
*/
#define PERIOD1 500
#define DURATION 10000
void blink1CB();
Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true );
TsTask tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, true );

/*
Approac 2: two callback methods: one turns ON, another turns OFF
*/
#define PERIOD2 400
void blink2CB_ON();
void blink2CB_OFF();
Task tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false );
TsTask tBlink2 ( PERIOD2 * TASK_MILLISECOND, DURATION / PERIOD2, &blink2CB_ON, &ts, false );

/*
Approach 3: Use RunCounter
*/
#define PERIOD3 300
void blink3CB();
Task tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false);
TsTask tBlink3 (PERIOD3 * TASK_MILLISECOND, DURATION / PERIOD3, &blink3CB, &ts, false);

/*
Approach 4: Use status request objects to pass control from one task to the other
Expand All @@ -73,8 +84,8 @@ bool blink41OE();
void blink41();
void blink42();
void blink42OD();
Task tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE );
Task tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD );
TsTask tBlink4On ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink41, &ts, false, &blink41OE );
TsTask tBlink4Off ( PERIOD4 * TASK_MILLISECOND, TASK_ONCE, blink42, &ts, false, NULL, &blink42OD );


/*
Expand All @@ -85,8 +96,8 @@ bool blink51OE();
void blink51();
void blink52();
void blink52OD();
Task tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE );
Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD );
TsTask tBlink5On ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE );
TsTask tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, false, NULL, &blink52OD );


/*
Expand All @@ -96,7 +107,7 @@ Task tBlink5Off ( 600 * TASK_MILLISECOND, DURATION / PERIOD5, &blink52, &ts, fal
void blink6CB();
bool blink6OE();
void blink6OD();
Task tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD );
TsTask tBlink6 ( PERIOD6 * TASK_MILLISECOND, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD );

void setup() {
// put your setup code here, to run once:
Expand Down Expand Up @@ -212,7 +223,7 @@ void blink41() {
// _PP(millis());
// _PL(": blink41");
LEDOn();
StatusRequest* r = tBlink4On.getInternalStatusRequest();
TsStatusRequest* r = tBlink4On.getInternalStatusRequest();
tBlink4Off.waitForDelayed( r );
counter++;
}
Expand All @@ -221,7 +232,7 @@ void blink42() {
// _PP(millis());
// _PL(": blink42");
LEDOff();
StatusRequest* r = tBlink4Off.getInternalStatusRequest();
TsStatusRequest* r = tBlink4Off.getInternalStatusRequest();
tBlink4On.waitForDelayed( r );
counter++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <TaskScheduler.h>
#include <QueueArray.h>

int freeMemory();

#if defined (ARDUINO_ARCH_AVR)
#include <MemoryFree.h>
#elif defined(__arm__)
Expand All @@ -25,8 +27,11 @@ static int freeMemory() {
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}
#elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32)
int freeMemory() { return ESP.getFreeHeap();}
#else
int freeMemory(); // supply your own
// Supply your own freeMemory method
int freeMemory() { return 0;}
#endif

Scheduler ts;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
TaskScheduler Test sketch - test of Task destructor
Test case:
Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object
which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed.
Self-destruct feature takes care of the task deletion after tasks complete
This sketch uses the following libraries:
- FreeMemory library: https://github.com/McNeight/MemoryFree
- QueueArray library: https://playground.arduino.cc/Code/QueueArray/
*/

#define _TASK_WDT_IDS // To enable task unique IDs
#define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run
#define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer
#define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable
#include <TaskScheduler.h>

int freeMemory();

#if defined (ARDUINO_ARCH_AVR)
#include <MemoryFree.h>
#elif defined(__arm__)
extern "C" char* sbrk(int incr);
static int freeMemory() {
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}
#elif defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32)
int freeMemory() { return ESP.getFreeHeap();}
#else
// Supply your own freeMemory method
int freeMemory() { return 0;}
#endif

Scheduler ts;

// Callback methods prototypes
void MainLoop();
void GC();

// Statis task
Task tMain(100 * TASK_MILLISECOND, 100, &MainLoop, &ts, true);

void Iteration();
bool OnEnable();
void OnDisable();

int noOfTasks = 0;

void MainLoop() {
Serial.print(millis()); Serial.print("\t");
Serial.print("MainLoop run: ");
int i = tMain.getRunCounter();
Serial.print(i); Serial.print(F(".\t"));

if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases
// Generating another task
long p = random(100, 5001); // from 100 ms to 5 seconds
long j = random(1, 11); // from 1 to 10 iterations)
Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable, true); // enable self-destruct

Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t"));
Serial.print(p); Serial.print(", "); Serial.print(j);
Serial.print(F("\tFree mem=")); Serial.print(freeMemory());
Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks);
t->enable();
}
else {
Serial.println(F("Skipped generating a task"));
}
}


void Iteration() {
Task &t = ts.currentTask();

Serial.print(millis()); Serial.print("\t");
Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: "));
int i = t.getRunCounter();
Serial.println(i);
}

bool OnEnable() {
// to-do: think of something to put in here.
return true;
}

void OnDisable() {
Task *t = &ts.currentTask();
unsigned int tid = t->getId();

Serial.print(millis()); Serial.print("\t");
Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished"));
Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks);
}

/**
Standard Arduino setup and loop methods
*/
void setup() {
Serial.begin(115200);

randomSeed(analogRead(0) + analogRead(5));
noOfTasks = 0;

Serial.println(F("Dynamic Task Creation/Destruction Example"));
Serial.println();

Serial.print(F("Free mem=")); Serial.print(freeMemory());
Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks);
Serial.println();
}

void loop() {
ts.execute();
if ( millis() > 5000 && noOfTasks == 0 ) {
Serial.print(F("\tFree mem=")); Serial.println(freeMemory());
while(1);
}
}
Loading

0 comments on commit e7703d4

Please sign in to comment.