AceRoutine  1.5.0
A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.
Public Member Functions | Static Public Member Functions | Static Public Attributes | Protected Types | Protected Member Functions | Static Protected Member Functions | Protected Attributes | Static Protected Attributes | Friends | List of all members
ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY > Class Template Referenceabstract

Base class of all coroutines. More...

#include <Coroutine.h>

Collaboration diagram for ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >:
Collaboration graph
[legend]

Public Member Functions

virtual int runCoroutine ()=0
 The body of the coroutine. More...
 
virtual void setupCoroutine ()
 Perform coroutine initialization. More...
 
int runCoroutineWithProfiler ()
 This is a variant of runCoroutine() which measures the execution time of runCoroutine() and updates the attached profiler if it exists. More...
 
uint8_t getNameType () const
 Return the type of the name string, either kNameTypeCString or kNameTypeFString.
 
void setName (const char *name)
 Set the name of the coroutine to the given c-string.
 
void setName (const __FlashStringHelper *name)
 Set the name of the coroutine to the given f-string.
 
const char * getCName () const
 Get name of the coroutine assuming it's a c-string. More...
 
const __FlashStringHelper * getFName () const
 Get name of the coroutine assuming it's an f-string. More...
 
void printNameTo (Print &printer, uint8_t maxLen=0) const
 Print name to the given Printer. More...
 
void suspend ()
 Suspend the coroutine at the next scheduler iteration. More...
 
void resume ()
 Add a Suspended coroutine into the head of the scheduler linked list, and change the state to Yielding. More...
 
void reset ()
 Reset the coroutine to its initial state. More...
 
bool isDelayExpired () const
 Check if delay millis time is over.
 
bool isDelayMicrosExpired () const
 Check if delay micros time is over.
 
bool isDelaySecondsExpired () const
 Check if delay seconds time is over.
 
bool isSuspended () const
 The coroutine was suspended with a call to suspend().
 
bool isYielding () const
 The coroutine returned using COROUTINE_YIELD().
 
bool isDelaying () const
 The coroutine returned using COROUTINE_DELAY().
 
bool isRunning () const
 The coroutine is currently running. More...
 
bool isEnding () const
 The coroutine returned using COROUTINE_END(). More...
 
bool isTerminated () const
 The coroutine was terminated by the scheduler with a call to setTerminated(). More...
 
bool isDone () const
 The coroutine is either Ending or Terminated. More...
 
void setupCoroutine (const char *) ACE_ROUTINE_DEPRECATED
 Deprecated method that does nothing. More...
 
void setupCoroutine (const __FlashStringHelper *) ACE_ROUTINE_DEPRECATED
 Deprecated method that does nothing. More...
 
void setProfiler (CoroutineProfiler *profiler)
 Set the profiler.
 
CoroutineProfilergetProfiler () const
 Get the profiler. More...
 
CoroutineTemplate ** getNext ()
 Return the next pointer as a pointer to the pointer, similar to getRoot(). More...
 

Static Public Member Functions

static CoroutineTemplate ** getRoot ()
 Get the pointer to the root pointer. More...
 

Static Public Attributes

static const uint8_t kNameTypeCString = 0
 Coroutine name is a const char* c-string.
 
static const uint8_t kNameTypeFString = 1
 Coroutine name is a const __FlashStringHelper* f-string.
 

Protected Types

typedef uint8_t Status
 The execution status of the coroutine, corresponding to the COROUTINE_YIELD(), COROUTINE_DELAY(), COROUTINE_AWAIT() and COROUTINE_END() macros. More...
 

Protected Member Functions

 CoroutineTemplate ()
 Constructor. More...
 
 ~CoroutineTemplate ()=default
 Destructor. More...
 
Status getStatus () const
 Return the status of the coroutine. More...
 
void statusPrintTo (Print &printer)
 Print the human-readable string of the Status.
 
void setJump (void *jumpPoint)
 Pointer to label where execute will start on the next call to runCoroutine().
 
void * getJump () const
 Pointer to label where execute will start on the next call to runCoroutine().
 
void setRunning ()
 Set the kStatusRunning state.
 
void setYielding ()
 Set the kStatusDelaying state.
 
void setDelaying ()
 Set the kStatusDelaying state.
 
