Просмотр исходного кода

v3.6.0 - _TASK_THREAD_SAFE compile option for running under RTOS

Anatoli Arkhipenko 4 лет назад
Родитель
Сommit
635cfd5bcc
6 измененных файлов с 420 добавлено и 224 удалено
  1. 2 1
      README.md
  2. 3 0
      keywords.txt
  3. 1 1
      library.json
  4. 1 1
      library.properties
  5. 398 211
      src/TaskScheduler.h
  6. 15 10
      src/TaskSchedulerDeclarations.h

+ 2 - 1
README.md

@@ -1,6 +1,6 @@
 # Task Scheduler
 # Task Scheduler
 ### Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers
 ### Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers
-#### Version 3.5.0: 2021-11-01 [Latest updates](https://github.com/arkhipenko/TaskScheduler/wiki/Latest-Updates)
+#### Version 3.6.0: 2021-12-17 [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)
 [![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,6 +34,7 @@ _“Everybody who learns concurrency and thinks they understand it, ends up find
 13. CPU load / idle statistics for time critical applications
 13. CPU load / idle statistics for time critical applications
 14. Scheduling options with priority for original schedule (with and without catchup) and interval
 14. Scheduling options with priority for original schedule (with and without catchup) and interval
 15. Ability to pause/resume and enable/disable scheduling
 15. Ability to pause/resume and enable/disable scheduling
+15. Thread-safe scheduling while running under preemptive scheduler (i. e., FreeRTOS)
 
 
 Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization)
 Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization)
 
 

+ 3 - 0
keywords.txt

@@ -114,6 +114,9 @@ _TASK_WDT_IDS	LITERAL1
 _TASK_EXPOSE_CHAIN	LITERAL1
 _TASK_EXPOSE_CHAIN	LITERAL1
 _TASK_DEFINE_MILLIS	LITERAL1
 _TASK_DEFINE_MILLIS	LITERAL1
 _TASK_SCHEDULING_OPTIONS	LITERAL1
 _TASK_SCHEDULING_OPTIONS	LITERAL1
+_TASK_DEFINE_MILLIS	LITERAL1
+_TASK_EXTERNAL_TIME	LITERAL1
+_TASK_THREAD_SAFE	LITERAL1
 SleepCallback	LITERAL1
 SleepCallback	LITERAL1
 TASK_FOREVER	LITERAL1
 TASK_FOREVER	LITERAL1
 TASK_HOUR	LITERAL1
 TASK_HOUR	LITERAL1

+ 1 - 1
library.json

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

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=TaskScheduler
 name=TaskScheduler
-version=3.5.0
+version=3.6.0
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers.
 sentence=Cooperative multitasking for Arduino, ESPx, STM32 and other microcontrollers.

+ 398 - 211
src/TaskScheduler.h

