modm API documentation
|
#define | RF_BEGIN(index) |
#define | RF_BEGIN() |
#define | RF_END_RETURN(...) |
#define | RF_END() |
#define | RF_END_RETURN_CALL(...) |
#define | RF_YIELD() |
#define | RF_WAIT_THREAD(...) |
#define | RF_WAIT_WHILE(...) |
#define | RF_WAIT_UNTIL(...) |
#define | RF_CALL(...) |
#define | RF_CALL_BLOCKING(...) |
#define | RF_RETURN_CALL(...) |
#define | RF_RETURN(...) |
template<typename T > | |
using | modm::ResumableResult = T |
Resumable functions implemented via fibers return like a normal function. | |
lbuild module: modm:processing:resumable
modm:processing:fiber
instead.An implementation of lightweight resumable functions which allow for nested calling.
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, with the PT_CALL(group.resumable())
macro, which will return the result of the resumable function. To call a resumable function inside another resumable function, use the RF_CALL(group.resumable())
.
You may use the RF_CALL_BLOCKING(group.resumable())
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.
You can either run your group of resumable functions independently from each other, or only run one function at a time, but it may nest calls to its own group's functions. So you need to inherit your group from modm::Resumable<#Functions>
, and from modm::NestedResumable<#Functions>
respectively.
You must begin each resumable function using RF_BEGIN(index)
where index
is the unique index of your resumable function starting at zero. You may exit and return a value by using RF_RETURN(value)
or return the result of another resumable function using RF_RETURN_CALL(resumable())
. This return value is wrapped in a modm::ResumableResult<Type>
struct and transparently returned by the RF_CALL
macro so it can be used to influence your program flow. If the resumable function reaches RF_END()
it will exit automatically, with the result of 0
cast to the return type. Should you wish to return a value at the end, you may use RF_END_RETURN(value)
. You may also return the result of another resumable function using RF_END_RETURN_CALL(resumable())
.
Be aware that this class keeps a separate state for each of your resumable functions. This allows each resumable function to be run at the same time. This might require the use of an internal semaphore or mutex if such dependencies exist in your use case. Take a look at the NestedResumable
class for mutually exclusive resumable functions, which also require a little less memory.
You are responsible to choosing the right nesting depth! This class will guard itself against calling another resumable function at too deep a nesting level and fail the rf.nest
assertion! It is then up to you to recognize this in your program design and increase the nesting depth or rethink your code.
The resumable functions of this class are mutually exclusive, so only one resumable function of the same object can run at the same time. Even if you call another resumable function, it will simply return modm::rf::WrongState
. Using the RF_CALL(resumable())
macro, you can wait for these resumable functions to become available and then run them, so you usually do not need to worry about those cases.
You must begin each resumable function using RF_BEGIN()
. You may exit and return a value by using RF_RETURN(value)
or return the result of another resumable function using RF_RETURN_CALL(resumable())
. This return value is wrapped in a modm::ResumableResult<Type>
struct and transparently returned by the RF_CALL
macro so it can be used to influence your program flow. If the resumable function reaches RF_END()
it will exit automatically, with the result of 0
cast to the return type. Should you wish to return a value at the end, you may use RF_END_RETURN(value)
. You may also return the result of another resumable function using RF_END_RETURN_CALL(resumable())
.
Here is a (slightly over-engineered) example:
For other examples take a look in the examples
folder in the modm root folder. The given example is in modm/examples/generic/resumable
.
Resumable functions can be implemented using stackful fibers by setting the modm:processing:protothread:use_fiber
option, which replaces the preprocessor macros and C++ implementations of this and the modm:processing:protothreads
module with a fiber version.
Specifically, the PT_*
and RF_*
macros are now forwarding their arguments unmodified and instead relying on modm::this_fiber::yield()
for context switching:
You may call RF_CALL_BLOCKING(resumable)
outside a fiber context, in which case the modm::this_fiber::yield()
will return immediately, which is the same behavior as before.
However, the modm::ResumableResult
, modm::Resumable
, and modm::NestedResumable
classes are now empty implementations:
There should be no modification necessary to the resumable functions itself.
There is one minor limitations when using fibers: 1) no nesting depth checking for NestedResumable
, 2) stopping resumable functions is not implementable.
The nesting depth is limited by the stack size of the fiber, so it would manifest as a stack overflow, which would need to be checked differently.
The utility member functions
Resumable::stopAllResumables()
,Resumable::stopResumable(uint8_t)
, andNestedResumable::stopResumable()
cannot be implemented using fibers as that would require the resumable to implement a cancellation point using modm::fiber::stop_token
. However, since there is no concept of this in the original API, it is simply missing and thus cannot be implemented transparently.Nested resumable functions protect against memory corruption by checking if the nesting level is within the allocated nesting level depth, on first entry to the function. If the allocated nesting level is exceeded, the assertion resumable.begin.nesting
fails.
You may disable this behavior by disabling this check, then instead of the assertion, the function on entry returns the modm::rf::NestingError
state value. PT_CALL()
and RF_CALL()
macros will respond to this error by stopping function polling and just continuing program execution.
RF_BEGIN(N)
, so exactly once on function entry and not during every polling call, so the performance penalty is relatively small.Generated with: yes in [yes, no]
#define RF_BEGIN |
Declare start of resumable function with index.
resumable()
implementation!Declare start of a nested resumable function. This will immediately return if the nesting is too deep.
resumable()
implementation! #define RF_BEGIN |
Declare start of resumable function with index.
resumable()
implementation!Declare start of a nested resumable function. This will immediately return if the nesting is too deep.
resumable()
implementation! #define RF_CALL |
Calls a resumable function and returns its result.
#define RF_CALL_BLOCKING |
Calls a resumable function, busy-waits and returns its result.
#define RF_END |
End the resumable function. You can use this to return void
, or if the result does not matter.
resumable()
implementation only! #define RF_END_RETURN |
End the resumable function and return a result.
resumable()
implementation only! #define RF_END_RETURN_CALL |
End the resumable function by calling another resumable function and returning its result.
resumable()
implementation only! #define RF_RETURN |
Stop and exit from resumable function with an optional result.
#define RF_RETURN_CALL |
Exits a resumable function and returns another resumable function's result.
#define RF_WAIT_THREAD |
Cause resumable function to wait until given child protothread completes.
#define RF_WAIT_UNTIL |
Cause resumable function to wait until given condition
is true.
#define RF_WAIT_WHILE |
Cause resumable function to wait while given condition
is true.
#define RF_YIELD |
Yield resumable function until next invocation.