void setEnding ()
 Set the kStatusEnding state.
 
void setTerminated ()
 Set status to indicate that the Coroutine has been removed from the Scheduler queue. More...
 
void setDelayMillis (T_DELAY delayMillis)
 Configure the delay timer for delayMillis. More...
 
void setDelayMicros (T_DELAY delayMicros)
 Configure the delay timer for delayMicros. More...
 
void setDelaySeconds (T_DELAY delaySeconds)
 Configure the delay timer for delaySeconds. More...
 

Static Protected Member Functions

static unsigned long coroutineMillis ()
 Returns the current millisecond clock. More...
 
static unsigned long coroutineMicros ()
 Returns the current microseconds clock. More...
 
static unsigned long coroutineSeconds ()
 Returns the current clock in unit of seconds, truncated to the lower 16-bits. More...
 

Protected Attributes

CoroutineTemplatemNext = nullptr
 Pointer to the next coroutine in a singly-linked list.
 
void * mJumpPoint = nullptr
 Address of the label used by the computed-goto.
 
const void * mName = nullptr
 Name of the coroutine, either (const char*) or (const __FlashStringHelper*). More...
 
uint8_t mNameType = kNameTypeCString
 String type of the coroutine mName.
 
Status mStatus = kStatusYielding
 Run-state of the coroutine.
 
T_DELAY mDelayStart
 Start time provided by COROUTINE_DELAY(), COROUTINE_DELAY_MICROS(), or COROUTINE_DELAY_SECONDS(). More...
 
T_DELAY mDelayDuration
 Delay time specified by COROUTINE_DELAY(), COROUTINE_DELAY_MICROS() or, COROUTINE_DELAY_SECONDS(). More...
 
CoroutineProfilermProfiler = nullptr
 Pointer to a profiler instance, either static or on the heap.
 

Static Protected Attributes

static const Status kStatusSuspended = 0
 Coroutine has been suspended using suspend() and the scheduler should remove it from the queue upon the next iteration. More...
 
static const Status kStatusYielding = 1
 Coroutine returned using the COROUTINE_YIELD() statement.
 
static const Status kStatusDelaying = 2
 Coroutine returned using the COROUTINE_DELAY() statement.
 
static const Status kStatusRunning = 3
 Coroutine is currenly running. More...
 
static const Status kStatusEnding = 4
 Coroutine executed the COROUTINE_END() statement.
 
static const Status kStatusTerminated = 5
 Coroutine has ended and no longer in the scheduler queue.
 

Friends

class CoroutineSchedulerTemplate< CoroutineTemplate< T_CLOCK, T_DELAY > >
 
class ::AceRoutineTest_statusStrings
 
class ::SuspendTest_suspendAndResume
 

Detailed Description

template<typename T_CLOCK, typename T_DELAY>
class ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >

Base class of all coroutines.

The actual coroutine code is an implementation of the virtual runCoroutine() method.

Template Parameters
T_CLOCKclass that provides micros(), millis() and seconds() functions, usually ClockInterface but can be something else for testing purposes.
T_DELAYtype used to store the mDelayStart and mDelayDuration, usually uint16_t. It may be possible to create a variant set of Coroutine32 and CoroutineScheduler32 classes which use a uint32_t instead of a uint16_t. This would probably allow the COROUTINE_DELAY(), COROUTINE_DELAY_MICROS() and COROUTINE_DELAY_SECONDS() macros to accept 32-bit integers instead of 16-bits. I have not tested this possibility at all.

Definition at line 292 of file Coroutine.h.

Member Typedef Documentation

◆ Status

template<typename T_CLOCK , typename T_DELAY >
typedef uint8_t ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::Status
protected

The execution status of the coroutine, corresponding to the COROUTINE_YIELD(), COROUTINE_DELAY(), COROUTINE_AWAIT() and COROUTINE_END() macros.

The finite state diagram looks like this:

*          Suspended
*          ^       ^
*         /         \
*        /           \
*       v             \
* Yielding          Delaying
*      ^               ^
*       \             /
*        \           /
*         \         /
*          v       v
*           Running
*              |
*              |
*              v
*           Ending
*              |
*              |
*              v
*         Terminated
* 

Definition at line 603 of file Coroutine.h.

