Kaynağa Gözat

v3.7.0 - optional self-destruct of tasks on disable

Anatoli Arkhipenko 3 yıl önce
ebeveyn
işleme
e7703d441b

+ 4 - 3
README.md

@@ -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)
 
@@ -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)
 
@@ -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)

+ 22 - 13
examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino

@@ -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

+ 34 - 23
examples/Scheduler_example00_Blink_Adafruit/Scheduler_example00_Blink_Adafruit.ino → examples/Scheduler_example00_Blink_Namespace/Scheduler_example00_Blink_Namespace.ino

@@ -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
@@ -40,7 +51,7 @@
 #endif
 
 // Scheduler
-TaskScheduler ts;
+TsScheduler ts;
 
 /*
    Approach 1: LED is driven by the boolean variable; false = OFF, true = ON
@@ -48,7 +59,7 @@ TaskScheduler ts;
 #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
@@ -56,14 +67,14 @@ Task tBlink1 ( PERIOD1 * TASK_MILLISECOND, DURATION / PERIOD1, &blink1CB, &ts, t
 #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
@@ -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 );
 
 
 /*
@@ -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 );
 
 
 /*
@@ -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:
@@ -212,7 +223,7 @@ void blink41() {
   //  _PP(millis());
   //  _PL(": blink41");
   LEDOn();
-  StatusRequest* r = tBlink4On.getInternalStatusRequest();
+  TsStatusRequest* r = tBlink4On.getInternalStatusRequest();
   tBlink4Off.waitForDelayed( r );
   counter++;
 }
@@ -221,7 +232,7 @@ void blink42() {
   //  _PP(millis());
   //  _PL(": blink42");
   LEDOff();
-  StatusRequest* r = tBlink4Off.getInternalStatusRequest();
+  TsStatusRequest* r = tBlink4Off.getInternalStatusRequest();
   tBlink4On.waitForDelayed( r );
   counter++;
 }

+ 6 - 1
examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino

@@ -17,6 +17,8 @@
 #include <TaskScheduler.h>
 #include <QueueArray.h>
 
+int freeMemory();
+
 #if defined (ARDUINO_ARCH_AVR)
 #include <MemoryFree.h>
 #elif defined(__arm__)
@@ -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;

+ 121 - 0
examples/Scheduler_example19_Dynamic_Tasks_SelfDestruct/Scheduler_example19_Dynamic_Tasks_SelfDestruct.ino

@@ -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);
+  }
+}

+ 137 - 0
examples/Scheduler_example19_Dynamic_Tasks_stdQueue/Scheduler_example19_Dynamic_Tasks_stdQueue.ino

@@ -0,0 +1,137 @@
+/**
+    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.
+      Garbage collection deletes all the tasks which have finished (enabled in their respective
+      OnDisable methods)
+
+      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
+
+#include <TaskScheduler.h>
+
+#include <queue>
+
+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);
+Task tGarbageCollection(200 * TASK_MILLISECOND, TASK_FOREVER, &GC, &ts, false);
+
+
+void Iteration();
+bool OnEnable();
+void OnDisable();
+
+int noOfTasks = 0;
+std::queue <Task*> toDelete;
+
+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);
+
+    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();
+  toDelete.push(t);
+  tGarbageCollection.enableIfNot();
+
+  Serial.print(millis()); Serial.print("\t");
+  Serial.print("Task N"); Serial.print(tid); Serial.println(F("\tfinished"));
+}
+
+/**
+   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 GC() {
+  if ( toDelete.empty() ) {
+    tGarbageCollection.disable();
+    return;
+  }
+  Task *t = toDelete.front(); toDelete.pop();
+  Serial.print(millis()); Serial.print("\t");
+  Serial.print("Task N"); Serial.print(t->getId()); Serial.println(F("\tdestroyed"));
+  Serial.print("Free mem="); Serial.print(freeMemory());
+  Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks);
+  delete t;
+}
+
+void loop() {
+  ts.execute();
+}

+ 27 - 13
examples/Scheduler_template/Scheduler_template.ino

@@ -37,21 +37,29 @@
 // ==== INCLUDES ==================================================================================
 
 // ==== Uncomment desired compile options =================================
-// #define _TASK_SLEEP_ON_IDLE_RUN  // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
+// ----------------------------------------
+// The following "defines" control library functionality at compile time,
+// and should be used in the main sketch depending on the functionality required
+// Should be defined BEFORE #include <TaskScheduler.h>  !!!
+//
 // #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 and ESP32 ONLY)
+// #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 dynamic callback method binding
-// #define _TASK_DEFINE_MILLIS      // Force forward declaration of millis() and micros() "C" style
+// #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>
 
@@ -66,23 +74,29 @@ void task2Callback();
 
 // ==== Scheduling defines (cheat sheet) =====================
 /*
-  TASK_MILLISECOND
-  TASK_SECOND
-  TASK_MINUTE
-  TASK_HOUR
-  TASK_IMMEDIATE
-  TASK_FOREVER
-  TASK_ONCE
-  TASK_NOTIMEOUT
+  TASK_MILLISECOND  - one millisecond in millisecond/microseconds
+  TASK_SECOND       - one second in millisecond/microseconds
+  TASK_MINUTE       - one minute in millisecond/microseconds
+  TASK_HOUR         - one hour in millisecond/microseconds
+  TASK_IMMEDIATE    - schedule task to runn as soon as possible
+  TASK_FOREVER      - run task indefinitely
+  TASK_ONCE         - run task once
+  TASK_NOTIMEOUT    - set timeout interval to No Timeout
   
   TASK_SCHEDULE     - schedule is a priority, with "catch up" (default)
   TASK_SCHEDULE_NC  - schedule is a priority, without "catch up"
   TASK_INTERVAL     - interval is a priority, without "catch up"
+  
+  TASK_SR_OK        - status request triggered with an OK code (all good)
+  TASK_SR_ERROR     - status request triggered with an ERROR code
+  TASK_SR_CANCEL    - status request was cancelled
+  TASK_SR_ABORT     - status request was aborted
+  TASK_SR_TIMEOUT   - status request timed out
 */
 
 // ==== Task definitions ========================
 Task t1 (100 * TASK_MILLISECOND, TASK_FOREVER, &task1Callback, &ts, true);
