Modeling your applications with actors, you can get two benefits: messaging and supervising between actors.
Actor is a reactive entity with a state, which can receive and send messages; actor state is usually changed with new messages. Every message has destination (one or more actors) and user-defined payload. Messaging is send with fire-and-forget approach, i.e. sender does not care about further delivery and processing of the message, like with UDP protocol.
The message delivery is asynchronous, as the message is put into the queue, and will be delivered some time later.
With all that properties it is possible to assemble concurrent programs.
Supervising is more about proper actor initialization order, synchronization and failure handling. Synchronization makes it sure that starting sending messages to actors is performed when actors are ready. Failure handling allows to use different restart strategies of a failing actor, or group of actors, or even escalate locally unresolvable problem into upper layers... upto restarting the whole board restart.
rotor-light
is platform-neutral framework, i.e. it can be build and run on any
plaform, which conforms the requirements (C++17, basically).
There is a successful C++ actor microframework rotor, when a few colleagues of mine asked me to have something similar for embedded systems.
The following table highlight the differences
rotor light | rotor | |
---|---|---|
max. number of actors | 64 | unlimited |
max. number of messages | compile-time defined | unlimited |
thread-safety | no | yes |
message priorities | yes | no |
request-response pattern | no | yes |
actors discovery & linking | no | yes |
multiple I/O backends | no (1) | yes |
timers | yes (1) | yes |
non-intrusiveness (2) | yes | yes |
dynamic allocations | no | yes |
C++ standard | C++17 | C++17 |
dependencies | no (except, stdlib++) | boost, event-loops |
multiple addresses per actor | no | yes |
multiple recipients | yes, via broadcasting | yes |
uses RTTI | no | yes |
(1) rotor-light
needs only "now" function from timer, the time point and
time interval are abstract notions, have to be defined by end-user of the library.
(2) Non-intrusiveness means, that a framework does not owns execution thread: when all messages have been processed, it just exits as there is nothing to do. That way it is possible to integrate a framework with other event-loops or enter into power-save mode for MCU or even let it go into sleep and wakeup on external event (interrupt).
There are a lot of useful patterns and explanations in rotor documentation, it is worth familiarize self with it to get some insights.
rotor-light
uses cmake build system. It has no dependencies,
except C++17 core libraries (e.g. <tuple>
,<type_traits>
etc.). Basically, the
framework can be build via
mkdir build
cd build
cmake ..
make -j4
There are few customization options:
ROTOR_LIGHT_TIMEPOINT
defines the type to be used as timepoint. The default
value is int64_t
ROTOR_LIGHT_DURATION
defines the type to be used as time interval. The default
value is int32_t
Usage example:
cmake -DROTOR_LIGHT_TIMEPOINT=int32_t ..