Constructor & Destructor Documentation

◆ CoroutineTemplate()

template<typename T_CLOCK , typename T_DELAY >
ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::CoroutineTemplate ( )
inlineprotected

Constructor.

Automatically insert self into singly-linked list.

Definition at line 629 of file Coroutine.h.

◆ ~CoroutineTemplate()

template<typename T_CLOCK , typename T_DELAY >
ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::~CoroutineTemplate ( )
protecteddefault

Destructor.

Non-virtual.

A virtual destructor increases the flash memory consumption on 8-bit AVR processors by 500-600 bytes because it pulls in the free() and malloc() functions. On the 32-bit SAMD21, the flash memory increases by by about 350 bytes. On other 32-bit processors (STM32, ESP8266, ESP32, Teensy 3.2), the flash memory increase is modest, about 50-150 bytes.

Since a Coroutine is expected to be created statically, instead of the heap, a non-virtual destructor is good enough.

Member Function Documentation

◆ coroutineMicros()

template<typename T_CLOCK , typename T_DELAY >
static unsigned long ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::coroutineMicros ( )
inlinestaticprotected

Returns the current microseconds clock.

By default it returns the global micros() function from Arduino but can be overridden by providing a different T_CLOCK template parameter.

Definition at line 751 of file Coroutine.h.

◆ coroutineMillis()

template<typename T_CLOCK , typename T_DELAY >
static unsigned long ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::coroutineMillis ( )
inlinestaticprotected

Returns the current millisecond clock.

By default it returns the global millis() function from Arduino but can be overridden by providing a different T_CLOCK template parameter.

Definition at line 742 of file Coroutine.h.

◆ coroutineSeconds()

template<typename T_CLOCK , typename T_DELAY >
static unsigned long ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::coroutineSeconds ( )
inlinestaticprotected

Returns the current clock in unit of seconds, truncated to the lower 16-bits.

This is an approximation of (millis / 1000). It does not need to be perfectly accurate because COROUTINE_DELAY_SECONDS() is not guaranteed to be precise.

Definition at line 761 of file Coroutine.h.

◆ getCName()

template<typename T_CLOCK , typename T_DELAY >
const char* ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getCName ( ) const
inline

Get name of the coroutine assuming it's a c-string.

Nullable.

Definition at line 375 of file Coroutine.h.

◆ getFName()

template<typename T_CLOCK , typename T_DELAY >
const __FlashStringHelper* ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getFName ( ) const
inline

Get name of the coroutine assuming it's an f-string.

Nullable.

Definition at line 378 of file Coroutine.h.

◆ getNext()

template<typename T_CLOCK , typename T_DELAY >
CoroutineTemplate** ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getNext ( )
inline

Return the next pointer as a pointer to the pointer, similar to getRoot().

This makes it much easier to manipulate a singly-linked list. Also makes setNext() method unnecessary. Should be used only by CoroutineScheduler.

Definition at line 570 of file Coroutine.h.

◆ getProfiler()

template<typename T_CLOCK , typename T_DELAY >
CoroutineProfiler* ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getProfiler ( ) const
inline

Get the profiler.

Nullable.

Definition at line 550 of file Coroutine.h.

◆ getRoot()

template<typename T_CLOCK , typename T_DELAY >
static CoroutineTemplate** ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getRoot ( )
inlinestatic

Get the pointer to the root pointer.

Implemented as a function static to fix the C++ static initialization problem, making it safe to use this in other static contexts.

Definition at line 557 of file Coroutine.h.

◆ getStatus()

template<typename T_CLOCK , typename T_DELAY >
Status ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::getStatus ( ) const
inlineprotected

Return the status of the coroutine.

Used by the CoroutineScheduler.

Definition at line 648 of file Coroutine.h.

◆ isDone()

template<typename T_CLOCK , typename T_DELAY >
bool ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::isDone ( ) const
inline

The coroutine is either Ending or Terminated.

This method is recommended over isEnding() or isTerminated() because it works when the coroutine is executed either manually or through the CoroutineScheduler.

Definition at line 525 of file Coroutine.h.

◆ isEnding()

template<typename T_CLOCK , typename T_DELAY >
bool ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::isEnding ( ) const
inline

The coroutine returned using COROUTINE_END().

