TaskSchedulerDeclarations.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // Cooperative multitasking library for Arduino
  2. // Copyright (c) 2015-2023 Anatoli Arkhipenko
  3. #include <stddef.h>
  4. #include <stdint.h>
  5. #ifndef _TASKSCHEDULERDECLARATIONS_H_
  6. #define _TASKSCHEDULERDECLARATIONS_H_
  7. // ----------------------------------------
  8. // The following "defines" control library functionality at compile time,
  9. // and should be used in the main sketch depending on the functionality required
  10. //
  11. // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
  12. // #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
  13. // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
  14. // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
  15. // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
  16. // #define _TASK_PRIORITY // Support for layered scheduling priority
  17. // #define _TASK_MICRO_RES // Support for microsecond resolution
  18. // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
  19. // #define _TASK_DEBUG // Make all methods and variables public for debug purposes
  20. // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
  21. // #define _TASK_TIMEOUT // Support for overall task timeout
  22. // #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance
  23. // #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
  24. // #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
  25. // #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
  26. // #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods
  27. // #define _TASK_THREAD_SAFE // Enable additional checking for thread safety
  28. // #define _TASK_SELF_DESTRUCT // Enable tasks to "self-destruct" after disable
  29. // #define _TASK_TICKLESS // Enable support for tickless sleep on FreeRTOS
  30. // #define _TASK_DO_NOT_YIELD // Disable yield() method in execute()
  31. class Scheduler;
  32. #define TASK_SCHEDULE 0 // default
  33. #define TASK_SCHEDULE_NC 1 // schedule + no catch-ups (always in the future)
  34. #define TASK_INTERVAL 2 // interval (always in the future)
  35. #ifdef _TASK_DEBUG
  36. #define _TASK_SCOPE public
  37. #else
  38. #define _TASK_SCOPE private
  39. #endif
  40. // task scheduling iteration common options
  41. #define TASK_IMMEDIATE 0
  42. #define TASK_FOREVER (-1)
  43. #define TASK_ONCE 1
  44. // options for setIntervalNodelay() method
  45. #define TASK_INTERVAL_KEEP 0
  46. #define TASK_INTERVAL_RECALC 1
  47. #define TASK_INTERVAL_RESET 2
  48. #ifdef _TASK_TIMEOUT
  49. #define TASK_NOTIMEOUT 0
  50. #endif
  51. #ifdef _TASK_PRIORITY
  52. extern Scheduler* iCurrentScheduler;
  53. #endif // _TASK_PRIORITY
  54. #ifdef _TASK_INLINE
  55. #define INLINE inline
  56. #else
  57. #define INLINE
  58. #endif
  59. #ifdef _TASK_EXTERNAL_TIME
  60. #define _task_millis() external_millis()
  61. #define _task_micros() external_micros()
  62. #endif // _TASK_EXTERNAL_TIME
  63. #ifndef _TASK_MICRO_RES
  64. #define TASK_MILLISECOND 1UL
  65. #define TASK_SECOND 1000UL
  66. #define TASK_MINUTE 60000UL
  67. #define TASK_HOUR 3600000UL
  68. #else
  69. #define TASK_MILLISECOND 1000UL
  70. #define TASK_SECOND 1000000UL
  71. #define TASK_MINUTE 60000000UL
  72. #define TASK_HOUR 3600000000UL
  73. #endif // _TASK_MICRO_RES
  74. #ifdef _TASK_TICKLESS
  75. #define _TASK_NEXTRUN_UNDEFINED 0b0
  76. #define _TASK_NEXTRUN_IMMEDIATE 0b1
  77. #define _TASK_NEXTRUN_TIMED 0x10
  78. #endif // _TASK_TICKLESS
  79. #ifdef _TASK_STATUS_REQUEST
  80. #define TASK_SR_OK 0
  81. #define TASK_SR_ERROR (-1)
  82. #define TASK_SR_CANCEL (-32766)
  83. #define TASK_SR_ABORT (-32767)
  84. #define TASK_SR_TIMEOUT (-32768)
  85. #define _TASK_SR_NODELAY 1
  86. #define _TASK_SR_DELAY 2
  87. class StatusRequest {
  88. friend class Scheduler;
  89. public:
  90. INLINE StatusRequest();
  91. INLINE void setWaiting(unsigned int aCount = 1);
  92. INLINE bool signal(int aStatus = 0);
  93. INLINE void signalComplete(int aStatus = 0);
  94. INLINE bool pending();
  95. INLINE bool completed();
  96. INLINE int getStatus();
  97. INLINE int getCount();
  98. #ifdef _TASK_TIMEOUT
  99. INLINE void setTimeout(unsigned long aTimeout) { iTimeout = aTimeout; };
  100. INLINE unsigned long getTimeout() { return iTimeout; };
  101. INLINE void resetTimeout();
  102. INLINE long untilTimeout();
  103. #endif
  104. _TASK_SCOPE:
  105. unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
  106. int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status (see TASK_SR_ constants)
  107. #ifdef _TASK_TIMEOUT
  108. unsigned long iTimeout; // Task overall timeout
  109. unsigned long iStarttime; // millis at task start time
  110. #endif // _TASK_TIMEOUT
  111. };
  112. #endif // _TASK_STATUS_REQUEST
  113. #ifdef _TASK_STD_FUNCTION
  114. #include <functional>
  115. typedef std::function<void()> TaskCallback;
  116. typedef std::function<void()> TaskOnDisable;
  117. typedef std::function<bool()> TaskOnEnable;
  118. #else
  119. typedef void (*TaskCallback)();
  120. typedef void (*TaskOnDisable)();
  121. typedef bool (*TaskOnEnable)();
  122. #endif // _TASK_STD_FUNCTION
  123. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  124. typedef void (*SleepCallback)( unsigned long aDuration );
  125. extern Scheduler* iSleepScheduler;
  126. extern SleepCallback iSleepMethod;
  127. #endif // _TASK_SLEEP_ON_IDLE_RUN
  128. typedef struct {
  129. bool enabled : 1; // indicates that task is enabled or not.
  130. bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops)
  131. bool canceled : 1; // indication that task has been canceled prior to normal end of all iterations or regular call to disable()
  132. #ifdef _TASK_SELF_DESTRUCT
  133. bool selfdestruct : 1; // indication that task has been requested to self-destruct on disable
  134. bool sd_request : 1; // request for scheduler to delete task object and take task out of the queue
  135. #endif // _TASK_SELF_DESTRUCT
  136. #ifdef _TASK_STATUS_REQUEST
  137. uint8_t waiting : 2; // indication if task is waiting on the status request
  138. #endif // _TASK_STATUS_REQUEST
  139. #ifdef _TASK_TIMEOUT
  140. bool timeout : 1; // indication if task timed out
  141. #endif // _TASK_TIMEOUT
  142. } __task_status;
  143. class Task {
  144. friend class Scheduler;
  145. public:
  146. #ifdef _TASK_OO_CALLBACKS
  147. INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=false
  148. #ifdef _TASK_SELF_DESTRUCT
  149. , bool aSelfDestruct=false);
  150. #else
  151. );
  152. #endif // #ifdef _TASK_SELF_DESTRUCT
  153. #else
  154. INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL
  155. #ifdef _TASK_SELF_DESTRUCT
  156. , bool aSelfDestruct=false);
  157. #else
  158. );
  159. #endif // #ifdef _TASK_SELF_DESTRUCT
  160. #endif // _TASK_OO_CALLBACKS
  161. #ifdef _TASK_STATUS_REQUEST
  162. #ifdef _TASK_OO_CALLBACKS
  163. // INLINE Task(Scheduler* aScheduler=NULL);
  164. INLINE Task(Scheduler* aScheduler);
  165. #else
  166. // INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
  167. INLINE Task(TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
  168. #endif // _TASK_OO_CALLBACKS
  169. #endif // _TASK_STATUS_REQUEST
  170. virtual INLINE ~Task();
  171. #ifdef _TASK_TIMEOUT
  172. INLINE void setTimeout(unsigned long aTimeout, bool aReset=false);
  173. INLINE void resetTimeout();
  174. INLINE unsigned long getTimeout();
  175. INLINE long untilTimeout();
  176. INLINE bool timedOut();
  177. #endif
  178. INLINE bool enable();
  179. INLINE bool enableIfNot();
  180. INLINE bool enableDelayed(unsigned long aDelay=0);
  181. INLINE bool restart();
  182. INLINE bool restartDelayed(unsigned long aDelay=0);
  183. INLINE void delay(unsigned long aDelay=0);
  184. INLINE void adjust(long aInterval);
  185. INLINE void forceNextIteration();
  186. INLINE bool disable();
  187. INLINE void abort();
  188. INLINE void cancel();
  189. INLINE bool isEnabled();
  190. INLINE bool canceled();
  191. #ifdef _TASK_SCHEDULING_OPTIONS
  192. INLINE unsigned int getSchedulingOption() { return iOption; }
  193. INLINE void setSchedulingOption(unsigned int aOption) { iOption = aOption; }
  194. #endif //_TASK_SCHEDULING_OPTIONS
  195. #ifdef _TASK_OO_CALLBACKS
  196. INLINE void set(unsigned long aInterval, long aIterations);
  197. #else
  198. INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
  199. #endif // _TASK_OO_CALLBACKS
  200. INLINE void setInterval(unsigned long aInterval);
  201. INLINE void setIntervalNodelay(unsigned long aInterval, unsigned int aOption = TASK_INTERVAL_KEEP);
  202. INLINE unsigned long getInterval();
  203. INLINE void setIterations(long aIterations);
  204. INLINE long getIterations();
  205. INLINE unsigned long getRunCounter();
  206. #ifdef _TASK_SELF_DESTRUCT
  207. INLINE void setSelfDestruct(bool aSelfDestruct=true) { iStatus.selfdestruct = aSelfDestruct; }
  208. INLINE bool getSelfDestruct() { return iStatus.selfdestruct; }
  209. #endif // #ifdef _TASK_SELF_DESTRUCT
  210. #ifdef _TASK_OO_CALLBACKS
  211. virtual INLINE bool Callback() =0; // return true if run was "productive - this will disable sleep on the idle run for next pass
  212. virtual INLINE bool OnEnable(); // return true if task should be enabled, false if it should remain disabled
  213. virtual INLINE void OnDisable();
  214. #else
  215. INLINE void setCallback(TaskCallback aCallback) ;
  216. INLINE void setOnEnable(TaskOnEnable aCallback) ;
  217. INLINE void setOnDisable(TaskOnDisable aCallback) ;
  218. INLINE void yield(TaskCallback aCallback);
  219. INLINE void yieldOnce(TaskCallback aCallback);
  220. #endif // _TASK_OO_CALLBACKS
  221. INLINE bool isFirstIteration() ;
  222. INLINE bool isLastIteration() ;
  223. #ifdef _TASK_TIMECRITICAL
  224. INLINE long getOverrun() ;
  225. INLINE long getStartDelay() ;
  226. #endif // _TASK_TIMECRITICAL
  227. #ifdef _TASK_STATUS_REQUEST
  228. INLINE bool waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
  229. INLINE bool waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
  230. INLINE StatusRequest* getStatusRequest() ;
  231. INLINE StatusRequest* getInternalStatusRequest() ;
  232. #endif // _TASK_STATUS_REQUEST
  233. #ifdef _TASK_WDT_IDS
  234. INLINE void setId(unsigned int aID) ;
  235. INLINE unsigned int getId() ;
  236. INLINE void setControlPoint(unsigned int aPoint) ;
  237. INLINE unsigned int getControlPoint() ;
  238. #endif // _TASK_WDT_IDS
  239. #ifdef _TASK_LTS_POINTER
  240. INLINE void setLtsPointer(void *aPtr) ;
  241. INLINE void* getLtsPointer() ;
  242. #endif // _TASK_LTS_POINTER
  243. #ifdef _TASK_EXPOSE_CHAIN
  244. INLINE Task* getPreviousTask() { return iPrev; }; // pointer to the previous task in the chain, NULL if first or not set
  245. INLINE Task* getNextTask() { return iNext; }; // pointer to the next task in the chain, NULL if last or not set
  246. #endif // _TASK_EXPOSE_CHAIN
  247. _TASK_SCOPE:
  248. INLINE void reset();
  249. volatile __task_status iStatus;
  250. volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate
  251. volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval)
  252. volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up"
  253. #ifdef _TASK_SCHEDULING_OPTIONS
  254. unsigned int iOption; // scheduling option
  255. #endif // _TASK_SCHEDULING_OPTIONS
  256. #ifdef _TASK_TIMECRITICAL
  257. volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
  258. volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis
  259. #endif // _TASK_TIMECRITICAL
  260. volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations
  261. long iSetIterations; // number of iterations originally requested (for restarts)
  262. unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable.
  263. #ifndef _TASK_OO_CALLBACKS
  264. TaskCallback iCallback; // pointer to the void callback method
  265. TaskOnEnable iOnEnable; // pointer to the bool OnEnable callback method
  266. TaskOnDisable iOnDisable; // pointer to the void OnDisable method
  267. #endif // _TASK_OO_CALLBACKS
  268. Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain
  269. Scheduler *iScheduler; // pointer to the current scheduler
  270. #ifdef _TASK_STATUS_REQUEST
  271. StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on
  272. StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion
  273. #endif // _TASK_STATUS_REQUEST
  274. #ifdef _TASK_WDT_IDS
  275. unsigned int iTaskID; // task ID (for debugging and watchdog identification)
  276. unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
  277. #endif // _TASK_WDT_IDS
  278. #ifdef _TASK_LTS_POINTER
  279. void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
  280. #endif // _TASK_LTS_POINTER
  281. #ifdef _TASK_TIMEOUT
  282. unsigned long iTimeout; // Task overall timeout
  283. unsigned long iStarttime; // millis at task start time
  284. #endif // _TASK_TIMEOUT
  285. #ifdef _TASK_THREAD_SAFE
  286. volatile uint8_t iMutex; // a mutex to pause scheduling during chages to the task
  287. #endif
  288. };
  289. class Scheduler {
  290. friend class Task;
  291. public:
  292. INLINE Scheduler();
  293. // ~Scheduler();
  294. INLINE void init();
  295. INLINE void addTask(Task& aTask);
  296. INLINE void deleteTask(Task& aTask);
  297. INLINE void pause() { iPaused = true; };
  298. INLINE void resume() { iPaused = false; };
  299. INLINE void enable() { iEnabled = true; };
  300. INLINE void disable() { iEnabled = false; };
  301. #ifdef _TASK_PRIORITY
  302. INLINE void disableAll(bool aRecursive = true);
  303. INLINE void enableAll(bool aRecursive = true);
  304. INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW.
  305. #else
  306. INLINE void disableAll();
  307. INLINE void enableAll();
  308. INLINE void startNow(); // reset ALL active tasks to immediate execution NOW.
  309. #endif
  310. #ifdef _TASK_TICKLESS
  311. INLINE bool execute(unsigned long* aNextRun); // Returns true if none of the tasks' callback methods was invoked (true = idle run)
  312. #else
  313. INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run)
  314. #endif
  315. INLINE Task& currentTask() ; // DEPRICATED
  316. INLINE Task* getCurrentTask() ; // Returns pointer to the currently active task
  317. INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task
  318. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  319. INLINE void allowSleep(bool aState = true);
  320. INLINE void setSleepMethod( SleepCallback aCallback );
  321. #endif // _TASK_SLEEP_ON_IDLE_RUN
  322. #ifdef _TASK_LTS_POINTER
  323. INLINE void* currentLts();
  324. #endif // _TASK_LTS_POINTER
  325. #ifdef _TASK_TIMECRITICAL
  326. INLINE bool isOverrun();
  327. INLINE void cpuLoadReset();
  328. INLINE unsigned long getCpuLoadCycle(){ return iCPUCycle; };
  329. INLINE unsigned long getCpuLoadIdle() { return iCPUIdle; };
  330. INLINE unsigned long getCpuLoadTotal();
  331. #endif // _TASK_TIMECRITICAL
  332. #ifdef _TASK_PRIORITY
  333. INLINE void setHighPriorityScheduler(Scheduler* aScheduler);
  334. INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
  335. #endif // _TASK_PRIORITY
  336. #ifdef _TASK_EXPOSE_CHAIN
  337. INLINE Task* getFirstTask() { return iFirst; }; // pointer to the previous task in the chain, NULL if first or not set
  338. INLINE Task* getLastTask() { return iLast; }; // pointer to the next task in the chain, NULL if last or not set
  339. #endif // _TASK_EXPOSE_CHAIN
  340. _TASK_SCOPE:
  341. Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain
  342. volatile bool iPaused, iEnabled;
  343. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  344. bool iAllowSleep; // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
  345. #endif // _TASK_SLEEP_ON_IDLE_RUN
  346. #ifdef _TASK_PRIORITY
  347. Scheduler* iHighPriority; // Pointer to a higher priority scheduler
  348. #endif // _TASK_PRIORITY
  349. #ifdef _TASK_TIMECRITICAL
  350. unsigned long iCPUStart;
  351. unsigned long iCPUCycle;
  352. unsigned long iCPUIdle;
  353. #endif // _TASK_TIMECRITICAL
  354. #ifdef _TASK_TICKLESS
  355. unsigned long iNextRun;
  356. unsigned int iNextRunDetermined;
  357. #endif
  358. };
  359. #endif /* _TASKSCHEDULERDECLARATIONS_H_ */