394 lines
16 KiB
C++
394 lines
16 KiB
C++
// Cooperative multitasking library for Arduino
|
|
// Copyright (c) 2015-2019 Anatoli Arkhipenko
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifndef _TASKSCHEDULERDECLARATIONS_H_
|
|
#define _TASKSCHEDULERDECLARATIONS_H_
|
|
|
|
// ----------------------------------------
|
|
// 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
|
|
|
|
class Scheduler;
|
|
|
|
#define TASK_SCHEDULE 0 // default
|
|
#define TASK_SCHEDULE_NC 1 // schedule + no catch-ups (always in the future)
|
|
#define TASK_INTERVAL 2 // interval (always in the future)
|
|
|
|
#ifdef _TASK_DEBUG
|
|
#define _TASK_SCOPE public
|
|
#else
|
|
#define _TASK_SCOPE private
|
|
#endif
|
|
|
|
#define TASK_IMMEDIATE 0
|
|
#define TASK_FOREVER (-1)
|
|
#define TASK_ONCE 1
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
#define TASK_NOTIMEOUT 0
|
|
#endif
|
|
|
|
#ifdef _TASK_PRIORITY
|
|
extern Scheduler* iCurrentScheduler;
|
|
#endif // _TASK_PRIORITY
|
|
|
|
#ifdef _TASK_INLINE
|
|
#define INLINE inline
|
|
#else
|
|
#define INLINE
|
|
#endif
|
|
|
|
#ifdef _TASK_EXTERNAL_TIME
|
|
#define _task_millis() external_millis()
|
|
#define _task_micros() external_micros()
|
|
#endif // _TASK_EXTERNAL_TIME
|
|
|
|
#ifndef _TASK_MICRO_RES
|
|
|
|
#define TASK_MILLISECOND 1UL
|
|
#define TASK_SECOND 1000UL
|
|
#define TASK_MINUTE 60000UL
|
|
#define TASK_HOUR 3600000UL
|
|
|
|
#else
|
|
|
|
#define TASK_MILLISECOND 1000UL
|
|
#define TASK_SECOND 1000000UL
|
|
#define TASK_MINUTE 60000000UL
|
|
#define TASK_HOUR 3600000000UL
|
|
|
|
#endif // _TASK_MICRO_RES
|
|
|
|
|
|
#ifdef _TASK_STATUS_REQUEST
|
|
|
|
#define TASK_SR_OK 0
|
|
#define TASK_SR_ERROR (-1)
|
|
#define TASK_SR_TIMEOUT (-99)
|
|
|
|
#define _TASK_SR_NODELAY 1
|
|
#define _TASK_SR_DELAY 2
|
|
|
|
class Scheduler;
|
|
|
|
class StatusRequest {
|
|
friend class Scheduler;
|
|
public:
|
|
INLINE StatusRequest();
|
|
INLINE void setWaiting(unsigned int aCount = 1);
|
|
INLINE bool signal(int aStatus = 0);
|
|
INLINE void signalComplete(int aStatus = 0);
|
|
INLINE bool pending();
|
|
INLINE bool completed();
|
|
INLINE int getStatus();
|
|
INLINE int getCount();
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
INLINE void setTimeout(unsigned long aTimeout) { iTimeout = aTimeout; };
|
|
INLINE unsigned long getTimeout() { return iTimeout; };
|
|
INLINE void resetTimeout();
|
|
INLINE long untilTimeout();
|
|
#endif
|
|
|
|
_TASK_SCOPE:
|
|
unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
|
|
int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
unsigned long iTimeout; // Task overall timeout
|
|
unsigned long iStarttime; // millis at task start time
|
|
#endif // _TASK_TIMEOUT
|
|
};
|
|
#endif // _TASK_STATUS_REQUEST
|
|
|
|
#ifdef _TASK_STD_FUNCTION
|
|
#include <functional>
|
|
typedef std::function<void()> TaskCallback;
|
|
typedef std::function<void()> TaskOnDisable;
|
|
typedef std::function<bool()> TaskOnEnable;
|
|
#else
|
|
typedef void (*TaskCallback)();
|
|
typedef void (*TaskOnDisable)();
|
|
typedef bool (*TaskOnEnable)();
|
|
#endif // _TASK_STD_FUNCTION
|
|
|
|
|
|
#ifdef _TASK_SLEEP_ON_IDLE_RUN
|
|
typedef void (*SleepCallback)( unsigned long aDuration );
|
|
|
|
extern Scheduler* iSleepScheduler;
|
|
extern SleepCallback iSleepMethod;
|
|
#endif // _TASK_SLEEP_ON_IDLE_RUN
|
|
|
|
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()
|
|
#ifdef _TASK_STATUS_REQUEST
|
|
uint8_t waiting : 2; // indication if task is waiting on the status request
|
|
#endif
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
bool timeout : 1; // indication if task timed out
|
|
#endif
|
|
} __task_status;
|
|
|
|
|
|
class Task {
|
|
friend class Scheduler;
|
|
public:
|
|
|
|
#ifdef _TASK_OO_CALLBACKS
|
|
INLINE Task(unsigned long aInterval=0, long aIterations=0, Scheduler* aScheduler=NULL, bool aEnable=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 // _TASK_OO_CALLBACKS
|
|
|
|
|
|
#ifdef _TASK_STATUS_REQUEST
|
|
#ifdef _TASK_OO_CALLBACKS
|
|
// INLINE Task(Scheduler* aScheduler=NULL);
|
|
INLINE Task(Scheduler* aScheduler);
|
|
#else
|
|
// INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
|
|
INLINE Task(TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
|
|
#endif // _TASK_OO_CALLBACKS
|
|
#endif // _TASK_STATUS_REQUEST
|
|
|
|
virtual INLINE ~Task();
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
INLINE void setTimeout(unsigned long aTimeout, bool aReset=false);
|
|
INLINE void resetTimeout();
|
|
INLINE unsigned long getTimeout();
|
|
INLINE long untilTimeout();
|
|
INLINE bool timedOut();
|
|
#endif
|
|
|
|
INLINE bool enable();
|
|
INLINE bool enableIfNot();
|
|
INLINE bool enableDelayed(unsigned long aDelay=0);
|
|
INLINE bool restart();
|
|
INLINE bool restartDelayed(unsigned long aDelay=0);
|
|
|
|
INLINE void delay(unsigned long aDelay=0);
|
|
INLINE void adjust(long aInterval);
|
|
INLINE void forceNextIteration();
|
|
INLINE bool disable();
|
|
INLINE void abort();
|
|
INLINE void cancel();
|
|
INLINE bool isEnabled();
|
|
INLINE bool canceled();
|
|
|
|
#ifdef _TASK_SCHEDULING_OPTIONS
|
|
INLINE unsigned int getSchedulingOption() { return iOption; }
|
|
INLINE void setSchedulingOption(unsigned int aOption) { iOption = aOption; }
|
|
#endif //_TASK_SCHEDULING_OPTIONS
|
|
|
|
#ifdef _TASK_OO_CALLBACKS
|
|
INLINE void set(unsigned long aInterval, long aIterations);
|
|
#else
|
|
INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL);
|
|
#endif // _TASK_OO_CALLBACKS
|
|
INLINE void setInterval(unsigned long aInterval);
|
|
INLINE unsigned long getInterval();
|
|
INLINE void setIterations(long aIterations);
|
|
INLINE long getIterations();
|
|
INLINE unsigned long getRunCounter() ;
|
|
|
|
#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
|
|
virtual INLINE bool OnEnable(); // return true if task should be enabled, false if it should remain disabled
|
|
virtual INLINE void OnDisable();
|
|
#else
|
|
INLINE void setCallback(TaskCallback aCallback) ;
|
|
INLINE void setOnEnable(TaskOnEnable aCallback) ;
|
|
INLINE void setOnDisable(TaskOnDisable aCallback) ;
|
|
INLINE void yield(TaskCallback aCallback);
|
|
INLINE void yieldOnce(TaskCallback aCallback);
|
|
#endif // _TASK_OO_CALLBACKS
|
|
|
|
INLINE bool isFirstIteration() ;
|
|
INLINE bool isLastIteration() ;
|
|
|
|
#ifdef _TASK_TIMECRITICAL
|
|
INLINE long getOverrun() ;
|
|
INLINE long getStartDelay() ;
|
|
#endif // _TASK_TIMECRITICAL
|
|
|
|
#ifdef _TASK_STATUS_REQUEST
|
|
INLINE bool waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
|
|
INLINE bool waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
|
|
INLINE StatusRequest* getStatusRequest() ;
|
|
INLINE StatusRequest* getInternalStatusRequest() ;
|
|
#endif // _TASK_STATUS_REQUEST
|
|
|
|
#ifdef _TASK_WDT_IDS
|
|
INLINE void setId(unsigned int aID) ;
|
|
INLINE unsigned int getId() ;
|
|
INLINE void setControlPoint(unsigned int aPoint) ;
|
|
INLINE unsigned int getControlPoint() ;
|
|
#endif // _TASK_WDT_IDS
|
|
|
|
#ifdef _TASK_LTS_POINTER
|
|
INLINE void setLtsPointer(void *aPtr) ;
|
|
INLINE void* getLtsPointer() ;
|
|
#endif // _TASK_LTS_POINTER
|
|
|
|
#ifdef _TASK_EXPOSE_CHAIN
|
|
INLINE Task* getPreviousTask() { return iPrev; }; // pointer to the previous task in the chain, NULL if first or not set
|
|
INLINE Task* getNextTask() { return iNext; }; // pointer to the next task in the chain, NULL if last or not set
|
|
#endif // _TASK_EXPOSE_CHAIN
|
|
|
|
|
|
_TASK_SCOPE:
|
|
INLINE void reset();
|
|
|
|
volatile __task_status iStatus;
|
|
volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate
|
|
volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval)
|
|
volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up"
|
|
|
|
#ifdef _TASK_SCHEDULING_OPTIONS
|
|
unsigned int iOption; // scheduling option
|
|
#endif // _TASK_SCHEDULING_OPTIONS
|
|
|
|
#ifdef _TASK_TIMECRITICAL
|
|
volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
|
|
volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis
|
|
#endif // _TASK_TIMECRITICAL
|
|
|
|
volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations
|
|
long iSetIterations; // number of iterations originally requested (for restarts)
|
|
unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable.
|
|
|
|
#ifndef _TASK_OO_CALLBACKS
|
|
TaskCallback iCallback; // pointer to the void callback method
|
|
TaskOnEnable iOnEnable; // pointer to the bool OnEnable callback method
|
|
TaskOnDisable iOnDisable; // pointer to the void OnDisable method
|
|
#endif // _TASK_OO_CALLBACKS
|
|
|
|
Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain
|
|
Scheduler *iScheduler; // pointer to the current scheduler
|
|
|
|
#ifdef _TASK_STATUS_REQUEST
|
|
StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on
|
|
StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion
|
|
#endif // _TASK_STATUS_REQUEST
|
|
|
|
#ifdef _TASK_WDT_IDS
|
|
unsigned int iTaskID; // task ID (for debugging and watchdog identification)
|
|
unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
|
|
#endif // _TASK_WDT_IDS
|
|
|
|
#ifdef _TASK_LTS_POINTER
|
|
void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
|
|
#endif // _TASK_LTS_POINTER
|
|
|
|
#ifdef _TASK_TIMEOUT
|
|
unsigned long iTimeout; // Task overall timeout
|
|
unsigned long iStarttime; // millis at task start time
|
|
#endif // _TASK_TIMEOUT
|
|
|
|
|
|
#ifdef _TASK_THREAD_SAFE
|
|
volatile uint8_t iMutex; // a mutex to pause scheduling during chages to the task
|
|
#endif
|
|
};
|
|
|
|
class Scheduler {
|
|
friend class Task;
|
|
public:
|
|
INLINE Scheduler();
|
|
// ~Scheduler();
|
|
INLINE void init();
|
|
INLINE void addTask(Task& aTask);
|
|
INLINE void deleteTask(Task& aTask);
|
|
INLINE void pause() { iPaused = true; };
|
|
INLINE void resume() { iPaused = false; };
|
|
INLINE void enable() { iEnabled = true; };
|
|
INLINE void disable() { iEnabled = false; };
|
|
#ifdef _TASK_PRIORITY
|
|
INLINE void disableAll(bool aRecursive = true);
|
|
INLINE void enableAll(bool aRecursive = true);
|
|
INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW.
|
|
#else
|
|
INLINE void disableAll();
|
|
INLINE void enableAll();
|
|
INLINE void startNow(); // reset ALL active tasks to immediate execution NOW.
|
|
#endif
|
|
INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run)
|
|
INLINE Task& currentTask() ; // DEPRICATED
|
|
INLINE Task* getCurrentTask() ; // Returns pointer to the currently active task
|
|
INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task
|
|
|
|
#ifdef _TASK_SLEEP_ON_IDLE_RUN
|
|
INLINE void allowSleep(bool aState = true);
|
|
INLINE void setSleepMethod( SleepCallback aCallback );
|
|
#endif // _TASK_SLEEP_ON_IDLE_RUN
|
|
|
|
#ifdef _TASK_LTS_POINTER
|
|
INLINE void* currentLts();
|
|
#endif // _TASK_LTS_POINTER
|
|
|
|
#ifdef _TASK_TIMECRITICAL
|
|
INLINE bool isOverrun();
|
|
INLINE void cpuLoadReset();
|
|
INLINE unsigned long getCpuLoadCycle(){ return iCPUCycle; };
|
|
INLINE unsigned long getCpuLoadIdle() { return iCPUIdle; };
|
|
INLINE unsigned long getCpuLoadTotal();
|
|
#endif // _TASK_TIMECRITICAL
|
|
|
|
#ifdef _TASK_PRIORITY
|
|
INLINE void setHighPriorityScheduler(Scheduler* aScheduler);
|
|
INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
|
|
#endif // _TASK_PRIORITY
|
|
|
|
#ifdef _TASK_EXPOSE_CHAIN
|
|
INLINE Task* getFirstTask() { return iFirst; }; // pointer to the previous task in the chain, NULL if first or not set
|
|
INLINE Task* getLastTask() { return iLast; }; // pointer to the next task in the chain, NULL if last or not set
|
|
#endif // _TASK_EXPOSE_CHAIN
|
|
|
|
_TASK_SCOPE:
|
|
Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain
|
|
|
|
volatile bool iPaused, iEnabled;
|
|
#ifdef _TASK_SLEEP_ON_IDLE_RUN
|
|
bool iAllowSleep; // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
|
|
#endif // _TASK_SLEEP_ON_IDLE_RUN
|
|
|
|
#ifdef _TASK_PRIORITY
|
|
Scheduler* iHighPriority; // Pointer to a higher priority scheduler
|
|
#endif // _TASK_PRIORITY
|
|
|
|
#ifdef _TASK_TIMECRITICAL
|
|
unsigned long iCPUStart;
|
|
unsigned long iCPUCycle;
|
|
unsigned long iCPUIdle;
|
|
#endif // _TASK_TIMECRITICAL
|
|
};
|
|
|
|
|
|
#endif /* _TASKSCHEDULERDECLARATIONS_H_ */
|