In most cases, isDone() is recommended instead because it works when coroutines are executed manually or through the CoroutineScheduler.

Definition at line 510 of file Coroutine.h.

◆ isRunning()

template<typename T_CLOCK , typename T_DELAY >
bool ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::isRunning ( ) const
inline

The coroutine is currently running.

True only within the coroutine.

Definition at line 503 of file Coroutine.h.

◆ isTerminated()

template<typename T_CLOCK , typename T_DELAY >
bool ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::isTerminated ( ) const
inline

The coroutine was terminated by the scheduler with a call to setTerminated().

In most cases, isDone() should be used instead because it works when coroutines are executed manually or through the CoroutineScheudler.

Definition at line 518 of file Coroutine.h.

◆ printNameTo()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::printNameTo ( Print &  printer,
uint8_t  maxLen = 0 
) const
inline

Print name to the given Printer.

If the name is null, then print the hexadecimal representation of the pointer to the coroutine.

Parameters
printerdestination of output, usually Serial
maxLentruncate or pad to maxLen if given

Definition at line 389 of file Coroutine.h.

◆ reset()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::reset ( )
inline

Reset the coroutine to its initial state.

Only the Coroutine base-class state is reset to the original state. If the subclass runCoroutine() uses any static variables (for example, a loop counter), you must reset those variables manually as well, since this library does not have any knowledge about them.

It is expected that this method will be called from outside the runCoroutine() method. If it is called within the method, I'm not sure what will happen. I think the coroutine will abandon the current continuation point, and start executing from the beginning of the Coroutine upon the next iteration.

Definition at line 467 of file Coroutine.h.

◆ resume()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::resume ( )
inline

Add a Suspended coroutine into the head of the scheduler linked list, and change the state to Yielding.

If the coroutine is in any other state, this method does nothing. This method works only if the CoroutineScheduler::loop() is used.

Definition at line 443 of file Coroutine.h.

◆ runCoroutine()

template<typename T_CLOCK , typename T_DELAY >
virtual int ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::runCoroutine ( )
pure virtual

The body of the coroutine.

The COROUTINE macro creates a subclass of this class and puts the body of the coroutine into this method.

Returns
The return value is always ignored. This method is declared to return an int to prevent the user from accidentally returning from this method using an explicit 'return' statement instead of through one of the macros (e.g. COROUTINE_YIELD(), COROUTINE_DELAY(), COROUTINE_AWAIT() or COROUTINE_END()).

◆ runCoroutineWithProfiler()

template<typename T_CLOCK , typename T_DELAY >
int ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::runCoroutineWithProfiler ( )
inline

This is a variant of runCoroutine() which measures the execution time of runCoroutine() and updates the attached profiler if it exists.

On 8-bit processors, memory consumption can be reduced by calling the Coroutine::runCoroutine() method directly in the global loop() function, instead of using the CoroutineScheduler. In such an environment, the end-user can enable profiling by manually changing the calls to Coroutine::runCoroutine() to Coroutine::runCoroutineWithProfiler(), and recompiling the program.

Definition at line 344 of file Coroutine.h.

◆ setDelayMicros()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setDelayMicros ( T_DELAY  delayMicros)
inlineprotected

Configure the delay timer for delayMicros.

Similar to seDelayMillis(), the maximum delay is 32767 micros.

Definition at line 713 of file Coroutine.h.

◆ setDelayMillis()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setDelayMillis ( T_DELAY  delayMillis)
inlineprotected

Configure the delay timer for delayMillis.

The maximum duration is set to (UINT16_MAX / 2) (i.e. 32767 milliseconds) if given a larger value. This makes the longest allowable time between two successive calls to isDelayExpired() for a given coroutine to be 32767 (UINT16_MAX - UINT16_MAX / 2 - 1) milliseconds, which should be long enough for all practical use-cases. (The '- 1' comes from an edge case where isDelayExpired() evaluates to be true in the CoroutineScheduler::runCoroutine() but becomes to be false in the COROUTINE_DELAY() macro inside Coroutine::runCoroutine()) because the clock increments by 1 millisecond.)

Definition at line 699 of file Coroutine.h.

◆ setDelaySeconds()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setDelaySeconds ( T_DELAY  delaySeconds)
inlineprotected

