AceRoutine  1.3.1
A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.
Classes | Macros | Typedefs
Coroutine.h File Reference
#include <stdint.h>
#include <Print.h>
#include <AceCommon.h>
#include "ClockInterface.h"
Include dependency graph for Coroutine.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

class  ace_routine::CoroutineSchedulerTemplate< T_COROUTINE >
 Class that manages instances of the Coroutine class, and executes them in a round-robin fashion. More...
 
class  ace_routine::CoroutineTemplate< T_CLOCK >
 Base class of all coroutines. More...
 

Macros

#define ACE_ROUTINE_DEPRECATED
 Macro that indicates a deprecation.
 
#define COROUTINE(...)   GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__)
 Create a Coroutine instance named 'name'. More...
 
#define GET_COROUTINE(_1, _2, NAME, ...)   NAME
 Internal helper macro to allow overloading of the COROUTINE() macro.
 
#define COROUTINE1(name)
 Implement the 1-argument COROUTINE() macro. More...
 
#define COROUTINE2(className, name)
 Implement the 2-argument COROUTINE() macro. More...
 
#define EXTERN_COROUTINE(...)
 Create an extern reference to a coroutine that is defined in another .cpp file. More...
 
#define GET_EXTERN_COROUTINE(_1, _2, NAME, ...)   NAME
 Internal helper macro to allow overloading of the EXTERN_COROUTINE() macro.
 
#define EXTERN_COROUTINE1(name)
 Implement the 1-argument EXTERN_COROUTINE() macro. More...
 
#define EXTERN_COROUTINE2(className, name)
 Implement the 2-argument EXTERN_COROUTINE() macro. More...
 
#define COROUTINE_BEGIN()
 Mark the beginning of a coroutine. More...
 
#define COROUTINE_LOOP()
 Mark the beginning of a coroutine loop. More...
 
#define COROUTINE_YIELD_INTERNAL()
 Implement the common logic for COROUTINE_YIELD(), COROUTINE_AWAIT(), COROUTINE_DELAY(). More...
 
#define COROUTINE_YIELD()
 Yield execution to another coroutine. More...
 
#define COROUTINE_AWAIT(condition)
 Yield until condition is true, then execution continues. More...
 
#define COROUTINE_DELAY(delayMillis)
 Yield for delayMillis. More...
 
#define COROUTINE_DELAY_MICROS(delayMicros)
 Yield for delayMicros. More...
 
#define COROUTINE_DELAY_SECONDS(delaySeconds)
 Yield for delaySeconds. More...
 
#define COROUTINE_END()
 Mark the end of a coroutine. More...
 

Typedefs

using ace_routine::Coroutine = CoroutineTemplate< ClockInterface >
 A concrete template instance of CoroutineTemplate that uses ClockInterface which uses the built-in millis() or micros() function. More...
 

Detailed Description

All coroutines are instances of the Coroutine base class. The COROUTINE() macro creates these instances, and registers them to automatically run when CoroutineScheduler::loop() is called.

Various macros use macro overloading to implement a 1-argument and a 2-argument version. See https://stackoverflow.com/questions/11761703 to description of how that works.

The computed goto is a GCC extension: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html The noinline and noclone attributes make sure that label pointers are always the same. I'm not 100% sure they are needed here, but they don't seem to hurt.

Definition in file Coroutine.h.

Macro Definition Documentation

◆ COROUTINE

#define COROUTINE (   ...)    GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__)

Create a Coroutine instance named 'name'.

Two forms are supported

The 1-argument form uses the Coroutine class as the base class of the coroutine. The 2-argument form uses the user-provided className which must be a subclass of Coroutine.

The code in {} following this macro becomes the body of the Coroutine::runCoroutine() method.

Definition at line 78 of file Coroutine.h.

◆ COROUTINE1

#define COROUTINE1 (   name)
Value:
struct Coroutine_##name : ace_routine::Coroutine { \
Coroutine_##name(); \
int runCoroutine() override; \
} name; \
Coroutine_##name :: Coroutine_##name() { \
} \
int Coroutine_##name :: runCoroutine()

Implement the 1-argument COROUTINE() macro.

Definition at line 85 of file Coroutine.h.

◆ COROUTINE2

#define COROUTINE2 (   className,
  name 
)
Value:
struct className##_##name : className { \
className##_##name(); \
int runCoroutine() override; \
} name; \
className##_##name :: className##_##name() { \
} \
int className##_##name :: runCoroutine()

Implement the 2-argument COROUTINE() macro.

Definition at line 95 of file Coroutine.h.

◆ COROUTINE_AWAIT

#define COROUTINE_AWAIT (   condition)
Value:
do { \
this->setYielding(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!(condition)); \
this->setRunning(); \
} while (false)

Yield until condition is true, then execution continues.

This is functionally equivalent to:

while (!condition) COROUTINE_YIELD();

but potentially slightly more efficient.

Definition at line 182 of file Coroutine.h.

◆ COROUTINE_BEGIN

#define COROUTINE_BEGIN ( )
Value:
void* p = this->getJump(); \
if (p != nullptr) { \
goto *p; \
}

Mark the beginning of a coroutine.

Definition at line 138 of file Coroutine.h.

◆ COROUTINE_DELAY

#define COROUTINE_DELAY (   delayMillis)
Value:
do { \
this->setDelayMillis(delayMillis); \
this->setDelaying(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!this->isDelayExpired()); \
this->setRunning(); \
} while (false)

Yield for delayMillis.

A delayMillis of 0 is functionally equivalent to COROUTINE_YIELD(). To save memory, the delayMillis is stored as a uint16_t but the actual maximum is limited to 32767 millliseconds. See setDelayMillis() for the reason for this limitation.