@@ -1,212 +1,216 @@
-// Cooperative multitasking library for Arduino
-// Copyright (c) 2015-2019 Anatoli Arkhipenko
-//
-// Changelog:
-// v1.0.0:
-//     2015-02-24 - Initial release
-//     2015-02-28 - added delay() and disableOnLastIteration() methods
-//     2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
-//                  1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
-//                  2. Delay is invoked only if none of the tasks ran
-//                  3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
-//     2015-05-11 - added  restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
-//     2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
-//
-// v1.4.1:
-//     2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
-//                  sleep on idle run is no longer a default and should be explicitly compiled with
-//                 _TASK_SLEEP_ON_IDLE_RUN defined
-//
-// v1.5.0:
-//     2015-09-20 - access to currently executing task (for callback methods)
-//     2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
-//     2015-09-20 - option to create a task already enabled
-//
-// v1.5.1:
-//     2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
-//                  Thanks to Hannes Morgenstern for catching this one
-//
-// v1.6.0:
-//     2015-09-22 - revert back to having all tasks disable on last iteration.
-//     2015-09-22 - deprecated disableOnLastIteration method as a result
-//     2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
-//     2015-10-01 - made version numbers semver compliant (documentation only)
-//
-// v1.7.0:
-//    2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number.
-//    2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled,
-//                 false if was disabled.
-//    2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
-//    2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called,
-//                 on disable runs only if task was enabled
-//    2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass
-//                 regardless how much time is left
-//
-// v1.8.0:
-//    2015-10-13 - support for status request objects allowing tasks waiting on requests
-//    2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
-//
-// v1.8.1:
-//    2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
-//
-// v1.8.2:
-//    2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
-//    2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
-//    2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
-//    2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
-//
-// v1.8.3:
-//    2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations
-//                (0 and 1 are still default values)
-//    2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion
-//                 delayed for one current interval
-//    2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
-//    2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
-//    2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
-//
-// v1.8.4:
-//    2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before.
-//                 Especially since OnEnable method can change the interval
-//    2015-11-16 - further optimizations of the task scheduler execute loop
-//
-// v1.8.5:
-//    2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
-//    2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
-//
-// v1.9.0:
-//    2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
-//
-// v1.9.2:
-//    2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
-//    2015-12-16 - bug fixes: automatic millis rollover support for delay methods
-//    2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
-//
-// v2.0.0:
-//    2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
-//
-// v2.0.1:
-//    2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
-//
-// v2.0.2:
-//    2016-01-05 - bug fix: time constants wrapped inside compile option
-//    2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
-//
-// v2.1.0:
-//    2016-02-01 - support for microsecond resolution
-//    2016-02-02 - added Scheduler baseline start time reset method: startNow()
-//
-// v2.2.0:
-//    2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
-//
-// v2.2.1:
-//    2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain
-//                 back together long running callback methods
-//    2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled.
-//                 Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method.
-//
-// v2.3.0:
-//    2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is
-//                 scheduled to run next time
-//
-// v2.4.0:
-//    2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
-//                 upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-//
-// v2.5.0:
-//    2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option
-//                 (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
-//    2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
-//                 Use at your own risk!
-//    2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
-//    2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
-//
-// v2.5.1:
-//    2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.5)
-//
-// v2.5.2:
-//    2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42)
-//
-// v2.6.0:
-//    2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality
-//    2018-01-30 - ESP32 support (experimental)
-//                 (Contributed by Marco Tombesi: https://github.com/baggior)
-//
-// v2.6.1:
-//    2018-02-13 - Bug: support for task self-destruction in the OnDisable method
-//                 Example 19: dynamic tasks creation and destruction
-//    2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty
-//                 Example 20: use of local task storage to work with task-specific class objects
-//
-// v3.0.0:
-//    2018-03-15 - Major Release: Support for dynamic callback methods binding via compilation parameter _TASK_OO_CALLBACKS
-//
-// v3.0.1:
-//    2018-11-09 - bug: task deleted from the execution chain cannot be added back (github issue #67)
-//
-// v3.0.2:
-//    2018-11-11 - bug: default constructor is ambiguous when Status Request objects are enabled (github issue #65 & #68)
-//
-// v3.0.3:
-//    2019-06-13 - feature: custom sleep callback method: setSleepMethod() - ability to dynamically control idle sleep for various microcontrollers
-//               - feature: support for MSP430 and MSP432 boards (pull request #75: big thanks to Guillaume Pirou, https://github.com/elominp)
-//               - officially discontinued support for offile documentation in favor of updating the Wiki pages
-//
-// v3.1.0:
-//    2020-01-07 - feature: added 4 cpu load monitoring methods for _TASK_TIMECRITICAL compilation option
-//
-// v3.1.1:
-//    2020-01-09 - update: more precise CPU load measuring. Ability to define idle sleep threshold for ESP chips
-//
-// v3.1.2:
-//    2020-01-17 - bug fix: corrected external forward definitions of millis() and micros
-// 
-// v3.1.3:
-//    2020-01-30 - bug fix: _TASK_DEFINE_MILLIS to force forward definition of millis and micros. Not defined by default. 
-//    2020-02-16 - bug fix: add 'virtual' to the Task destructor definition (issue #86)
-//
-// v3.1.4:
-//    2020-02-22 - bug: get rid of unnecessary compiler warnings
-//    2020-02-22 - feature: access to the task chain with _TASK_EXPOSE_CHAIN compile option
-//
-// v3.1.5:
-//    2020-05-08 - feature: implemented light sleep for esp32
-//
-// v3.1.6:
-//    2020-05-12 - bug fix: deleteTask and addTask should check task ownership first (Issue #97)
-//
-// v3.1.7:
-//    2020-07-07 - warning fix: unused parameter 'aRecursive' (Issue #99)
-//
-// v3.2.0:
-//    2020-08-16 - feature: scheduling options
-//
-// v3.2.1:
-//    2020-10-04 - feature: Task.abort method. Stop task execution without calling OnDisable(). 
-//
-// v3.2.2:
-//    2020-12-14 - feature: enable and restart methods return true if task enabled 
-//                 feature: Task.cancel() method - disable task with a cancel flag (could be used for alt. path
-//                          processing in the onDisable method.
-//                 feature: Task.cancelled() method - indicates that task was disabled with a cancel() method.
-//
-// v3.2.3:
-//    2021-01-01 - feature: discontinued use of 'register' keyword. Depricated in C++ 11 
-//                 feature: add STM32 as a platform supporting _TASK_STD_FUNCTION. (PR #105)
-//
-// v3.3.0:
-//    2021-05-11 - feature: Timeout() methods for StatusRequest objects 
-//
-// v3.4.0:
-//    2021-07-14 - feature: ability to Enable/Disable and Pause/Resume scheduling 
-//               - feature: optional use of external millis/micros methods 
-//
-// v3.5.0:
-//    2021-11-01 - feature: adjust(long aInterval) method - adjust execution schedule: 
-//                 + aInterval - shift schedule forward (later)
-//                 - aInterval - shift schedule backwards (earlier)
-//
-//
+/*
+Cooperative multitasking library for Arduino
+Copyright (c) 2015-2019 Anatoli Arkhipenko
+
+Changelog:
+v1.0.0:
+    2015-02-24 - Initial release
+    2015-02-28 - added delay() and disableOnLastIteration() methods
+    2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
+                 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
+                 2. Delay is invoked only if none of the tasks ran
+                 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
+    2015-05-11 - added  restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
+    2015-05-19 - completely removed  delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+
+v1.4.1:
+    2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
+                 sleep on idle run is no longer a default and should be explicitly compiled with
+                _TASK_SLEEP_ON_IDLE_RUN defined
+
+v1.5.0:
+    2015-09-20 - access to currently executing task (for callback methods)
+    2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
+    2015-09-20 - option to create a task already enabled
+
+v1.5.1:
+    2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
+                 Thanks to Hannes Morgenstern for catching this one
+
+v1.6.0:
+    2015-09-22 - revert back to having all tasks disable on last iteration.
+    2015-09-22 - deprecated disableOnLastIteration method as a result
+    2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
+    2015-10-01 - made version numbers semver compliant (documentation only)
+
+v1.7.0:
+   2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number.
+   2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled,
+                false if was disabled.
+   2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
+   2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called,
+                on disable runs only if task was enabled
+   2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass
+                regardless how much time is left
+
+v1.8.0:
+   2015-10-13 - support for status request objects allowing tasks waiting on requests
+   2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
+
+v1.8.1:
+   2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
+
+v1.8.2:
+   2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
+   2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
+   2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
+   2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
+
+v1.8.3:
+   2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations
+               (0 and 1 are still default values)
+   2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion
+                delayed for one current interval
+   2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
+   2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
+   2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
+
+v1.8.4:
+   2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before.
+                Especially since OnEnable method can change the interval
+   2015-11-16 - further optimizations of the task scheduler execute loop
+
+v1.8.5:
+   2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
+   2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
+
+v1.9.0:
+   2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
+
+v1.9.2:
+   2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
+   2015-12-16 - bug fixes: automatic millis rollover support for delay methods
+   2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
+
+v2.0.0:
+   2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
+
+v2.0.1:
+   2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
+
+v2.0.2:
+   2016-01-05 - bug fix: time constants wrapped inside compile option
+   2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
+
+v2.1.0:
+   2016-02-01 - support for microsecond resolution
+   2016-02-02 - added Scheduler baseline start time reset method: startNow()
+
+v2.2.0:
+   2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
+
+v2.2.1:
+   2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain
+                back together long running callback methods
+   2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled.
+                Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method.
+
+v2.3.0:
+   2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is
+                scheduled to run next time
+
+v2.4.0:
+   2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
+                upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+
+v2.5.0:
+   2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option
+                (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
+   2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
+                Use at your own risk!
+   2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
+   2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
+
+v2.5.1:
+   2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.5)
+
+v2.5.2:
+   2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42)
+
+v2.6.0:
+   2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality
+   2018-01-30 - ESP32 support (experimental)
+                (Contributed by Marco Tombesi: https://github.com/baggior)
+
+v2.6.1:
+   2018-02-13 - Bug: support for task self-destruction in the OnDisable method
+                Example 19: dynamic tasks creation and destruction
+   2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty
+                Example 20: use of local task storage to work with task-specific class objects
+
+v3.0.0:
+   2018-03-15 - Major Release: Support for dynamic callback methods binding via compilation parameter _TASK_OO_CALLBACKS
+
+v3.0.1:
+   2018-11-09 - bug: task deleted from the execution chain cannot be added back (github issue #67)
+
+v3.0.2:
+   2018-11-11 - bug: default constructor is ambiguous when Status Request objects are enabled (github issue #65 & #68)
+
+v3.0.3:
+   2019-06-13 - feature: custom sleep callback method: setSleepMethod() - ability to dynamically control idle sleep for various microcontrollers
+              - feature: support for MSP430 and MSP432 boards (pull request #75: big thanks to Guillaume Pirou, https://github.com/elominp)
+              - officially discontinued support for offile documentation in favor of updating the Wiki pages
+
+v3.1.0:
+   2020-01-07 - feature: added 4 cpu load monitoring methods for _TASK_TIMECRITICAL compilation option
+
+v3.1.1:
+   2020-01-09 - update: more precise CPU load measuring. Ability to define idle sleep threshold for ESP chips
+
+v3.1.2:
+   2020-01-17 - bug fix: corrected external forward definitions of millis() and micros
+
+v3.1.3:
+   2020-01-30 - bug fix: _TASK_DEFINE_MILLIS to force forward definition of millis and micros. Not defined by default. 
+   2020-02-16 - bug fix: add 'virtual' to the Task destructor definition (issue #86)
+
+v3.1.4:
+   2020-02-22 - bug: get rid of unnecessary compiler warnings
+   2020-02-22 - feature: access to the task chain with _TASK_EXPOSE_CHAIN compile option
+
+v3.1.5:
+   2020-05-08 - feature: implemented light sleep for esp32
+
+v3.1.6:
+   2020-05-12 - bug fix: deleteTask and addTask should check task ownership first (Issue #97)
+
+v3.1.7:
+   2020-07-07 - warning fix: unused parameter 'aRecursive' (Issue #99)
+
+v3.2.0:
+   2020-08-16 - feature: scheduling options
+
+v3.2.1:
+   2020-10-04 - feature: Task.abort method. Stop task execution without calling OnDisable(). 
+
+v3.2.2:
+   2020-12-14 - feature: enable and restart methods return true if task enabled 
+                feature: Task.cancel() method - disable task with a cancel flag (could be used for alt. path
+                         processing in the onDisable method.
+                feature: Task.cancelled() method - indicates that task was disabled with a cancel() method.
+
+v3.2.3:
+   2021-01-01 - feature: discontinued use of 'register' keyword. Depricated in C++ 11 
+                feature: add STM32 as a platform supporting _TASK_STD_FUNCTION. (PR #105)
 
 
+v3.3.0:
+   2021-05-11 - feature: Timeout() methods for StatusRequest objects 
+
+v3.4.0:
+   2021-07-14 - feature: ability to Enable/Disable and Pause/Resume scheduling 
+              - feature: optional use of external millis/micros methods 
+
+v3.5.0:
+   2021-11-01 - feature: adjust(long aInterval) method - adjust execution schedule: 
+                + aInterval - shift schedule forward (later)
+                - aInterval - shift schedule backwards (earlier)
+
+v3.6.0:
+   2021-11-01 - feature: _TASK_THREAD_SAFE compile option for multi-core systems or running under RTOS 
+
+
+*/
 
 
 
 
 #include <Arduino.h>
 #include <Arduino.h>