Configure the delay timer for delaySeconds.

Similar to seDelayMillis(), the maximum delay is 32767 seconds.

Definition at line 727 of file Coroutine.h.

◆ setTerminated()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setTerminated ( )
inlineprotected

Set status to indicate that the Coroutine has been removed from the Scheduler queue.

Should be used only by the CoroutineScheduler.

Definition at line 684 of file Coroutine.h.

◆ setupCoroutine() [1/3]

template<typename T_CLOCK , typename T_DELAY >
virtual void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setupCoroutine ( )
inlinevirtual

Perform coroutine initialization.

This is intended to be called directly from the global setup() function, or through the CoroutineScheduler::setupCoroutines() method which should also be called from the global setup() function.

If your coroutines do not override this method, hence do not need to perform any setup, then you should not call CoroutineScheduler::setupCoroutines() to avoid consuming unnecessary flash memory. On AVR processors, each Coroutine::setupCoroutine() seems to consume at least 50-60 bytes of flash memory overhead per coroutine. On 32-bit processors, the overhead seems to be only about 30-40 bytes per coroutine.

Definition at line 331 of file Coroutine.h.

◆ setupCoroutine() [2/3]

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setupCoroutine ( const __FlashStringHelper *  )
inline

Deprecated method that does nothing.

Starting v1.3, the setup into the singly-linked list is automatically performed by the constructor and this method no longer needs to be called manually. This method is retained for backwards compatibility.

Definition at line 543 of file Coroutine.h.

◆ setupCoroutine() [3/3]

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::setupCoroutine ( const char *  )
inline

Deprecated method that does nothing.

Starting v1.3, the setup into the singly-linked list is automatically performed by the constructor and this method no longer needs to be called manually. This method is retained for backwards compatibility.

Definition at line 535 of file Coroutine.h.

◆ suspend()

template<typename T_CLOCK , typename T_DELAY >
void ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::suspend ( )
inline

Suspend the coroutine at the next scheduler iteration.

If the coroutine is already in the process of ending or is already terminated, then this method does nothing. A coroutine cannot use this method to suspend itself, it can only suspend some other coroutine. Currently, there is no ability for a coroutine to suspend itself. I think that would require the addition of a COROUTINE_SUSPEND() macro. Also, this method works only if the CoroutineScheduler::loop() is used because the suspend functionality is implemented by the CoroutineScheduler.

Definition at line 432 of file Coroutine.h.

Member Data Documentation

◆ kStatusRunning

template<typename T_CLOCK , typename T_DELAY >
const Status ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::kStatusRunning = 3
staticprotected

Coroutine is currenly running.

True only within the coroutine itself.

Definition at line 620 of file Coroutine.h.

◆ kStatusSuspended

template<typename T_CLOCK , typename T_DELAY >
const Status ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::kStatusSuspended = 0
staticprotected

Coroutine has been suspended using suspend() and the scheduler should remove it from the queue upon the next iteration.

We don't distinguish whether the coroutine is still in the queue or not with this status. We can add that later if we need to.

Definition at line 611 of file Coroutine.h.

◆ mDelayDuration

template<typename T_CLOCK , typename T_DELAY >
T_DELAY ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::mDelayDuration
protected

Delay time specified by COROUTINE_DELAY(), COROUTINE_DELAY_MICROS() or, COROUTINE_DELAY_SECONDS().

The unit of this number is context dependent, milliseconds, microseconds, or seconds.

Definition at line 813 of file Coroutine.h.

◆ mDelayStart

template<typename T_CLOCK , typename T_DELAY >
T_DELAY ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::mDelayStart
protected

Start time provided by COROUTINE_DELAY(), COROUTINE_DELAY_MICROS(), or COROUTINE_DELAY_SECONDS().

The unit of this number is context dependent, milliseconds, microseconds, or seconds.

Definition at line 806 of file Coroutine.h.

◆ mName

template<typename T_CLOCK , typename T_DELAY >
const void* ace_routine::CoroutineTemplate< T_CLOCK, T_DELAY >::mName = nullptr
protected

Name of the coroutine, either (const char*) or (const __FlashStringHelper*).

(Optional)

Definition at line 793 of file Coroutine.h.


The documentation for this class was generated from the following file: