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

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

#include <stdint.h>
#include <Print.h>
#include "Flash.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_END()
 Mark the end of a coroutine. 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::run() method.

Definition at line 64 of file Coroutine.h.

◆ COROUTINE1

#define COROUTINE1 (   name)
Value:
struct Coroutine_##name : ace_routine::Coroutine { \
Coroutine_##name(); \
virtual int run() override \
__attribute__((__noinline__,__noclone__)); \
} name; \
Coroutine_##name :: Coroutine_##name() { \
init(ACE_ROUTINE_F(#name)); \
} \
int Coroutine_##name :: run()
virtual int run()=0
The body of the coroutine.
Base class of all coroutines.
Definition: Coroutine.h:217

Definition at line 69 of file Coroutine.h.

◆ COROUTINE2

#define COROUTINE2 (   className,
  name 
)
Value:
struct className##_##name : className { \
className##_##name(); \
virtual int run() override \
__attribute__((__noinline__,__noclone__)); \
} name; \
className##_##name :: className##_##name() { \
init(ACE_ROUTINE_F(#name)); \
} \
int className##_##name :: run()

Definition at line 80 of file Coroutine.h.

◆ COROUTINE_AWAIT

#define COROUTINE_AWAIT (   condition)
Value:
do { \
while (!(condition)) { \
setAwaiting(); \
COROUTINE_YIELD_INTERNAL(); \
} \
setRunning(); \
} while (false)

Yield until condition is true, then execution continues.

This is functionally equivalent to:

while (!condition) COROUTINE_YIELD();

but the getStatus() during the waiting is set to kStatusAwaiting instead of kStatusYielding. The current scheduler treats the two states the same, but it's possible that a different scheduler may want to treat them differently.

Definition at line 164 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 122 of file Coroutine.h.

◆ COROUTINE_DELAY

#define COROUTINE_DELAY (   delayMillis)
Value:
do { \
setDelay(delayMillis); \
while (!isDelayExpired()) { \
setDelaying(); \
COROUTINE_YIELD_INTERNAL(); \
} \
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 setDelay() 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::run() if the delay has not expired.

Definition at line 188 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::run() will do nothing.

Definition at line 202 of file Coroutine.h.

◆ COROUTINE_LOOP

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

Mark the beginning of a coroutine loop.

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

Definition at line 132 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 145 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 136 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 99 of file Coroutine.h.

◆ EXTERN_COROUTINE1

#define EXTERN_COROUTINE1 (   name)
Value:
struct Coroutine_##name : ace_routine::Coroutine { \
Coroutine_##name(); \
virtual int run() override \
__attribute__((__noinline__,__noclone__)); \
}; \
extern Coroutine_##name name
virtual int run()=0
The body of the coroutine.
Base class of all coroutines.
Definition: Coroutine.h:217

Definition at line 105 of file Coroutine.h.

◆ EXTERN_COROUTINE2

#define EXTERN_COROUTINE2 (   className,
  name 
)
Value:
struct className##_##name : className { \
className##_##name(); \
virtual int run() override \
__attribute__((__noinline__,__noclone__)); \
}; \
extern className##_##name name

Definition at line 113 of file Coroutine.h.