User documentation
Overview
The async
module defines an interface for asynchronous execution of runnable objects in specific execution contexts.
To be able to run code on arbitrary platforms in a generic way, this module provides an abstraction on top of
OS specific primitives. async
uses the underlying async::AsyncBinding
type to access to specific OS
implementations, for instance via async::FreeRtosAdapter
for FreeRTOS OS.
Features
Three asynchronous (non-blocking) functions are declared by async in Async.h
:
execute |
Allows to execute a |
schedule |
Allows to schedule a |
scheduleAtFixedRate |
Allows to schedule a |
The util/Call.h
declares a runnable class that allows customized implementation on execution
by providing callable object (with function call operator ()
).
For example the predefined async::Function
type is declaring ::estd::function
as callable type.
The util/MemberCall.h
provides a async::MemberCall
class, which allows to create a async::RunnableType
from a member function of a non-runnable class.
Examples
Making runnable class and scheduling it
Define a class that implements the async::IRunnable
interface:
#include <async/Async.h>
class RunnableImpl : public ::async::IRunnable
{
public:
void execute() override
{
fprintf(stdout, "Executing user logic!\n");
}
};
In client code allocate the runnable object and asynchronously execute it:
static RunnableImpl runnable;
static ::async::TimeoutType timeout;
void executeRunnable(::async::ContextType& context)
{
// asynchronously execute the runnable:
::async::execute(context, runnable);
// schedule the runnable for single-time execution after 1 second from now:
::async::schedule(context, runnable, timeout, 1, ::async::TimeUnit::SECONDS);
// schedule the runnable for cyclic execution with period 2 seconds:
::async::scheduleAtFixedRate(context, runnable, timeout, 2, ::async::TimeUnit::SECONDS);
}
Use async::Function
to provide customized implementation on execution
Allocate the timeout variables and define execution callback functions:
static constexpr ::async::TimeUnitType PERIOD_IN_S = 1;
static ::async::TimeoutType runTimeout;
static ::async::TimeoutType cancelTimeout;
void runModule()
{
fprintf(stdout, "running!\n");
}
void stopModule()
{
runTimeout.cancel();
fprintf(stdout, "stopped!\n");
}
In client code specify the async::Function
objects and schedule them:
void startStopModule(::async::ContextType& context)
{
// execute runModule every PERIOD_IN_S:
::async::Function moduleRunnable(runModule);
::async::scheduleAtFixedRate(context, moduleRunnable, runTimeout, PERIOD_IN_S, ::async::TimeUnit::SECONDS);
// execute stopModule after 2*PERIOD_IN_S:
::async::Function cancelRunnable(stopModule);
::async::schedule(context, cancelRunnable, cancelTimeout, 2*PERIOD_IN_S, ::async::TimeUnit::SECONDS);
}
This example could produce the following output, if startStopModule()
is called:
running!
running!
stopped!
Use async::MemberCall
to force non-runnable class behave like runnable
Declare a non-runnable class:
#include <async/Async.h>
#include <async/util/MemberCall.h>
class NonRunnable // the class is NOT inheriting from Runnable!
{
public:
void run()
{
fprintf(stderr, "running!\n");
}
// must not necessarily be member of the class:
::async::MemberCall<NonRunnable> memberCall{this, &NonRunnable::run};
};
In client code, use the async::MemberCall
object to schedule the execution:
void executeNonRunnable(NonRunnable & nonRunnable, ::async::ContextType& context)
{
// asynchronously call the run method of the NonRunnable object:
::async::execute(nonRunnable.memberCall, 0);
// asynchronously call the run method with delay 1 second:
::async::schedule(context, nonRunnable.memberCall, timeout, 1, ::async::TimeUnit::SECONDS);
// asynchronously call the run method periodically every 2 seconds:
::async::scheduleAtFixedRate(context, nonRunnable.memberCall, timeout, 2, ::async::TimeUnit::SECONDS);
}
Relevant types
Context
async::ContextType
represents an execution context. All functions that run in the same context are guaranteed
to be run sequentially, allowing safe access to shared resources.
It can be created from a uint8_t
and is copyable, assignable, and comparable, with a defined invalid value (CONTEXT_INVALID
).
Runnable
async::RunnableType
is an object that defines a void execute(void)
method, allowing it to be executed.
The async::IRunnable
interface in async::AsyncImpl
is one example of a async::RunnableType
, used by certain implementations.
Locks
async::LockType
and async::ModifiableLockType
are scoped locks. The modifiable counterpart can be unlocked
and locked manually.
Warning
The funcional and non-functional semantics of these lock types can differ between implementations of this module for different target platforms. In some cases, platform specific usage invariants may apply. Using these lock types will impact the software’s real-time performance and should only be employed when absolutely necessary and with a clear understanding of their broader impact.
Time units
async::TimeUnitType
is an alias of TimeUnit
type providing a definition of micro-, milli-, and full seconds.
The following values are available within the async::TimeUnit
scope:
TimeUnit::MICROSECONDS
TimeUnit::MILLISECONDS
TimeUnit::SECONDS
Timeout
async::TimeoutType
provides the necessary memory to support the use of schedule()
and scheduleAtFixedRate()
.
It can be cancelled using its cancel()
method.
Integration
If a module uses async
, it must verify the correct usage of types by writing a unit test with the async
mocks provided in the mock/gmock
subfolder.
The following mocks are available, each of which mocks all async
methods:
AsyncMock
LockMock
TimeoutMock
When mocking all of async is not desired, TestContext.h
and .cpp
can be used.
They provide a way to emulate async behaviour, that can be triggered manually for a specific context.
Porting to a new platform
To port async to a new platform, one must start by providing corresponding AdapterType, mapping the concepts to the target platform.