-Task t2 (TASK_IMMEDIATE, 100, &task2Callback, &ts, true);
+Task t2 (TASK_IMMEDIATE, 100 /* times */, &task2Callback, &ts, true);
 
 
 

+ 7 - 0
keywords.txt

@@ -11,6 +11,10 @@ StatusRequest	KEYWORD1
 Task	KEYWORD1
 TS	KEYWORD1
 TaskScheduler	KEYWORD1
+TsScheduler	KEYWORD1
+TsStatusRequest	KEYWORD1
+TsTask	KEYWORD1
+TsTaskScheduler	KEYWORD1
 
 #######################################
 # Methods and Functions (KEYWORD2)
@@ -56,6 +60,7 @@ getOverrun	KEYWORD2
 getPreviousTask	KEYWORD2
 getRunCounter	KEYWORD2
 getSchedulingOption	KEYWORD2
+getSelfDestruct	KEYWORD2
 getStartDelay	KEYWORD2
 getStatus	KEYWORD2
 getStatusRequest	KEYWORD2
@@ -84,6 +89,7 @@ setLtsPointer	KEYWORD2
 setOnDisable	KEYWORD2
 setOnEnable	KEYWORD2
 setSchedulingOption	KEYWORD2
+setSelfDestruct	KEYWORD2
 setSleepMethod	KEYWORD2
 setTimeout	KEYWORD2
 setWaiting	KEYWORD2
@@ -119,6 +125,7 @@ _TASK_SCHEDULING_OPTIONS	LITERAL1
 _TASK_DEFINE_MILLIS	LITERAL1
 _TASK_EXTERNAL_TIME	LITERAL1
 _TASK_THREAD_SAFE	LITERAL1
+_TASK_SELF_DESTRUCT	LITERAL1
 SleepCallback	LITERAL1
 TASK_FOREVER	LITERAL1
 TASK_HOUR	LITERAL1

+ 1 - 1
library.json

@@ -16,7 +16,7 @@
       "maintainer": true
     }
   ],
-  "version": "3.6.2",
+  "version": "3.7.0",
   "frameworks": "arduino",
   "platforms": "*"
 }

+ 2 - 2
library.properties

@@ -1,9 +1,9 @@
 name=TaskScheduler
-version=3.6.2
+version=3.7.0
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers.
-paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266, esp32 only), overall task timeout, static and dynamic callback method binding.
+paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (where supported), overall task timeout, static and dynamic callback method binding.
 category=Timing
 url=https://github.com/arkhipenko/TaskScheduler.git
 architectures=*

