Platforms
Warning
TODO: add API links
Most of the hsmcpp library’s functionality is platform independent. But implementation of events dispatching, timers and multi-threading support highly depends on underlying OS. To allow support of various execution environments platform and framework specific parts were separated into an abstraction layer. Required abstractions include:
mutex
critical section
semaphore
Supported Platforms
Platform selection is done using HSMBUILD_PLATFORM build setting. Depending on the provided value, some dispatchers might not be available:
Value |
Supported Dispatchers |
Multi Threading |
Interrupts |
Description |
---|---|---|---|---|
posix |
STD |
✅ |
✅ |
This is the default implementation of OS specific primitives based on standard C++ library. Any platform, that has a full set of std features available (specifically threads and synchronization) and is POSIX compliant, can be covered by this implementation. For example: Linux, QNX, Integrity, etc. |
Glib |
✅ |
✅ |
||
Glibmm |
✅ |
✅ |
||
Qt |
✅ |
✅ |
||
windows |
STD |
✅ |
❌ |
Same as POSIX based implementation, but doesn’t include support for interrupts/signals. |
Qt |
✅ |
❌ |
||
arduino |
Arduino |
❌ |
✅ |
Arduino doesn’t provide any multi-process or multi-threading support. So abstraction layer for this platform merely provides an empty stub for a common logic to compile. All dispatching and callbacks execution is done sequentially and no synchronization is required. |
freertos |
FreeRTOS |
✅ |
✅ |
Even though FreeRTOS comes with a basic implementation of std library, it lacks threads related functionality due to a bit different approach to concurrency (Tasks and Interrupts instead of threads). |
Dispatchers Overview
hsmcpp library is asynchronous by design. All transitions within it are handled through events queue. To make things asynchronous, processing of this queue must be done on a separate thread or some kind of event loop. Since requirements to such processing mechanism will be different from project to project, it was decided to make it an additional abstraction (IHsmEventDispatcher) which could be replaced based on your specific needs.
IHsmEventDispatcher responsibilities include:
asynchronously invoke listeners callback after receiving event was emitted;
provide thread-safe way to emit events;
provide interrupts-safe way to emit events;
start/stop/restart timers;
maintain a list of listeners.
When initializing HSM object it’s required to provide an instance of IHsmEventDispatcher. Unless you keep a copy of shared_ptr with dispatcher instance, it will be distroyed automatically during HSM object destruction. Clients can use on of the built-in dispatchers or implement their own.
Built-in Dispatchers
Currently hsmcpp includes the following dispatchers:
Dispatcher |
Creates Thread |
Create / Init / Delete |
Details |
---|---|---|---|
HsmEventDispatcherSTD |
✅ |
anywhere |
Internally starts std::thread and uses it to dispatch HSM events. |
HsmEventDispatcherGlib |
❌ |
event loop |
Dispatches hsm events through Glib main loop. |
HsmEventDispatcherGlibmm |
❌ |
event loop |
Same as HsmEventDispatcherGlib, but uses Glibmm (C++ wrapper over Glib) and Glib::Dispatcher to handle events. |
HsmEventDispatcherQt |
❌ |
event loop |
Dispatches hsm events through main Qt event loop. |
HsmEventDispatcherArduino |
❌ |
anywhere |
Dispatches hsm events every time dispatchEvents() method is called in Arduino’s loop() function. |
HsmEventDispatcherFreeRTOS |
✅ |
FreeRTOS Task |
Internally starts FreeRTOS task and uses it to dispatch HSM events. |
Since these dispatchers have dependencies on external libraries (and only one of them is usually needed) you need to explicitly enable them for compilation using these CMake options:
HSMBUILD_DISPATCHER_GLIB
HSMBUILD_DISPATCHER_GLIBMM
HSMBUILD_DISPATCHER_STD
HSMBUILD_DISPATCHER_QT
HSMBUILD_DISPATCHER_FREERTOS
HsmEventDispatcherSTD
HsmEventDispatcherSTD is a simple thread based dispatcher. Internally it uses std::thread to start a new thread.
Simplified logic:
This dispatcher is compatible with all types of applications and should not interfere with existing event loops from other frameworks. If desirable it’s also possible to use HsmEventDispatcherSTD as a replacement of your application main loop. To do so you need to call HsmEventDispatcherSTD::join() to prevent main thread from exiting. For reference see /examples/00_helloworld/00_helloworld_std.cpp.
HsmEventDispatcherGlib
HsmEventDispatcherGlib utilizes GLib main loop and pipe to dispatch events. Clients can specify which GLib context to use (if application has multiple) by providing GMainContext to constructor:
explicit HsmEventDispatcherGLib(GMainContext* context);
To use default GLib main loop just use default constructor:
HsmEventDispatcherGLib();
HsmEventDispatcherGlibmm
In general is same as HsmEventDispatcherGlib, but it utilizes GLib::Dispatcher class to handle events. Due to GLib::Dispatcher implementation this applies some restrictions:
HsmEventDispatcherGLibmm must be constructred and destroyed in the receiver thread (the thread in whose main loop it will execute its connected slots)
registerEventHandler() must be called from the same thread where dispatcher was created.
For more details see: Using Glib::Dispatcher
Not following these rules will result in an occasional SIGSEGV crash (usually when deleting dispatcher instance).
Unless you really have to, it’s always better to reuse a single dispatcher instance for multiple HSMs instead of creating/deliting multiple ones(they will anyway handle events sequentially since they use same Glib main loop).
HsmEventDispatcherQt
HsmEventDispatcherQt utilizes QCoreApplication::postEvent() function for posting events on Qt’s main event loop. As a result all HSM callbacks are executed on the same thread where event loop is running (usually main thread).
HsmEventDispatcherArduino
Dispatching is done by periodically calling dispatchEvents() in the Arduino’s loop() function. All transitions and callbaks are processed within it. Therefore it’s advised to avoid using blocking operations inside HSM callbacks to make your software more responsive. Instead, utilization of async APIs is highly recommended. When possible, try replacing calls to delay() with HSM timeouts.
Note
It’s advised to allocate instances of dispatcher and HSM on heap.
Compiling
Arduino build is not supported in current CMake configuration. Recommended way of including hsmcpp for Arduino software is by using PlatformIO IDE and PlatformIO package.
HsmEventDispatcherFreeRTOS
HsmEventDispatcherFreeRTOS utilizes a custom FreeRTOS Task to handle HSM events and callbacks. Task is created and started during call to dispatcher’s initialize()API.
Note
Creation and initialization of HSM and dispatcher should be done inside of a Task and not main() function.
Compiling
hsmcpp library musts be compiled as part of your application’s build. Make sure to set HSMBUILD_FREERTOS_ROOT build option and specify path to your FreeRTOS root directory. For reference see /examples/08_freertos
FreeRTOS configuration file
Due to the specifics of the platfrom, you would usually need a FreeRTOS configuration file. If you don’t care much about specific settings, hsmcpp library already includes a default configuration (though I do recommend to review it to avoid any unexpected behavior of your code). In case a custom configuration is needed, it can be specified using HSMBUILD_FREERTOS_CONFIG_FILE_DIRECTORY build opion. It should point to a folder containing your FreeRTOSConfig.h file.
Warning
FreeRTOSConfig.h included in hsmcpp repository was tested only with POSIX simulation of FreeRTOS. It should be treated only as a reference and it’s your responsibility to provide correct config for your specific HW.
Additionally, following FreeRTOS features must be enabled:
INCLUDE_xTaskGetCurrentTaskHandle = 1
configUSE_TASK_NOTIFICATIONS = 1
configUSE_TIMERS = 1
configUSE_MUTEXES = 1
configSUPPORT_DYNAMIC_ALLOCATION = 1
If you are using interrupts (ISR) in your code, you must provide implementation for xPortIsInsideInterrupt() API (usually provided by your FreeRTOS port; defined in portmacro.h). In case this API is not available and your are not going to interact with HSM from within interrupts handler, then you can enable default implementation of xPortIsInsideInterrupt() by turning on BUILD_FREERTOS_DEFAULT_ISR_DETECT build option (default implementation simply always returns FALSE).
Warning
Using this option will make most calls to hsmcpp API from ISR unsafe and will result in undefined behavior (most often memory corruption and crash).
Implementing custom dispatchers
Even though STD based dispatcher will work in all situations, sometimes it’s not desirable or even impossible to have an additional unmanaged thread running in the process (for example in case of RTOS systems which often utilize watchdog mechanism). In this case it’s possible to use your own dispatcher by implementing IHsmEventDispatcher interface. When doing so keeping the following things in mind:
emit() method should be thread-safe.
start() method is used by HSM to start event dispatching. It is called during initialize() and must be non blocking. Calling this method when dispatching is already ongoing should always return TRUE.
registerEventHandler must support multiple callbacks registration. This is needed to support sharing dispatcher between different HSM instances.
Even though you can implement IHsmEventDispatcher interface directly, it’s recommended to use HsmEventDispatcherBase as your parent class.
I recommend checking existing dispatchers as a reference to get an idea on how to implement your own: