AceRoutine  1.0
A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.
Classes | Macros | Functions
Coroutine.h File Reference

All coroutines are instances of the Coroutine base class. More...

#include <stdint.h>
#include <Print.h>
#include "FCString.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::Coroutine
 Base class of all coroutines. More...
 

Macros

#define COROUTINE(...)   GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__)
 Create a Coroutine instance named 'name'. More...
 
#define GET_COROUTINE(_1, _2, NAME, ...)   NAME
 
#define COROUTINE1(name)
 
#define COROUTINE2(className, name)
 
#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
 
#define EXTERN_COROUTINE1(name)
 
#define EXTERN_COROUTINE2(className, name)
 
#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()
 
#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...
 

Functions

unsigned long ace_routine::internal::udiv1000 (unsigned long n)
 Approximate division by 1000. 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 65 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() { \
setupCoroutine(F(#name)); \
} \
int Coroutine_##name :: runCoroutine()
virtual int runCoroutine()=0
The body of the coroutine.
Base class of all coroutines.
Definition: Coroutine.h:255

Definition at line 70 of file Coroutine.h.

◆ COROUTINE2

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

Definition at line 80 of file Coroutine.h.

◆ COROUTINE_AWAIT

#define COROUTINE_AWAIT (   condition)
Value:
do { \
setYielding(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!(condition)); \
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 159 of file Coroutine.h.

◆ COROUTINE_BEGIN

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

Mark the beginning of a coroutine.

Definition at line 119 of file Coroutine.h.

◆ COROUTINE_DELAY

#define COROUTINE_DELAY (   delayMillis)
Value:
do { \
setDelayMillis(delayMillis); \
setDelaying(); \
do { \
COROUTINE_YIELD_INTERNAL(); \
} while (!isDelayExpired()); \
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 or use COROUTINE_DELAY_SECONDS().

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 184 of file Coroutine.h.

◆ COROUTINE_DELAY_MICROS

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

Yield for delayMicros.

Similiar to COROUTINE_DELAY(delayMillis).

Definition at line 195 of file Coroutine.h.

◆ COROUTINE_DELAY_SECONDS

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

Yield for delaySeconds.

Similar to COROUTINE_DELAY(delayMillis).

Definition at line 206 of file Coroutine.h.

◆ COROUTINE_END

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

Mark the end of a coroutine.

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

Definition at line 220 of file Coroutine.h.

◆ COROUTINE_LOOP

#define COROUTINE_LOOP ( )
Value:
while (true) \
#define COROUTINE_BEGIN()
Mark the beginning of a coroutine.
Definition: Coroutine.h:119

Mark the beginning of a coroutine loop.

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

Definition at line 129 of file Coroutine.h.

◆ COROUTINE_YIELD

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

Yield execution to another coroutine.

Definition at line 142 of file Coroutine.h.

◆ COROUTINE_YIELD_INTERNAL

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

Definition at line 133 of file Coroutine.h.

◆ EXTERN_COROUTINE

#define EXTERN_COROUTINE (   ...)
Value:
GET_EXTERN_COROUTINE(\
__VA_ARGS__, EXTERN_COROUTINE2, EXTERN_COROUTINE1)(__VA_ARGS__)

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 98 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
virtual int runCoroutine()=0
The body of the coroutine.
Base class of all coroutines.
Definition: Coroutine.h:255

Definition at line 104 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

Definition at line 111 of file Coroutine.h.

Function Documentation

◆ udiv1000()

unsigned long ace_routine::internal::udiv1000 ( unsigned long  n)
inline

Approximate division by 1000.

More accurate algorithms exist (see for example http://www.hackersdelight.org/divcMore.pdf) but I'm pretty sure that this good enough since we don't guarantee accurate timing of the COROUTINE_DELAY*() methods.

Definition at line 238 of file Coroutine.h.