QP/C++  8.0.0
Real-Time Embedded Framework
Loading...
Searching...
No Matches
Interaction Viewpoint

Structure ViewpointState Dynamics Viewpoint

The Interaction Design Viewpoint provides an analysis of interactions within QP/C++ Framework elements. This design viewpoint frames the following concerns:

  • behavior of the system under various scenarios of use
  • interactions in terms of events exchanged among Active Objects
  • required sequences of operations
  • control flow
  • concurrency and parallelism
  • resource management

Behavior View

SDS_QA_START

SDS_QA_START : QA Application startup sequence

Model Kinds
The QA Application startup sequence is illustrated with two kinds of models:

Figure SDS-START: QP/C++ Application startup sequence.

[0] int main(int argc, char *argv[]) {
[1] QP::QF::init(); // initialize the QF Active Object framework
[2] BSP::init(); // initialize the Board Support Package
[3] // initialize publish-subscribe...
static QP::QSubscrList subscrSto[APP::MAX_PUB_SIG];
QP::QActive::psInit(subscrSto, Q_DIM(subscrSto));
[4] // initialize event pools...
static QF_MPOOL_EL(APP::SmallEvt) smlPoolSto[10];
QP::QF::poolInit(smlPoolSto, sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
static QF_MPOOL_EL(MediumEvt) medPoolSto[5];
QP::QF::poolInit(medPoolSto, sizeof(medPoolSto), sizeof(medPoolSto[0]));
. . .
[5] // start Active Objects...
static QP::QEvt const *aoA_QueueSto[5];
APP::AO_activeA->start(
2U, // @QPX priority of the AO
aoA_QueueSto, // event queue storage
Q_DIM(aoA_QueueSto), // queue length [events]
nullptr, 0U, // no stack storage
nullptr); // no initialization param
static QEvt const *aoB_QueueSto[3];
APP::AO_activeB->start(
5U, // @QPX priority of the AO
aoB_QueueSto, // event queue storage
Q_DIM(aoB_QueueSto), // queue length [events]
nullptr, 0U, // no stack storage
nullptr); // no initialization param
. . .
[6] // transfer control to QF framework to run the Active Objects
return QP::QF::run();
}
static void psInit(QSubscrList *const subscrSto, enum_t const maxSignal) noexcept
Definition qf_ps.cpp:75
Event class.
Definition qp.hpp:131
Subscriber List (for publish-subscribe)
Definition qp.hpp:662
void poolInit(void *const poolSto, std::uint_fast32_t const poolSize, std::uint_fast16_t const evtSize) noexcept
Definition qf_dyn.cpp:64
void init()
Definition qv.cpp:131
int_t run()
Definition qv.cpp:153
#define QF_MPOOL_EL(evType_)
Definition qmpool.hpp:69
#define Q_DIM(array_)
Definition qp.hpp:501
Listing SDS-START: QP/C++ Application startup sequence.

Description
The following descriptions pertain to both Figure SDS-START and Listing SDS-START because the steps are labeled consistently in these two model kinds.

[0] The QP/C++ Application startup sequence occurs in the main() function.

[1] The Active Object framework layer gets initialized, which also performs initialization of the real-time kernel.

[2] The Board Support Package (BSP) initialization sets up the hardware, software tracing (if used), etc.

[3] If this QP/C++ Application uses publish-subscribe, it is initialized with a call to QP::QActive::psInit().

Note
The QP/C++ Application must provide the memory for the subscriber-lists (subscrSto), which is allocated statically in this case.

[4] The QP/C++ Application initializes all event pools that it is using by repeated calls to QP::QF::poolInit()

Note
The QP/C++ Application must provide the memory for each of the initialized event pools (e.g., medPoolSto), which is allocated statically in this case.

[5] The QP/C++ Application instantiates (by calling the constructor) and starts all Active Objects by calling QP::QActive::start(). Each Active Objects is assigned unique priorities, and provided with event-queue buffers and the stacks (if required). Also, this step involves executing the top-most initial transition in the Active Object (Figure SDS-START [5B]). Additionally, this step triggers the top-most initial transition in the Active Object's state machine, which might involve some interaction with the BSP to initialize the hardware controlled by this Active Object (Figure SDS-START [5C]).

Note
The QP/C++ Application must provide the event-queue buffer (e.g., aoA_QueueSto) and the stack (if required by the underlying kernel) for each started Active Object.

[6] The QP/C++ Application transfers control to QF Active Object Framework by calling QP::QF::run(). QP::QF::run() begins by calling the QP::QF::onStartup() callback to configure and enable interrupts (Figure SDS-START [6A]), as the system is only now ready to receive them. After this, the QF Framework starts execution of Active Objects (Figure SDS-START [6B]), which typically involves transferring control to the underlying real-time kernel.

Note
In deeply embedded applications QP::QF::run() does not return. In case QP/C++ runs on top of a General-Purpose OS (e.g., POSIX), QP::QF::run() might return to the OS. The behavior of QP::QF::run() depends on the QP/C++ Port (see QP OSAL).
Backward Traceability
Forward Traceability

Event Exchange View

SDS_QP_POST

SDS_QP_POST : QP event posting sequence

Model Kinds
Event posting to Active Objects is illustrated with UML sequence diagrams shown in Figure SDS-POST1 and Figure SDS-POST2 (dynamic views). The views are explained in the labeled descriptions following the sequence diagrams. The chosen scenarios pertain to mutable events dynamically allocated from event pools. Also the shown order of processing corresponds to a preemptive scheduler.

Scenario: Event posting from ISR to Active Object

Figure SDS-POST1: Posting event form ISR to an Active Object.

[1] Event posting begins with incrementing the reference counter of the event (for mutable events dynamically allocated from event pools), which happens within a critical section.

[2] The event is posted to the event queue of the recipient Active Object. The default behavior of the queue is to assert internally that the queue does not overflow and can accept the event (this is part of the evnet delivery guarantee).

[2A] Assuming that the recipient Active Object didn't have events before, adding an event to its queue makes the Active Object ready to run. However, the Active Object is not assigned the CPU just yet because the ISR has a higher priority and continues to completion. (NOTE: the same behavior would occur if the event was posted from an Active Object of a higher priority than the recipient.)

[3] Only after the ISR completes, the received event is dispatched to the internal state machine of the recipient Active Object. The virtual dispatch() function runs-to-completion (RTC) in the thread context of the recipient Active Object.

[4] After the RTC step, the event is garbage-collected, which decrements the reference counter (for a mutable dynamic event). The event is recycled back to the original event pool only when the reference count drops to zero.

Scenario: Event posting from low-priority to high-priority Active Object

Figure SDS-POST2: Posting event form a lower-priority Active Object to higher-priority Active Object (preemptive scheduler).

[1] As before in Figure SDS-POST1, event posting begins with incrementing the reference counter of the event (for mutable events dynamically allocated from event pools), which happens within a critical section.

[2] As before, the event is posted to the event queue of the recipient Active Object.

[2A] However, assuming that the system executes under a preemptive, priority-based scheduler, the recipient Active Object immediately preempts the sender Active Object. This happens because the recipient has a higher priority than the sender and a preemptive scheduler must always give control to the highest-priority Active Object ready to run.

[3] The recipient Active Object dispatches the event to its internal state machine.

[4] The recipient Active Object calls the garbage-collector to decrement the reference count and recycles the event back to the original event pool.

[5] The CPU is assigned back to the preempted sender Active Object. The sender completes its RTC step.

Note
The sender Active Object should NOT access the event after posing because it might be recycled by that time, as illustrated in this scenario. Please see also dynamic event life-cycle in Figure SRS-EVT-LIFE.
Backward Traceability
Forward Traceability

SDS_QP_PUB

SDS_QP_PUB : QP event publishing sequence

Model Kinds
Event publishing to Active Objects is illustrated with UML sequence diagrams shown in Figure SDS-PUB1 and Figure SDS-PUB2 (dynamic view). The view is explained in the labeled descriptions following the sequence diagram. The chosen scenarios pertain to mutable events dynamically allocated from event pools. Also the shown order of processing corresponds to a preemptive scheduler.

Scenario: Event publishing with scheduler locking

Figure SDS-PUB1: Publishing event form a medium-priority Active Object (preemptive scheduler).

[1] Event publishing begins with incrementing the reference counter of the event (for mutable events dynamically allocated from event pools), which happens within a critical section.

[2] Next, the publish operation determines the highest-priority subscriber and selectively locks the scheduler up to that priority. (NOTE: selective scheduler locking is supported by modern real-time kernels. Older kernels support only indistriminate, global scheduler locking).

[3] Event is posted to the highest-priority subscriber Active Object, which increments the reference counter of the event (per SDS_QP_POST).

[3A] Even if the priority of that subscriber is higher than the sender, preemption does not happen because of the scheduler lock.

[4] Event is posted to the medium-priority subscriber Active Objects, which increments the reference counter of the event (per SDS_QP_POST).

[5] Event is posted to the low-priority subscriber Active Objects, which increments the reference counter of the event (per SDS_QP_POST).

[4A-5A] The events are only posted, but the recipients Active Objects don't run yet.

[6] The publish operation unlocks the scheduler to the previous level before the publishing.

[7] The highest-priority recipient Active Object immediately preempts the lower-priority publisher and dispatches the published event to its state machine.

[8] During the processing, the highest-priority Active Object posts another event e1 to ActiveObjB.

[8A] The posted event e1 is only enqueued, but is not processed.

[9] The highest-priority Active Object continues and garbage-collects the published event. This only decrements its reference counter, but does not recycle the published event.

[10] The event publisher garbage-collects the original event. This decrements the reference counter incremented in step [1].

Note
The garbage-collect step [10] prevents event leak in case there are no subscribers to that event.

[11] ActiveObjB dispatches the published event to its state machine.

[12] The event publisher garbage-collects published event. This decrements the reference counter, but does not recycle the event yet.

[13] ActiveObjB dispatches the posted event e1 to its state machine.

Remarks
ActiveObjB processes events in the expected order (based on cause and effect): published event e followed by posted event e1 (caused by the published event e).

[14] ActiveObjB garbage-collects the posted event e1, which decrements its reference counter and recycles the event.

[15] The lowest-priority ActiveObjA dispatches the published event to its state machine.

[16] The lowest-priority ActiveObjA garbage-collects the original event, which decrements its reference counter. This time, the counter drops to zero, so the event is finally recycled to its original event pool.

Scenario: Event publishing WITHOUT scheduler locking

Figure SDS-PUB2: Publishing event WITHOUT scheduler locking (preemptive scheduler).

[1] As before, event publishing begins with incrementing the reference counter of the event (for mutable events dynamically allocated from event pools), which happens within a critical section.

[2] The publish operation determines the highest-priority subscriber and posts the event to that Active Object. This increments the reference counter of the event (per SDS_QP_POST).

[3] However, this time the scheduler is NOT locked, so the highest-priority Active Object immediately preempts the lower-priority publisher.

[4] During the processing, the higest-priority Active Object posts another event e1 to ActiveObjB.

[4A] The posted event e1 is only enqueued, but is not processed.

[5] The highest-priority Active Object continues and garbage-collects the published event. This only decrements its reference counter, but does not recycle the published event because its reference counter has been incremented in step [1]

[6] The preemptive scheduler resumes the preempted publisher, which posts the original event to ActiveObjB.

Note
At this point, the medium-priority ActiveObjB has two events, which are enqueued in the following order: e1 followed by e. This is an unexpected order because event e1 is caused by event e, yet it precedes event e. This unexpected re-ordering of events is the result of NOT locking the scheduler.

[6A] The posted event is only enqueued, but is not processed.

[7] The preemptive scheduler posts the original event to ActiveObjC.

[7A] The posted event is only enqueued, but is not processed.

[8] The event publisher garbage-collects the original event. This decrements the reference counter incremented in step [1].

Note
The garbage-collect step [8] prevents event leak in case there are no subscribers to that event.

[9] The medium-priority ActiveObjB dispatches the event e1 to its internal state machine.

[10] The medium-priority ActiveObjB garbage-collects event e1, which causes recycling of that event.

[11] The medium-priority ActiveObjB dispatches the event e to its internal state machine.

[12] The medium-priority ActiveObjB garbage-collects published event e, which decrements its reference counter but does not recycle the event yet.

[13] The lowest-priority ActiveObjA dispatches the published event e to its internal state machine.

[14] The lowest-priority ActiveObjA garbage-collects the published event e, which is no longer referenced, so it is recycled.

Remarks
Even without scheduler locking, the event publishing performs event multicasting without event leaks. However, without scheduler locking event publishing might end up with an unexptected re-ordering of events, as illustrated for the medium-priority ActiveObjB in this scenario.
Backward Traceability
Forward Traceability

Structure ViewpointState Dynamics Viewpoint