xpcc
Resumables

An implementation of lightweight resumable functions which allow for nested calling. More...

Classes

class  xpcc::NestedResumable< Levels >
 This is the base class which must be inherited from for using nested resumable functions in your class. More...
 
struct  xpcc::ResumableResult< T >
 All resumable functions return an encapsulated result type. More...
 
class  xpcc::Resumable< Functions >
 Resumable base class. More...
 

Macros

#define RF_BEGIN(index)
 Declare start of resumable function with index. More...
 
#define RF_BEGIN()
 Declare start of resumable function with index. More...
 
#define RF_END_RETURN(result)
 End the resumable function and return a result. More...
 
#define RF_END()
 End the resumable function. More...
 
#define RF_END_RETURN_CALL(resumable)
 End the resumable function by calling another resumable function and returning its result. More...
 
#define RF_YIELD()
 Yield resumable function until next invocation.
 
#define RF_WAIT_THREAD(child)
 Cause resumable function to wait until given child protothread completes.
 
#define RF_WAIT_WHILE(condition)
 Cause resumable function to wait while given condition is true.
 
#define RF_WAIT_UNTIL(condition)
 Cause resumable function to wait until given condition is true.
 
#define RF_CALL(resumable)
 Calls a resumable function and returns its result.
 
#define RF_CALL_BLOCKING(resumable)
 Calls a resumable function, busy-waits and returns its result. More...
 
#define RF_RETURN_CALL(resumable)
 Exits a resumable function and returns another resumable function's result.
 
#define RF_RETURN(result)
 Stop and exit from resumable function with a result. More...
 
#define RF_RETURN()
 Stop and exit from resumable function with a result. More...
 
#define XPCC_RESUMABLE_CHECK_NESTING_DEPTH
 Nested Resumables protect against memory corruption by checking if the nesting level is within the allocated nesting level depth. More...
 
#define XPCC_RESUMABLE_MODULE_NAME
 

Detailed Description

This base class and its macros allows you to implement and use several resumable functions in one class. This allows you to modularize your code by placing it into its own resumable functions instead of the placing everything into one big method. It also allows you to call and run resumable functions within your resumables, so you can reuse their functionality.

Note that you should call resumable functions within a protothreads. It is sufficient to use the this pointer of the class as context when calling the resumables. So calling a resumable function is done using PT_CALL(resumable(this)) which will return the result of the resumable function.

You may use the RF_CALL_BLOCKING(resumable(ctx)) macro to execute a resumable function outside of a protothread, however, this which will force the CPU to busy-wait until the resumable function ended.

Here is a (slightly over-engineered) example:

#include <xpcc/architecture.hpp>
#include <xpcc/processing.hpp>
typedef GpioOutputB0 Led;
class BlinkingLight : public xpcc::pt::Protothread, private xpcc::NestedResumable<2>
{
public:
bool
run()
{
// set everything up
Led::setOutput();
Led::set();
while (true)
{
Led::set();
PT_CALL(waitForTimer()))
Led::reset();
PT_CALL(setTimer(200));
PT_WAIT_UNTIL(timeout.isExpired());
}
PT_END();
}
waitForTimer()
{
// nested calling is allowed
if (RF_CALL(setTimer(100)))
{
RF_WAIT_UNTIL(timeout.isExpired());
RF_RETURN(true);
}
RF_END_RETURN(false);
}
setTimer(uint16_t timeout)
{
timeout.restart(timeout);
if(timeout.isRunning())
RF_RETURN(true);
// clean up code goes here
RF_END_RETURN(false);
}
private:
};
...
BlinkingLight light;
while (...) {
light.run();
}

For other examples take a look in the examples folder in the XPCC root folder.

Macro Definition Documentation

#define RF_BEGIN (   index)

Declare start of a nested resumable function.

Warning
Use at start of the resumable() implementation!

This will immidiately return if the nesting is too deep.

Warning
Use at start of the resumable() implementation!
#define RF_BEGIN ( )

Declare start of a nested resumable function.

Warning
Use at start of the resumable() implementation!

This will immidiately return if the nesting is too deep.

Warning
Use at start of the resumable() implementation!
#define RF_END_RETURN (   result)
Warning
Use at end of the resumable() implementation only!
#define RF_END ( )

You can use this to return void, or if the result does not matter.

Warning
Use at end of the resumable() implementation only!
#define RF_END_RETURN_CALL (   resumable)
Warning
Use at end of the resumable() implementation only!
#define RF_CALL_BLOCKING (   resumable)
Warning
Use this with extreme caution, this can cause deadlocks!
#define RF_RETURN (   result)

Stop and exit from resumable function of void return type.

#define RF_RETURN ( )

Stop and exit from resumable function of void return type.

#define XPCC_RESUMABLE_CHECK_NESTING_DEPTH

If the allocated nesting level is exceeded, the resumable function does not execute, but returns the xpcc::rf::NestingError state value. However, the PT_CALL() or RF_CALL() macros are not constructed to handle this error and will interpret this error as a normal resumable function stop and therefore immediately continue program execution.

Since the required nesting depth of a nested resumable function can be known before runtime by looking at the source code and counting the nesting levels, this check acts only as a protection against executing random code, when entering too many levels. So instead of the entire program crashing, the resumable function simply does not execute at all, which can make it easier to track down the problem too few allocated nesting levels.

You as a programmer are responible for allocating the correct amount of nesting levels! However, if you are sure that you have counted the levels correctly and your code is not breaking, you may remove this check from all resumable functions in your project.cfg:

[defines]
XPCC_RESUMABLE_CHECK_NESTING_DEPTH = false
See also
NestedResumable