+ 7 - 4
src/TScheduler.hpp

@@ -16,7 +16,10 @@ namespace TS{
 #include "TaskScheduler.h"
 }
 
-using TaskScheduler = TS::Scheduler;
-using SleepCallback = TS::SleepCallback;
-using Task = TS::Task;
-using StatusRequest = TS::StatusRequest;
+using TsScheduler = TS::Scheduler;
+using TsTask = TS::Task;
+using TsStatusRequest = TS::StatusRequest;
+using TsSleepCallback = TS::SleepCallback;
+using TsTaskCallback = TS::TaskCallback;
+using TsTaskOnDisable = TS::TaskOnDisable;
+using TsTaskOnEnable = TS::TaskOnEnable;

+ 7 - 4
src/TSchedulerDeclarations.hpp

@@ -16,7 +16,10 @@ namespace TS{
 #include "TaskSchedulerDeclarations.h"
 }
 
-using TaskScheduler = TS::Scheduler;
-using SleepCallback = TS::SleepCallback;
-using Task = TS::Task;
-using StatusRequest = TS::StatusRequest;
+using TsScheduler = TS::Scheduler;
+using TsTask = TS::Task;
+using TsStatusRequest = TS::StatusRequest;
+using TsSleepCallback = TS::SleepCallback;
+using TsTaskCallback = TS::TaskCallback;
+using TsTaskOnDisable = TS::TaskOnDisable;
+using TsTaskOnEnable = TS::TaskOnEnable;

+ 49 - 7
src/TaskScheduler.h