If you need to wait for longer than that, use a for-loop to call COROUTINE_DELAY() as many times as necessary.

This could have been implemented using COROUTINE_AWAIT() but this macro matches the global delay(millis) function already provided by the Arduino API. Also having a separate kStatusDelaying state allows the CoroutineScheduler to be slightly more efficient by avoiding the call to Coroutine::runCoroutine() if the delay has not expired.

Definition at line 206 of file Coroutine.h.

◆ COROUTINE_DELAY_MICROS

#define COROUTINE_DELAY_MICROS (   delayMicros)
Value:
do { \
this->setDelayMicros(delayMicros); \
this->setDelaying(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!this->isDelayMicrosExpired()); \
this->setRunning(); \
} while (false)

Yield for delayMicros.

Similiar to COROUTINE_DELAY(delayMillis).

Definition at line 217 of file Coroutine.h.

◆ COROUTINE_DELAY_SECONDS

#define COROUTINE_DELAY_SECONDS (   delaySeconds)
Value:
do { \
this->setDelaySeconds(delaySeconds); \
this->setDelaying(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!this->isDelaySecondsExpired()); \
this->setRunning(); \
} while (false)

Yield for delaySeconds.

Similar to COROUTINE_DELAY(delayMillis).

The accuracy of the delay interval in units of seconds is not perfectly accurate. The current implementation uses the builtin millis() to infer the "seconds". The millis() function returns a value that overflows after 4,294,967.296 seconds. Therefore, the last inferred second just before overflowing contains only 0.296 seconds instead of a full second. A delay which straddles this overflow will return 0.704 seconds earlier than it should.

On microcontrollers without hardware integer division instruction, (i.e. AVR, SAMD21, ESP8266), the division by 1000 is relatively slow and consumes significant amount of flash memory (100-150 bytes on AVR).

Definition at line 242 of file Coroutine.h.

◆ COROUTINE_END

#define COROUTINE_END ( )
Value:
do { \
__label__ jumpLabel; \
this->setEnding(); \
this->setJump(&& jumpLabel); \
jumpLabel: ; \
return 0; \
} while (false)

Mark the end of a coroutine.

Subsequent calls to Coroutine::runCoroutine() will do nothing.

Definition at line 256 of file Coroutine.h.

◆ COROUTINE_LOOP

#define COROUTINE_LOOP ( )
Value:
while (true) \

Mark the beginning of a coroutine loop.

Can be used instead of COROUTINE_BEGIN() at the beginning of a Coroutine.

Definition at line 148 of file Coroutine.h.

◆ COROUTINE_YIELD

#define COROUTINE_YIELD ( )
Value:
do { \
this->setYielding(); \
COROUTINE_YIELD_INTERNAL(); \
this->setRunning(); \
} while (false)

Yield execution to another coroutine.

Definition at line 165 of file Coroutine.h.

◆ COROUTINE_YIELD_INTERNAL

#define COROUTINE_YIELD_INTERNAL ( )
Value:
do { \
__label__ jumpLabel; \
this->setJump(&& jumpLabel); \
return 0; \
jumpLabel: ; \
} while (false)

Implement the common logic for COROUTINE_YIELD(), COROUTINE_AWAIT(), COROUTINE_DELAY().

Definition at line 156 of file Coroutine.h.

◆ EXTERN_COROUTINE

#define EXTERN_COROUTINE (   ...)
Value:

Create an extern reference to a coroutine that is defined in another .cpp file.

The extern reference is needed before it can be used. Two forms are supported:

Definition at line 112 of file Coroutine.h.

◆ EXTERN_COROUTINE1

#define EXTERN_COROUTINE1 (   name)
Value:
struct Coroutine_##name : ace_routine::Coroutine { \
Coroutine_##name(); \
int runCoroutine() override; \
}; \
extern Coroutine_##name name

Implement the 1-argument EXTERN_COROUTINE() macro.

Definition at line 122 of file Coroutine.h.

◆ EXTERN_COROUTINE2

#define EXTERN_COROUTINE2 (   className,
  name 
)
Value:
struct className##_##name : className { \
className##_##name(); \
int runCoroutine() override; \
}; \
extern className##_##name name

Implement the 2-argument EXTERN_COROUTINE() macro.

Definition at line 130 of file Coroutine.h.

Typedef Documentation

◆ Coroutine

using ace_routine::Coroutine = typedef CoroutineTemplate<ClockInterface>

A concrete template instance of CoroutineTemplate that uses ClockInterface which uses the built-in millis() or micros() function.

This becomes the base class of all user-defined coroutines created using the COROUTINE() macro or through manual subclassing of this class.

Definition at line 680 of file Coroutine.h.

COROUTINE_BEGIN
#define COROUTINE_BEGIN()
Mark the beginning of a coroutine.
Definition: Coroutine.h:138
EXTERN_COROUTINE2
#define EXTERN_COROUTINE2(className, name)
Implement the 2-argument EXTERN_COROUTINE() macro.
Definition: Coroutine.h:130
COROUTINE_YIELD
#define COROUTINE_YIELD()
Yield execution to another coroutine.
Definition: Coroutine.h:165
ace_routine::CoroutineTemplate
Base class of all coroutines.
Definition: Coroutine.h:278
ace_routine::CoroutineTemplate::runCoroutine
virtual int runCoroutine()=0
The body of the coroutine.
EXTERN_COROUTINE1
#define EXTERN_COROUTINE1(name)
Implement the 1-argument EXTERN_COROUTINE() macro.
Definition: Coroutine.h:122
GET_EXTERN_COROUTINE
#define GET_EXTERN_COROUTINE(_1, _2, NAME,...)
Internal helper macro to allow overloading of the EXTERN_COROUTINE() macro.
Definition: Coroutine.h:119