@@ -243,6 +247,7 @@ extern "C" {
 // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
 // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
 // #define _TASK_DEFINE_MILLIS      // Force forward declaration of millis() and micros() "C" style
 // #define _TASK_DEFINE_MILLIS      // Force forward declaration of millis() and micros() "C" style
 // #define _TASK_EXTERNAL_TIME      // Custom millis() and micros() methods
 // #define _TASK_EXTERNAL_TIME      // Custom millis() and micros() methods
+// #define _TASK_THREAD_SAFE        // Enable additional checking for thread safety
 
 
  #ifdef _TASK_MICRO_RES
  #ifdef _TASK_MICRO_RES
 
 
@@ -391,24 +396,48 @@ void StatusRequest::signalComplete(int aStatus) {
  *  If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled.
  *  If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled.
  */
  */
 bool Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
 bool Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iStatusRequest = aStatusRequest;
     iStatusRequest = aStatusRequest;
     if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
     if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
         setIterations(aIterations);
         setIterations(aIterations);
         setInterval(aInterval);
         setInterval(aInterval);
         iStatus.waiting = _TASK_SR_NODELAY;  // no delay
         iStatus.waiting = _TASK_SR_NODELAY;  // no delay
+        
+#ifdef _TASK_THREAD_SAFE
+        iMutex--;
+#endif  // _TASK_THREAD_SAFE
+        
         return enable();
         return enable();
     }
     }
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
     return false;
     return false;
 }
 }
 
 
 bool Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
 bool Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iStatusRequest = aStatusRequest;
     iStatusRequest = aStatusRequest;
     if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
     if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
         setIterations(aIterations);
         setIterations(aIterations);
         if ( aInterval ) setInterval(aInterval);  // For the dealyed version only set the interval if it was not a zero
         if ( aInterval ) setInterval(aInterval);  // For the dealyed version only set the interval if it was not a zero
         iStatus.waiting = _TASK_SR_DELAY;  // with delay equal to the current interval
         iStatus.waiting = _TASK_SR_DELAY;  // with delay equal to the current interval