@@ -217,6 +217,10 @@ v3.6.2:
    2022-10-04 - feature: added TScheduler.hpp and TSchedulerDeclarations.hpp - a workaround for conflicting declarations (e.g., nRF52840 using Adafruit Core).
                 using namespace TS (credit: https://github.com/vortigont)
    
+v3.7.0:
+   2022-10-10 - feature: added ability for Task to "self-destruct" on disable. Useful for dynamic task management.
+                Added updated example 19 for this functionality. Updated the Sketch Template
+                (Thanks, https://github.com/vortigont for the idea).
 
 */
 
@@ -256,6 +260,7 @@ extern "C" {
 // #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
 
  #ifdef _TASK_MICRO_RES
 
@@ -304,15 +309,29 @@ static uint32_t _task_micros() {return micros();}
  * so could be called with no parameters.
  */
 #ifdef _TASK_OO_CALLBACKS
-Task::Task( unsigned long aInterval, long aIterations, Scheduler* aScheduler, bool aEnable ) {
+Task::Task( unsigned long aInterval, long aIterations, Scheduler* aScheduler, bool aEnable
+#ifdef _TASK_SELF_DESTRUCT
+, bool aSelfDestruct ) {
+#else 
+  ) {
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
     reset();
     set(aInterval, aIterations);
 #else
-Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
+Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable
+#ifdef _TASK_SELF_DESTRUCT
+, bool aSelfDestruct ) {
+#else
+  ) {
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
     reset();
     set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
 #endif
 
+#ifdef _TASK_SELF_DESTRUCT
+    setSelfDestruct(aSelfDestruct);
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
+    
     if (aScheduler) aScheduler->addTask(*this);
 
 #ifdef _TASK_WDT_IDS
@@ -327,9 +346,8 @@ Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, S
  *  prior to being deleted.
  */
 Task::~Task() {
-    disable();
-    if (iScheduler)
-        iScheduler->deleteTask(*this);
+    if ( this->isEnabled() ) disable();
+    if (iScheduler) iScheduler->deleteTask(*this);
 }
 
 
@@ -538,6 +556,11 @@ void Task::reset() {
 #ifdef _TASK_THREAD_SAFE
     iMutex = 0;
 #endif  // _TASK_THREAD_SAFE
+
+#ifdef _TASK_SELF_DESTRUCT
+    iStatus.sd_request = false;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
+
 }
 
 /** Explicitly set Task execution parameters
@@ -882,6 +905,10 @@ bool Task::disable() {
 #ifdef _TASK_STATUS_REQUEST
     iMyStatusRequest.signalComplete();
 #endif
+
+#ifdef _TASK_SELF_DESTRUCT
+    if ( getSelfDestruct() ) iStatus.sd_request = true;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
     return (previousEnabled);
 }
 
@@ -895,6 +922,10 @@ void Task::abort() {
 #ifdef _TASK_STATUS_REQUEST
     iMyStatusRequest.signalComplete(TASK_SR_ABORT);
 #endif
+
+#ifdef _TASK_SELF_DESTRUCT
+    if ( getSelfDestruct() ) iStatus.sd_request = true;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
 }
 
 
@@ -1089,10 +1120,15 @@ void Scheduler::disableAll() {
 
     iEnabled = false;
     
-    Task    *current = iFirst;
+    Task*    current = iFirst;
+    Task*    next;
     while (current) {
+        next = current->iNext;
         current->disable();
-        current = current->iNext;
+#ifdef _TASK_SELF_DESTRUCT
+        if ( current->iStatus.sd_request ) delete iCurrent;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
+        current = next;
     }
 
 #ifdef _TASK_PRIORITY
@@ -1300,6 +1336,9 @@ bool Scheduler::execute() {
     // Disable task on last iteration:
                 if (iCurrent->iIterations == 0) {
                     iCurrent->disable();
+#ifdef _TASK_SELF_DESTRUCT
+                    if ( iCurrent->iStatus.sd_request ) delete iCurrent;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
                     break;
                 }
                 m = _TASK_TIME_FUNCTION();
@@ -1310,6 +1349,9 @@ bool Scheduler::execute() {
                 if ( iCurrent->iTimeout && (m - iCurrent->iStarttime > iCurrent->iTimeout) ) {
                     iCurrent->iStatus.timeout = true;
                     iCurrent->disable();
+#ifdef _TASK_SELF_DESTRUCT
+                    if ( iCurrent->iStatus.sd_request ) delete iCurrent;
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
                     break;
                 }
 #endif // _TASK_TIMEOUT

+ 26 - 6
src/TaskSchedulerDeclarations.h

@@ -28,6 +28,7 @@
 // #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
 
 class Scheduler;
 
@@ -143,14 +144,18 @@ typedef bool (*TaskOnEnable)();
 typedef struct  {
     bool  enabled       : 1;           // indicates that task is enabled or not.
     bool  inonenable    : 1;           // indicates that task execution is inside OnEnable method (preventing infinite loops)
-    bool  canceled      : 1;           // indication that tast has been canceled prior to normal end of all iterations or regular call to disable()
+    bool  canceled      : 1;           // indication that task has been canceled prior to normal end of all iterations or regular call to disable()
+#ifdef _TASK_SELF_DESTRUCT
+    bool  selfdestruct  : 1;           // indication that task has been requested to self-destruct on disable
+    bool  sd_request    : 1;           // request for scheduler to delete task object and take task out of the queue
+#endif  // _TASK_SELF_DESTRUCT
 #ifdef _TASK_STATUS_REQUEST
     uint8_t  waiting    : 2;           // indication if task is waiting on the status request
-#endif
+#endif  // _TASK_STATUS_REQUEST
 
 #ifdef _TASK_TIMEOUT
     bool  timeout       : 1;           // indication if task timed out
-#endif
+#endif  //  _TASK_TIMEOUT
 } __task_status;
 
 
@@ -159,9 +164,19 @@ class Task {
   public:
 
 #ifdef _TASK_OO_CALLBACKS
-    INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false);
+    INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false
+#ifdef _TASK_SELF_DESTRUCT
+    , bool aSelfDestruct=false);
 #else
-    INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
+    );
+#endif  // #ifdef _TASK_SELF_DESTRUCT
+#else
+    INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL
+#ifdef _TASK_SELF_DESTRUCT
+  , bool aSelfDestruct=false);
+#else
+  );
+#endif  // #ifdef _TASK_SELF_DESTRUCT
 #endif // _TASK_OO_CALLBACKS
 
 
@@ -214,7 +229,12 @@ class Task {
     INLINE unsigned long getInterval();
     INLINE void setIterations(long aIterations);
     INLINE long getIterations();
-    INLINE unsigned long getRunCounter() ;
+    INLINE unsigned long getRunCounter();
+    
+#ifdef _TASK_SELF_DESTRUCT
+    INLINE void setSelfDestruct(bool aSelfDestruct=true) { iStatus.selfdestruct = aSelfDestruct; }
+    INLINE bool getSelfDestruct() { return iStatus.selfdestruct; }
+#endif  //  #ifdef _TASK_SELF_DESTRUCT
 
 #ifdef _TASK_OO_CALLBACKS
     virtual INLINE bool Callback() =0;  // return true if run was "productive - this will disable sleep on the idle run for next pass