In QP/C++ Framework, as in any event-driven system, events are frequently passed from producers to consumers. The exchanged event instances can be either immutable or mutable.
Immutable events are event instances that never change at runtime. Such immutable events can be pre-allocated statically (typically and preferably in ROM) and can be shared safely among any number of concurrent entities (Active Objects, ISRs, "naked" threads, etc.) rather than being created and recycled dynamically every time. QP/C++ Framework does not need to manage memory for immutable events, but needs to clearly distinguish them from mutable events, precisely to avoid any memory management for them.
Many event instances, especially events with parameters cannot be easily made immutable because their main function is specifically to deliver information produced at runtime. Consequently, such mutable events must be dynamically created, filled with information (mutated), passed around, and eventually recycled. The management of these processes at runtime is one of the most valuable services QP/C++ Framework can provide to QP/C++ Application.
The main challenge of managing mutable events is to guarantee that once a mutable event gets posted or published (which might involve event multicasting), it does not change and does not get recycled until all Active Objects have finished their Run-to-Completion processing of that event. In fact, changing or premature recycling the current event constitutes a violation of the RTC semantics.
Every dynamically created mutable event must be eventually recycled, or the memory would leak. Some event-driven systems leave the event recycling to the application, but in the safety-related context, this is considered unreliable and unsafe. Therefore, the event memory management in QP/C++ Framework must also include automatic event recycling.
From the safety point of view, the ideal would be to copy entire mutable events into and out of the event queues, as it is often done with the message queues of a traditional RTOS. Unfortunately, it is prohibitively expensive in RAM and nondeterministic in CPU cycles for larger event instances (events with large parameters). However, an event-driven framework, like QP, can be far more sophisticated than a traditional RTOS because, due to inversion of control, the framework manages an event's whole life cycle. The framework extracts an event from the Active Object's event queue and dispatches it for processing. After the Run-to-Completion processing, the framework regains control of the event and can automatically recycle the event.
An event-driven framework can also easily control the dynamic allocation of mutable events (e.g., the QP/C++ Framework provides API for this purpose). All this permits the framework to implement controlled, concurrency-safe sharing of mutable events, which, from the application standpoint, is almost indistinguishable from copying entire events. Such event management is called zero-copy event management.
The whole event memory management must also carefully avoid concurrency hazards around the shared mutable events. Failure in any of those aspects results in defects (bugs) that are the hardest to detect, isolate, and fix.
The zero-copy event management is designed to be intuitive and transparent to the application-level code. However, for the zero-copy event management abstraction to behave exactly like true event copying, QP/C++ Application needs to obey specific event ownership rules similar to the rules of working with objects allocated with the C++ operator new
and summarized in the life cycle diagram of a mutable event (see Figure SRS-EVT-LIFE). In exchange, QP/C++ Framework can safely and deterministically deliver your mutable events with hard real-time performance, which does not complicate the RMS/RMA method and is superior to the copying entire events approach.
Figure SRS-EVT-LIFE illustrates the mutable event life cycle and possible transfers of ownership rights to the event:
[0]
All mutable events are initially owned by QP/C++ Framework.
[1]
An event producer might gain ownership of a new event only by allocating it. At this point, the producer gains the ownership rights with the permissions to write to the event. Indeed, the purpose of this stage in the mutable event's life cycle is to initialize the event and fill it with data. The event producer might keep the event as long as it needs. For example, the producer (e.g., ISR) might fill the event with data over many invocations. Eventually, however, the producer must transfer the ownership back to the framework.
[2a,2b,2c]
Typically the producer posts [2a]
or publishes [2b]
the event. As a special case, the producer might decide that the event is not good, in which case the producer must explicitly recycle [2c]
the event. After any of these three operations, the producer immediately loses ownership of the event and can no longer access it. In particular, it is illegal to post, publish, or recycle the event again.
[3]
The recipient Active Object gains ownership of the current event upon the start of the RTC step. This time, the Active Object gains the read-only permissions to the current event.
[4a,4b]
During the RTC step, the recipient Active Object is allowed to re-post [4a]
or re-publish [4b]
the current event any number of times without losing ownership of the event.
[5a]
As a special case, the recipient Active Object may defer the current event. Event deferral extends the read-only ownership rights beyond the current RTC step.
[5b]
Eventually, however, the deferred event must be recalled, which self-posts the event into the Active Object's event queue (using the LIFO policy). Recalling ends the ownership of the original deferred event.
[6]
The end of the RTC step terminates the ownership of the current event. The Active Object cannot use the event in any way past the RTC step. In particular, if any data from that event is needed in the future, QP/C++ Application must save that data (typically in some attributes inside the Active Object).
To manage the memory for mutable events, QP/C++ Framework needs deterministic, efficient, and concurrency-safe method of dynamically allocating and recycling the event memory. The general-purpose, variable-block-size heap does not fit this bill and is inappropriate for safety-related applications, anyway. However, simpler, higher-performance, and safer options exist to the general-purpose heap. A well-known alternative, commonly supported by RTOSs, is a fixed-block-size heap, also known as a memory partition or memory pool. Memory pools are a much better choice for a real-time framework like QP/C++ to manage mutable event memory than the general-purpose heap. Unlike the conventional (variable-block-size) heap, a memory pool is deterministic, has guaranteed capacity, and is not subject to fragmentation because all blocks are exactly the same size.
The most obvious drawback of a memory pool is that it does not support variable-sized blocks. Consequently, the blocks have to be oversized to handle the biggest possible allocation. Such a policy is often too wasteful if the actual sizes of allocated objects (mutable events, in this case) vary a lot. A good compromise is often to use not one but multiple memory pools with memory blocks of different sizes. QP/C++ Framework chooses that option to implement event pools, which are multiple memory pools specialized to hold mutable events.
SRS_QP_EMM_00 : QP/C++ Framework shall support immutable events. |
---|
Description Support for immutable events means that QP/C++ Framework shall provide a way to create and initialize them (including allocation and initialization in ROM). Also, QP/C++ Framework must recognize and distinguish immutable events from mutable events and should never attempt to manage immutable events as mutable. |
Use Case QP/C++ Framework can fulfill this requirement by embedding a unique immutable-event signature in the internal data of the event (see SRS_QP_EVT_31). QP/C++ Framework must then provide a constant initializer for immutable events. |
Forward Traceability
|
SRS_QP_EMM_10 : QP/C++ Framework shall support mutable events. |
---|
Description Support for mutable events means that QP/C++ Framework shall provide a way to create and initialize them. Also, QP/C++ Framework must recognize and distinguish mutable events from immutable events and should never attempt to manage mutable events as immutable. |
Forward Traceability
|
SRS_QP_EMM_11 : QP/C++ Framework shall support up to 15 event pools for mutable events. |
---|
Description Support for mutable events means that QP/C++ Framework shall support multiple, deterministic event pools with different block sizes. The maximum number of event pools managed by QP/C++ Framework at any given time shall be compile-time configurable with the maximum of 15 event pools. |
Use Case QP/C++ Application configures QP/C++ for the maximum number of event pools to 5 but initializes only 3 event pools, each for a different event size. |
Forward Traceability
|
SRS_QP_EMM_20 : QP/C++ Framework shall provide a method of allocating mutable events at runtime. |
---|
Description The allocation shall choose the appropriate event pool from which to allocate the event based on the event size. The method of allocating mutable events shall be available to all event producers, such as Active Objects, but also ISRs, "device drivers", or "naked" thread of an RTOS (if a traditional RTOS is used). |
Use Case QP/C++ Framework might meet this requirement by sorting the event pools internally based on their increasing block size. Then any given event is allocated from the smallest block-size event pool that fits the requested event size. |
SRS_QP_EMM_30 : QP/C++ Framework shall support automatic recycling of mutable events according to the "zero-copy" memory management policy. |
---|
Description QP/C++ Framework shall keep track of every use of a mutable event, so that it can support the event life-cycle and ownership rules spelled out in Figure SRS-EVT-LIFE. In particular, QP/C++ Framewrok shall automatically recycle an unused event. |
Use Case One way to meet this requirement is for QP/C++ Framewrok to use the standard reference-counting algorithm for mutable events. |
Forward Traceability |
SRS_QP_EMM_40 : QP/C++ Framework shall provide a method of explicitly recycling mutable events. |
---|
Description The method of recycling mutable events back to the appropriate event pool shall be available to all event producers, such as Active Objects, but also ISRs, "device drivers", or "naked" thread of an RTOS (if a traditional RTOS is used). |