+#ifdef _TASK_THREAD_SAFE
+        iMutex--;
+#endif  // _TASK_THREAD_SAFE
         return enable();
         return enable();
     }
     }
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
     return false;
     return false;
 }
 }
 
 
@@ -454,6 +483,11 @@ void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; }
  * out of the execution chain as a result
  * out of the execution chain as a result
  */
  */
 void Task::reset() {
 void Task::reset() {
+  
+#ifdef _TASK_THREAD_SAFE
+    iMutex = 1;
+#endif  // _TASK_THREAD_SAFE
+
     iStatus.enabled = false;
     iStatus.enabled = false;
     iStatus.inonenable = false;
     iStatus.inonenable = false;
     iStatus.canceled = false;
     iStatus.canceled = false;
@@ -492,6 +526,10 @@ void Task::reset() {
     iStarttime = 0;
     iStarttime = 0;
     iStatus.timeout = false;
     iStatus.timeout = false;
 #endif  // _TASK_TIMEOUT
 #endif  // _TASK_TIMEOUT
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex = 0;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 /** Explicitly set Task execution parameters
 /** Explicitly set Task execution parameters
@@ -506,13 +544,30 @@ void Task::reset() {
 void Task::set(unsigned long aInterval, long aIterations) {
 void Task::set(unsigned long aInterval, long aIterations) {
 #else
 #else
 void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) {
 void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iCallback = aCallback;
     iCallback = aCallback;
     iOnEnable = aOnEnable;
     iOnEnable = aOnEnable;
     iOnDisable = aOnDisable;
     iOnDisable = aOnDisable;
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
 #endif // _TASK_OO_CALLBACKS
 #endif // _TASK_OO_CALLBACKS
 
 
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     setInterval(aInterval);
     setInterval(aInterval);
     iSetIterations = iIterations = aIterations;
     iSetIterations = iIterations = aIterations;
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
 }
 }
 
 
 /** Sets number of iterations for the task
 /** Sets number of iterations for the task
@@ -520,7 +575,15 @@ void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback
  * @param aIterations - number of iterations, use -1 for no limit
  * @param aIterations - number of iterations, use -1 for no limit
  */
  */
 void Task::setIterations(long aIterations) {
 void Task::setIterations(long aIterations) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iSetIterations = iIterations = aIterations;
     iSetIterations = iIterations = aIterations;
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 #ifndef _TASK_OO_CALLBACKS
 #ifndef _TASK_OO_CALLBACKS
@@ -529,6 +592,10 @@ void Task::setIterations(long aIterations) {
  * @param aCallback - pointer to the callback method for the next step
  * @param aCallback - pointer to the callback method for the next step
  */
  */
 void Task::yield (TaskCallback aCallback) {
 void Task::yield (TaskCallback aCallback) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iCallback = aCallback;
     iCallback = aCallback;
     forceNextIteration();
     forceNextIteration();
 
 
@@ -537,14 +604,26 @@ void Task::yield (TaskCallback aCallback) {
     // a series of callback methods
     // a series of callback methods
     iRunCounter--;
     iRunCounter--;
     if ( iIterations >= 0 ) iIterations++;
     if ( iIterations >= 0 ) iIterations++;
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 /** Prepare task for next step iteration following yielding of control to the scheduler
 /** Prepare task for next step iteration following yielding of control to the scheduler
  * @param aCallback - pointer to the callback method for the next step
  * @param aCallback - pointer to the callback method for the next step
  */
  */
 void Task::yieldOnce (TaskCallback aCallback) {
 void Task::yieldOnce (TaskCallback aCallback) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     yield(aCallback);
     yield(aCallback);
     iIterations = 1;
     iIterations = 1;
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 #endif // _TASK_OO_CALLBACKS
 #endif // _TASK_OO_CALLBACKS
 
 
@@ -555,6 +634,11 @@ void Task::yieldOnce (TaskCallback aCallback) {
  */
  */
 bool Task::enable() {
 bool Task::enable() {
     if (iScheduler) { // activation without active scheduler does not make sense
     if (iScheduler) { // activation without active scheduler does not make sense
+
+#ifdef _TASK_THREAD_SAFE
+        iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
         iRunCounter = 0;
         iRunCounter = 0;
         iStatus.canceled = false;
         iStatus.canceled = false;
 
 
@@ -592,6 +676,11 @@ bool Task::enable() {
             iMyStatusRequest.setWaiting();
             iMyStatusRequest.setWaiting();
 #endif // _TASK_STATUS_REQUEST
 #endif // _TASK_STATUS_REQUEST
         }
         }
+
+#ifdef _TASK_THREAD_SAFE
+        iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
         return iStatus.enabled;
         return iStatus.enabled;
     }
     }
     return false;
     return false;
@@ -601,8 +690,17 @@ bool Task::enable() {
  * Returns previous state (true if was already enabled, false if was not)
  * Returns previous state (true if was already enabled, false if was not)
  */
  */
 bool Task::enableIfNot() {
 bool Task::enableIfNot() {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     bool previousEnabled = iStatus.enabled;
     bool previousEnabled = iStatus.enabled;
     if ( !previousEnabled ) enable();
     if ( !previousEnabled ) enable();
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
     return (previousEnabled);
     return (previousEnabled);
 }
 }
 
 
@@ -610,20 +708,45 @@ bool Task::enableIfNot() {
  * and schedules it for execution after a delay = aInterval
  * and schedules it for execution after a delay = aInterval
  */
  */
 bool Task::enableDelayed(unsigned long aDelay) {
 bool Task::enableDelayed(unsigned long aDelay) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     enable();
     enable();
     delay(aDelay);
     delay(aDelay);
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
+
     return iStatus.enabled;
     return iStatus.enabled;
 }
 }
 
 
 #ifdef _TASK_TIMEOUT
 #ifdef _TASK_TIMEOUT
 void Task::setTimeout(unsigned long aTimeout, bool aReset) {
 void Task::setTimeout(unsigned long aTimeout, bool aReset) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iTimeout = aTimeout;
     iTimeout = aTimeout;
     if (aReset) resetTimeout();
     if (aReset) resetTimeout();
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 void Task::resetTimeout() {
 void Task::resetTimeout() {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iStarttime = _TASK_TIME_FUNCTION();
     iStarttime = _TASK_TIME_FUNCTION();
     iStatus.timeout = false;
     iStatus.timeout = false;
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 unsigned long Task::getTimeout() {
 unsigned long Task::getTimeout() {
@@ -650,14 +773,27 @@ bool Task::timedOut() {
  * if aDelay is zero, delays for the original scheduling interval from now
  * if aDelay is zero, delays for the original scheduling interval from now
  */
  */
 void Task::delay(unsigned long aDelay) {
 void Task::delay(unsigned long aDelay) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iDelay = aDelay ? aDelay : iInterval;
     iDelay = aDelay ? aDelay : iInterval;
     iPreviousMillis = _TASK_TIME_FUNCTION(); 
     iPreviousMillis = _TASK_TIME_FUNCTION(); 
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 /** Adjusts Task execution with aInterval (if task is enabled).
 /** Adjusts Task execution with aInterval (if task is enabled).
  */
  */
 void Task::adjust(long aInterval) {
 void Task::adjust(long aInterval) {
     if ( aInterval == 0 ) return;  //  nothing to do for a zero
     if ( aInterval == 0 ) return;  //  nothing to do for a zero
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     if ( aInterval < 0 ) {
     if ( aInterval < 0 ) {
       iPreviousMillis += aInterval;
       iPreviousMillis += aInterval;
     }
     }
@@ -665,6 +801,9 @@ void Task::adjust(long aInterval) {
       iDelay += aInterval;  //  we have to adjust delay because adjusting iPreviousMillis might push
       iDelay += aInterval;  //  we have to adjust delay because adjusting iPreviousMillis might push
                             //  it into the future beyond current millis() and cause premature trigger
                             //  it into the future beyond current millis() and cause premature trigger
     }
     }
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 
 
@@ -673,7 +812,15 @@ void Task::adjust(long aInterval) {
  * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
  * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
  */
  */
 void Task::forceNextIteration() {
 void Task::forceNextIteration() {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
     iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 /** Sets the execution interval.
 /** Sets the execution interval.
@@ -682,8 +829,16 @@ void Task::forceNextIteration() {
  * @param aInterval - new execution interval
  * @param aInterval - new execution interval
  */
  */
 void Task::setInterval (unsigned long aInterval) {
 void Task::setInterval (unsigned long aInterval) {
+#ifdef _TASK_THREAD_SAFE
+    iMutex++;
+#endif  // _TASK_THREAD_SAFE
+
     iInterval = aInterval;
     iInterval = aInterval;
     delay(); // iDelay will be updated by the delay() function
     delay(); // iDelay will be updated by the delay() function
+
+#ifdef _TASK_THREAD_SAFE
+    iMutex--;
+#endif  // _TASK_THREAD_SAFE
 }
 }
 
 
 /** Disables task
 /** Disables task
@@ -807,12 +962,13 @@ Scheduler::~Scheduler() {
 /** Initializes all internal varaibles
 /** Initializes all internal varaibles
  */
  */
 void Scheduler::init() {
 void Scheduler::init() {
+    iEnabled = false;
+    
     iFirst = NULL;
     iFirst = NULL;
     iLast = NULL;
     iLast = NULL;
     iCurrent = NULL;
     iCurrent = NULL;
 
 
     iPaused = false;
     iPaused = false;
-    iEnabled = true;  
 
 
 #ifdef _TASK_PRIORITY
 #ifdef _TASK_PRIORITY
     iHighPriority = NULL;
     iHighPriority = NULL;
@@ -825,6 +981,8 @@ void Scheduler::init() {
 #ifdef _TASK_TIMECRITICAL
 #ifdef _TASK_TIMECRITICAL
     cpuLoadReset();
     cpuLoadReset();
 #endif  // _TASK_TIMECRITICAL
 #endif  // _TASK_TIMECRITICAL
+
+    iEnabled = true;  
 }
 }
 
 
 /** Appends task aTask to the tail of the execution chain.
 /** Appends task aTask to the tail of the execution chain.
@@ -832,12 +990,13 @@ void Scheduler::init() {
  * @note Task can only be part of the chain once.
  * @note Task can only be part of the chain once.
  */
  */
  void Scheduler::addTask(Task& aTask) {
  void Scheduler::addTask(Task& aTask) {
-
 // If task already belongs to a scheduler, we should not be adding
 // If task already belongs to a scheduler, we should not be adding
 // it to this scheduler. It should be deleted from the other scheduler first. 
 // it to this scheduler. It should be deleted from the other scheduler first. 
     if (aTask.iScheduler != NULL)
     if (aTask.iScheduler != NULL)
         return;
         return;
 
 
+    iEnabled = false;
+
     aTask.iScheduler = this;
     aTask.iScheduler = this;
 // First task situation:
 // First task situation:
     if (iFirst == NULL) {
     if (iFirst == NULL) {
@@ -852,6 +1011,8 @@ void Scheduler::init() {
 // "Previous" last task gets linked to this one - as this one becomes the last one
 // "Previous" last task gets linked to this one - as this one becomes the last one
     aTask.iNext = NULL;
     aTask.iNext = NULL;
     iLast = &aTask;
     iLast = &aTask;
+
+    iEnabled = true;
 }
 }
 
 
 /** Deletes specific Task from the execution chain
 /** Deletes specific Task from the execution chain
@@ -862,17 +1023,21 @@ void Scheduler::deleteTask(Task& aTask) {
     if (aTask.iScheduler != this) 
     if (aTask.iScheduler != this) 
         return;
         return;
     
     
+    iEnabled = false;
+
     aTask.iScheduler = NULL;
     aTask.iScheduler = NULL;
     if (aTask.iPrev == NULL) {
     if (aTask.iPrev == NULL) {
         if (aTask.iNext == NULL) {
         if (aTask.iNext == NULL) {
             iFirst = NULL;
             iFirst = NULL;
             iLast = NULL;
             iLast = NULL;
+            iEnabled = true;
             return;
             return;
         }
         }
         else {
         else {
             aTask.iNext->iPrev = NULL;
             aTask.iNext->iPrev = NULL;
             iFirst = aTask.iNext;
             iFirst = aTask.iNext;
             aTask.iNext = NULL;
             aTask.iNext = NULL;
+            iEnabled = true;
             return;
             return;
         }
         }
     }
     }
@@ -881,6 +1046,7 @@ void Scheduler::deleteTask(Task& aTask) {
         aTask.iPrev->iNext = NULL;
         aTask.iPrev->iNext = NULL;
         iLast = aTask.iPrev;
         iLast = aTask.iPrev;
         aTask.iPrev = NULL;
         aTask.iPrev = NULL;
+        iEnabled = true;
         return;
         return;
     }
     }
 
 
@@ -888,6 +1054,8 @@ void Scheduler::deleteTask(Task& aTask) {
     aTask.iNext->iPrev = aTask.iPrev;
     aTask.iNext->iPrev = aTask.iPrev;
     aTask.iPrev = NULL;
     aTask.iPrev = NULL;
     aTask.iNext = NULL;
     aTask.iNext = NULL;
+    
+    iEnabled = true;
 }
 }
 
 
 /** Disables all tasks in the execution chain
 /** Disables all tasks in the execution chain
@@ -900,6 +1068,9 @@ void Scheduler::disableAll(bool aRecursive) {
 #else
 #else
 void Scheduler::disableAll() {
 void Scheduler::disableAll() {
 #endif
 #endif
+
+    iEnabled = false;
+    
     Task    *current = iFirst;
     Task    *current = iFirst;
     while (current) {
     while (current) {
         current->disable();
         current->disable();
@@ -909,6 +1080,8 @@ void Scheduler::disableAll() {
 #ifdef _TASK_PRIORITY
 #ifdef _TASK_PRIORITY
     if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
     if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
 #endif  // _TASK_PRIORITY
 #endif  // _TASK_PRIORITY
+
+    iEnabled = true;
 }
 }
 
 
 
 
@@ -920,6 +1093,9 @@ void Scheduler::enableAll(bool aRecursive) {
 #else
 #else
 void Scheduler::enableAll() {
 void Scheduler::enableAll() {
 #endif    
 #endif    
+
+    iEnabled = false;
+    
     Task    *current = iFirst;
     Task    *current = iFirst;
     while (current) {
     while (current) {
         current->enable();
         current->enable();
@@ -930,6 +1106,7 @@ void Scheduler::enableAll() {
     if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
     if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
 #endif  // _TASK_PRIORITY
 #endif  // _TASK_PRIORITY
 
 
+    iEnabled = true;
 }
 }
 
 
 /** Sets scheduler for the higher priority tasks (support for layered task priority)
 /** Sets scheduler for the higher priority tasks (support for layered task priority)
@@ -963,6 +1140,8 @@ void Scheduler::startNow() {
 #endif
 #endif
     unsigned long t = _TASK_TIME_FUNCTION();
     unsigned long t = _TASK_TIME_FUNCTION();
 
 
+    iEnabled = false;
+    
     iCurrent = iFirst;
     iCurrent = iFirst;
     while (iCurrent) {
     while (iCurrent) {
         if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
         if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
@@ -972,6 +1151,8 @@ void Scheduler::startNow() {
 #ifdef _TASK_PRIORITY
 #ifdef _TASK_PRIORITY
     if (aRecursive && iHighPriority) iHighPriority->startNow( true );
     if (aRecursive && iHighPriority) iHighPriority->startNow( true );
 #endif  // _TASK_PRIORITY
 #endif  // _TASK_PRIORITY
+
+    iEnabled = true;
 }
 }
 
 
 /** Returns number millis or micros until next scheduled iteration of a given task
 /** Returns number millis or micros until next scheduled iteration of a given task
@@ -1067,7 +1248,7 @@ bool Scheduler::execute() {
         iCurrentScheduler = this;
         iCurrentScheduler = this;
 #endif  // _TASK_PRIORITY
 #endif  // _TASK_PRIORITY
 
 
-    //  each scheduled is enabled/disabled individually, so check iEnabed only
+    //  each scheduled is enabled/disabled individually, so check iEnabled only
     //  after the higher priority scheduler has been invoked.
     //  after the higher priority scheduler has been invoked.
     if ( !iEnabled ) return true; //  consider this to be an idle run
     if ( !iEnabled ) return true; //  consider this to be an idle run
 
 
@@ -1087,6 +1268,12 @@ bool Scheduler::execute() {
         do {
         do {
             if ( iCurrent->iStatus.enabled ) {
             if ( iCurrent->iStatus.enabled ) {
 
 
+#ifdef _TASK_THREAD_SAFE
+            //  this task is in the scheduling state and should not be invoked
+            //  as there could be incosistent settings until scheduling is done
+            if ( iCurrent->iMutex ) break;
+#endif  // _TASK_THREAD_SAFE
+
 #ifdef _TASK_WDT_IDS
 #ifdef _TASK_WDT_IDS
     // For each task the control points are initialized to avoid confusion because of carry-over:
     // For each task the control points are initialized to avoid confusion because of carry-over:
                 iCurrent->iControlPoint = 0;
                 iCurrent->iControlPoint = 0;

+ 15 - 10
src/TaskSchedulerDeclarations.h

@@ -27,6 +27,7 @@
 // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
 // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
 // #define _TASK_DEFINE_MILLIS      // Force forward declaration of millis() and micros() "C" style
 // #define _TASK_DEFINE_MILLIS      // Force forward declaration of millis() and micros() "C" style
 // #define _TASK_EXTERNAL_TIME      // Custom millis() and micros() methods
 // #define _TASK_EXTERNAL_TIME      // Custom millis() and micros() methods
+// #define _TASK_THREAD_SAFE        // Enable additional checking for thread safety
 
 
 class Scheduler;
 class Scheduler;
 
 
@@ -141,17 +142,16 @@ typedef bool (*TaskOnEnable)();
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 
 typedef struct  {
 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  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()
 #ifdef _TASK_STATUS_REQUEST
 #ifdef _TASK_STATUS_REQUEST
-    uint8_t  waiting : 2;           // indication if task is waiting on the status request
+    uint8_t  waiting    : 2;           // indication if task is waiting on the status request
 #endif
 #endif
 
 
 #ifdef _TASK_TIMEOUT
 #ifdef _TASK_TIMEOUT
-    bool  timeout    : 1;           // indication if task timed out
+    bool  timeout       : 1;           // indication if task timed out
 #endif
 #endif
-
 } __task_status;
 } __task_status;
 
 
 
 
@@ -310,6 +310,11 @@ class Task {
     unsigned long            iTimeout;               // Task overall timeout
     unsigned long            iTimeout;               // Task overall timeout
     unsigned long            iStarttime;             // millis at task start time
     unsigned long            iStarttime;             // millis at task start time
 #endif // _TASK_TIMEOUT
 #endif // _TASK_TIMEOUT
+
+
+#ifdef _TASK_THREAD_SAFE
+    volatile uint8_t          iMutex;                // a mutex to pause scheduling during chages to the task
+#endif
 };
 };
 
 
 class Scheduler {
 class Scheduler {
@@ -366,15 +371,15 @@ class Scheduler {
 #endif // _TASK_EXPOSE_CHAIN
 #endif // _TASK_EXPOSE_CHAIN
 
 
   _TASK_SCOPE:
   _TASK_SCOPE:
-    Task       *iFirst, *iLast, *iCurrent;        // pointers to first, last and current tasks in the chain
+    Task          *iFirst, *iLast, *iCurrent;        // pointers to first, last and current tasks in the chain
 
 
-    bool       iPaused, iEnabled;
+    volatile bool iPaused, iEnabled;
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
-    bool        iAllowSleep;                      // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
+    bool          iAllowSleep;                      // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 
 #ifdef _TASK_PRIORITY
 #ifdef _TASK_PRIORITY
-    Scheduler  *iHighPriority;                    // Pointer to a higher priority scheduler
+    Scheduler*    iHighPriority;                    // Pointer to a higher priority scheduler
 #endif  // _TASK_PRIORITY
 #endif  // _TASK_PRIORITY
 
 
 #ifdef _TASK_TIMECRITICAL
 #ifdef _TASK_TIMECRITICAL