QP/C++ 8.1.2
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_actq.cpp
Go to the documentation of this file.
1//============================================================================
2// QP/C++ Real-Time Event Framework (RTEF)
3//
4// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
5//
6// Q u a n t u m L e a P s
7// ------------------------
8// Modern Embedded Software
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
11//
12// This software is dual-licensed under the terms of the open-source GNU
13// General Public License (GPL) or under the terms of one of the closed-
14// source Quantum Leaps commercial licenses.
15//
16// Redistributions in source code must retain this top-level comment block.
17// Plagiarizing this software to sidestep the license obligations is illegal.
18//
19// NOTE:
20// The GPL does NOT permit the incorporation of this code into proprietary
21// programs. Please contact Quantum Leaps for commercial licensing options,
22// which expressly supersede the GPL and are designed explicitly for
23// closed-source distribution.
24//
25// Quantum Leaps contact information:
26// <www.state-machine.com/licensing>
27// <info@state-machine.com>
28//============================================================================
29#define QP_IMPL // this is QP implementation
30#include "qp_port.hpp" // QP port
31#include "qp_pkg.hpp" // QP package-scope interface
32#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
33#ifdef Q_SPY // QS software tracing enabled?
34 #include "qs_port.hpp" // QS port
35 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
36#else
37 #include "qs_dummy.hpp" // disable the QS software tracing
38#endif // Q_SPY
39
40//============================================================================
41// unnamed namespace for local definitions with internal linkage
42namespace {
43Q_DEFINE_THIS_MODULE("qf_actq")
44} // unnamed namespace
45
46namespace QP {
47
48//............................................................................
50 QEvt const * const e,
51 std::uint_fast16_t const margin,
52 void const * const sender) noexcept
53{
54#ifdef Q_UTEST // test?
55#if (Q_UTEST != 0) // testing QP-stub?
56 if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
57 return static_cast<QActiveDummy *>(this)->fakePost(e, margin, sender);
58 }
59#endif // (Q_UTEST != 0)
60#endif // def Q_UTEST
61
64
65 // the event to post must not be NULL
66 Q_REQUIRE_INCRIT(100, e != nullptr);
67
68 QEQueueCtr const nFree = m_eQueue.m_nFree; // get member into temporary
69
70 bool status = ((margin == QF::NO_MARGIN)
71 || (nFree > static_cast<QEQueueCtr>(margin)));
72 if (status) { // should try to post the event?
73
74 // the queue must have a free slot
75 Q_ASSERT_INCRIT(130, nFree != 0U);
76
77#if (QF_MAX_EPOOL > 0U)
78 if (e->poolNum_ != 0U) { // is it a mutable event?
79 QEvt_refCtr_inc_(e); // increment the reference counter
80 }
81#endif // (QF_MAX_EPOOL > 0U)
82
83 postFIFO_(e, sender);
84
86#ifdef Q_UTEST
87 if (QS_LOC_CHECK_(m_prio)) {
88 QS::onTestPost(sender, this, e, true); // QUTest callback
89 }
90#endif // def Q_UTEST
91 }
92 else { // event cannot be posted, but it is OK
94 QS_TIME_PRE(); // timestamp
95 QS_OBJ_PRE(sender); // the sender object
96 QS_SIG_PRE(e->sig); // the signal of the event
97 QS_OBJ_PRE(this); // this active object (recipient)
98 QS_2U8_PRE(e->poolNum_, e->refCtr_);
99 QS_EQC_PRE(nFree); // # free entries
100 QS_EQC_PRE(margin); // margin requested
101 QS_END_PRE()
102
103 QF_CRIT_EXIT();
104
105#ifdef Q_UTEST
106 if (QS_LOC_CHECK_(m_prio)) {
107 QS::onTestPost(sender, this, e, status); // QUTEst callback
108 }
109#endif // def Q_USTEST
110
111#if (QF_MAX_EPOOL > 0U)
112 QF::gc(e); // recycle the event to avoid a leak
113#endif // (QF_MAX_EPOOL > 0U)
114 }
115
116 return status;
117}
118
119//............................................................................
120void QActive::postLIFO(QEvt const * const e) noexcept {
121#ifdef Q_UTEST // test?
122#if (Q_UTEST != 0) // testing QP-stub?
123 if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
124 static_cast<QActiveDummy *>(this)->QActiveDummy::fakePostLIFO(e);
125 return;
126 }
127#endif // (Q_UTEST != 0)
128#endif // def Q_UTEST
129
132
133 // the event to post must be be valid (which includes not NULL)
134 Q_REQUIRE_INCRIT(200, e != nullptr);
135
136 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
137
138 // the queue must NOT overflow for the LIFO posting policy.
139 Q_REQUIRE_INCRIT(230, nFree != 0U);
140
141 if (e->poolNum_ != 0U) { // is it a mutable event?
142 QEvt_refCtr_inc_(e); // increment the reference counter
143 }
144
145 --nFree; // one free entry just used up
146 m_eQueue.m_nFree = nFree; // update the original
147 if (m_eQueue.m_nMin > nFree) {
148 m_eQueue.m_nMin = nFree; // update minimum so far
149 }
150
152 QS_TIME_PRE(); // timestamp
153 QS_SIG_PRE(e->sig); // the signal of this event
154 QS_OBJ_PRE(this); // this active object
155 QS_2U8_PRE(e->poolNum_, e->refCtr_);
156 QS_EQC_PRE(nFree); // # free entries
157 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
158 QS_END_PRE()
159
160#ifdef Q_UTEST
161 // callback to examine the posted event under the same conditions
162 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
163 // the local filter for this AO ('m_prio') is set
164 if (QS_LOC_CHECK_(m_prio)) {
165 QF_CRIT_EXIT();
166 QS::onTestPost(nullptr, this, e, true);
168 }
169#endif // def Q_UTEST
170
171 QEvt const * const frontEvt = m_eQueue.m_frontEvt;
172 m_eQueue.m_frontEvt = e; // deliver the event directly to the front
173
174 if (frontEvt != nullptr) { // was the queue NOT empty?
175 QEQueueCtr tail = m_eQueue.m_tail; // get member into temporary
176 ++tail;
177 if (tail == m_eQueue.m_end) { // need to wrap the tail?
178 tail = 0U; // wrap around
179 }
180 m_eQueue.m_tail = tail;
181 m_eQueue.m_ring[tail] = frontEvt;
182 }
183 else { // queue was empty
184 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
185 }
186
187 QF_CRIT_EXIT();
188}
189
190//............................................................................
191QEvt const * QActive::get_() noexcept {
194
195 // wait for event to arrive directly (depends on QP port)
196 // NOTE: might use assertion-IDs 400-409
198
199 // always remove event from the front
200 QEvt const * const e = m_eQueue.m_frontEvt;
201
202 // the queue must NOT be empty
203 Q_REQUIRE_INCRIT(310, e != nullptr); // queue must NOT be empty
204
205 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
206
207 ++nFree; // one more free event in the queue
208 m_eQueue.m_nFree = nFree; // update the # free
209
210 if (nFree <= m_eQueue.m_end) { // any events in the ring buffer?
211
212 // remove event from the tail
213 QEQueueCtr tail = m_eQueue.m_tail; // get member into temporary
214 QEvt const * const frontEvt = m_eQueue.m_ring[tail];
215
216 // the event queue must not be empty (frontEvt != NULL)
217 Q_ASSERT_INCRIT(350, frontEvt != nullptr);
218
220 QS_TIME_PRE(); // timestamp
221 QS_SIG_PRE(e->sig); // the signal of this event
222 QS_OBJ_PRE(this); // this active object
224 QS_EQC_PRE(nFree); // # free entries
225 QS_END_PRE()
226
227 m_eQueue.m_frontEvt = frontEvt; // update the original
228
229 if (tail == 0U) { // need to wrap the tail?
230 tail = m_eQueue.m_end;
231 }
232 --tail; // advance the tail (counter-clockwise)
233 m_eQueue.m_tail = tail; // update the original
234 }
235 else {
236 m_eQueue.m_frontEvt = nullptr; // queue becomes empty
237
238 // all entries in the queue must be free (+1 for fronEvt)
239 Q_ASSERT_INCRIT(360, nFree == (m_eQueue.m_end + 1U));
240
242 QS_TIME_PRE(); // timestamp
243 QS_SIG_PRE(e->sig); // the signal of this event
244 QS_OBJ_PRE(this); // this active object
246 QS_END_PRE()
247 }
248
249 QF_CRIT_EXIT();
250
251 return e;
252}
253
254//............................................................................
256 QEvt const * const e,
257 void const * const sender)
258{
259 // NOTE: this helper function is called *inside* critical section
260#ifndef Q_SPY
261 Q_UNUSED_PAR(sender);
262#endif
263
264 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
265
266 --nFree; // one free entry just used up
267 m_eQueue.m_nFree = nFree; // update the original
268 if (m_eQueue.m_nMin > nFree) {
269 m_eQueue.m_nMin = nFree; // update minimum so far
270 }
271
273 QS_TIME_PRE(); // timestamp
274 QS_OBJ_PRE(sender); // the sender object
275 QS_SIG_PRE(e->sig); // the signal of the event
276 QS_OBJ_PRE(this); // this active object (recipient)
278 QS_EQC_PRE(nFree); // # free entries
279 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
280 QS_END_PRE()
281
282 if (m_eQueue.m_frontEvt == nullptr) { // is the queue empty?
283 m_eQueue.m_frontEvt = e; // deliver event directly
284
285#ifdef QXK_HPP_
286 if (m_state.act == nullptr) { // extended thread?
287 QXTHREAD_EQUEUE_SIGNAL_(this); // signal eXtended Thread
288 }
289 else { // basic thread (AO)
290 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
291 }
292#else
293 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
294#endif // def QXK_HPP_
295 }
296 else { // queue was not empty, insert event into the ring-buffer
297 QEQueueCtr head = m_eQueue.m_head; // get member into temporary
298 m_eQueue.m_ring[head] = e; // insert e into buffer
299
300 if (head == 0U) { // need to wrap the head?
301 head = m_eQueue.m_end;
302 }
303 --head; // advance the head (counter-clockwise)
304
305 m_eQueue.m_head = head; // update the original
306 }
307}
308
309//............................................................................
310std::uint16_t QActive::getQueueUse(std::uint_fast8_t const prio) noexcept {
313
314 // prio must be in range. prio==0 is OK (special case)
315 Q_REQUIRE_INCRIT(500, prio <= QF_MAX_ACTIVE);
316
317 std::uint16_t nUse = 0U;
318
319 if (prio > 0U) {
320 QActive const * const a = QActive_registry_[prio];
321 // the AO must be registered (started)
322 Q_REQUIRE_INCRIT(510, a != nullptr);
323
324 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
325 nUse = a->m_eQueue.getUse();
326 }
327 else { // special case of prio==0U: use of all AO event queues
328 for (std::uint_fast8_t p = QF_MAX_ACTIVE; p > 0U; --p) {
329 QActive const * const a = QActive_registry_[p];
330 if (a != nullptr) { // is the AO registered?
331 // NOTE: QEQueue::getUse() does NOT apply crit.sect. internally
332 nUse += a->m_eQueue.getUse();
333 }
334 }
335 }
336
337 QF_CRIT_EXIT();
338
339 return nUse;
340}
341
342//............................................................................
343std::uint16_t QActive::getQueueFree(std::uint_fast8_t const prio) noexcept {
346
347 Q_REQUIRE_INCRIT(600, (0U < prio) && (prio <= QF_MAX_ACTIVE));
348
349 QActive const * const a = QActive_registry_[prio];
350 // the AO must be registered (started)
351 Q_REQUIRE_INCRIT(610, a != nullptr);
352
353 // NOTE: critical section prevents asynchronous change of the free count
354 std::uint16_t const nFree =
355 static_cast<std::uint16_t>(a->m_eQueue.m_nFree);
356 QF_CRIT_EXIT();
357
358 return nFree;
359}
360
361//............................................................................
362std::uint16_t QActive::getQueueMin(std::uint_fast8_t const prio) noexcept {
365
366 // the queried prio. must be in range (excluding the idle thread)
367 Q_REQUIRE_INCRIT(700, (0U < prio) && (prio <= QF_MAX_ACTIVE));
368
369 QActive const * const a = QActive_registry_[prio];
370 // the AO must be registered (started)
371 Q_REQUIRE_INCRIT(710, a != nullptr);
372
373 // NOTE: critical section prevents asynchronous change of the min count
374 std::uint16_t const nMin = static_cast<std::uint16_t>(a->m_eQueue.m_nMin);
375
376 QF_CRIT_EXIT();
377
378 return nMin;
379}
380
381//============================================================================
382#if (QF_MAX_TICK_RATE > 0U)
383
384//............................................................................
385QTicker::QTicker(std::uint8_t const tickRate) noexcept
386 : QActive(nullptr)
387{
388 // reuse m_head for tick-rate
389 m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate);
390}
391
392//............................................................................
394 void const * const e,
395 std::uint_fast8_t const qsId)
396{
397 Q_UNUSED_PAR(e);
398 Q_UNUSED_PAR(qsId);
399
402
403 // instead of the top-most initial transition, QTicker initializes
404 // the super.eQueue member inherited from QActive and reused
405 // to count the number of tick events posted to this QTicker.
406 // see also: QTicker_trig_()
407 m_eQueue.m_tail = 0U;
408 QF_CRIT_EXIT();
409}
410
411//............................................................................
413 QEvt const * const e,
414 std::uint_fast8_t const qsId)
415{
416 Q_UNUSED_PAR(e);
417 Q_UNUSED_PAR(qsId);
418
421
422 // get members into temporaries
423 QEQueueCtr nTicks = m_eQueue.m_tail;
424 QEQueueCtr const tickRate = m_eQueue.m_head;
425
426 // QTicker_dispatch_() shall be called only when it has tick events
427 Q_REQUIRE_INCRIT(800, nTicks > 0U);
428
429 m_eQueue.m_tail = 0U; // clear # ticks
430
431 QF_CRIT_EXIT();
432
433 // instead of dispatching the event, QTicker calls QTimeEvt_tick_()
434 // processing for the number of times indicated in eQueue.tail.
435 for (; nTicks > 0U; --nTicks) {
436 QTimeEvt::tick(static_cast<std::uint_fast8_t>(tickRate), this);
437 }
438}
439
440//............................................................................
441void QTicker::trig_(void const * const sender) noexcept {
442#ifndef Q_SPY
443 Q_UNUSED_PAR(sender);
444#endif
445
446 // static immutable (const) event to post to the QTicker AO
447 static constexpr QEvt tickEvt(0U);
448
451
452 QEQueueCtr nTicks = m_eQueue.m_tail; // get member into temporary
453
454 if (nTicks == 0U) { // no ticks accumulated yet?
455 // when no ticks accumuilated, m_eQueue.m_fronEvt must be NULL
456 Q_REQUIRE_INCRIT(930, m_eQueue.m_frontEvt == nullptr);
457
458 m_eQueue.m_frontEvt = &tickEvt; // deliver event directly
459 m_eQueue.m_nFree = 0U;
460
461 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
462 }
463 else { // some tick have accumulated (and were not processed yet)
464 // when some ticks accumuilated, eQueue.fronEvt must be &tickEvt
465 Q_REQUIRE_INCRIT(940,m_eQueue.m_frontEvt == &tickEvt);
466
467 // the nTicks counter must accept one more count without overflowing
468 Q_REQUIRE_INCRIT(950, nTicks < 0xFFU);
469 }
470
471 ++nTicks; // account for one more tick event
472 m_eQueue.m_tail = nTicks; // update the original
473
475 QS_TIME_PRE(); // timestamp
476 QS_OBJ_PRE(sender); // the sender object
477 QS_SIG_PRE(0U); // the signal of the event
478 QS_OBJ_PRE(this); // this active object
479 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
480 QS_EQC_PRE(0U); // # free entries
481 QS_EQC_PRE(0U); // min # free entries
482 QS_END_PRE()
483
484 QF_CRIT_EXIT();
485}
486
487#endif // (QF_MAX_TICK_RATE > 0U)
488
489} // namespace QP
void fakePostLIFO(QEvt const *const e) noexcept
Definition qutest.cpp:548
friend class QActiveDummy
Definition qp.hpp:629
static std::uint16_t getQueueMin(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:362
friend class QTicker
Definition qp.hpp:624
void postFIFO_(QEvt const *const e, void const *const sender)
Definition qf_actq.cpp:255
friend void QF::init()
bool postx_(QEvt const *const e, std::uint_fast16_t const margin, void const *const sender) noexcept
Posts an event e directly to the event queue of the active object using the First-In-First-Out (FIFO)...
Definition qf_actq.cpp:49
QActive(QStateHandler const initial) noexcept
QActive constructor (abstract base class).
Definition qf_qact.cpp:55
QACTIVE_EQUEUE_TYPE m_eQueue
Port-dependent event-queue type (often QP::QEQueue).
Definition qp.hpp:506
QEvt const * get_() noexcept
Get an event from the event queue of an active object.
Definition qf_actq.cpp:191
void postLIFO(QEvt const *const e) noexcept
Posts an event e directly to the event queue of the active object using the Last-In-First-Out (LIFO) ...
Definition qf_actq.cpp:120
static std::uint16_t getQueueUse(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:310
static std::uint16_t getQueueFree(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:343
std::uint8_t m_prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.hpp:494
QAsmAttr m_temp
Temporary storage for target/act-table etc.
Definition qp.hpp:170
QAsmAttr m_state
Current state (pointer to the current state-handler function).
Definition qp.hpp:169
Event class.
Definition qp.hpp:101
std::uint32_t poolNum_
Event pool number of this event.
Definition qp.hpp:104
std::uint32_t refCtr_
Event reference counter.
Definition qp.hpp:105
std::uint32_t sig
Signal of the event (see Event Signal).
Definition qp.hpp:103
static void onTestPost(void const *sender, QActive *recipient, QEvt const *e, bool status)
void trig_(void const *const sender) noexcept
Asynchronously trigger the QTicker active object to perform tick processing.
Definition qf_actq.cpp:441
void dispatch(QEvt const *const e, std::uint_fast8_t const qsId) override
Virtual function to dispatch an event to the state machine.
Definition qf_actq.cpp:412
static void tick(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Processes all armed time events at every clock tick.
Definition qf_time.cpp:276
void gc(QEvt const *const e) noexcept
Recycle a mutable (mutable) event.
Definition qf_dyn.cpp:276
constexpr std::uint_fast16_t NO_MARGIN
Special value of margin that causes asserting failure in case event allocation or event posting fails...
Definition qp.hpp:488
QP/C++ Framework namespace.
Definition qequeue.hpp:36
@ QS_QF_ACTIVE_GET_LAST
AO got an event and its queue is empty.
Definition qs.hpp:75
@ QS_QF_ACTIVE_POST_ATTEMPT
attempt to post an evt to AO failed
Definition qs.hpp:117
@ QS_QF_ACTIVE_POST_LIFO
an event was posted (LIFO) directly to AO
Definition qs.hpp:73
@ QS_QF_ACTIVE_GET
AO got an event and its queue is not empty.
Definition qs.hpp:74
@ QS_QF_ACTIVE_POST
an event was posted (FIFO) directly to AO
Definition qs.hpp:72
void QEvt_refCtr_inc_(QEvt const *const me) noexcept
Internal function to increment the refCtr of a const event.
Definition qf_act.cpp:57
std::uint16_t QEQueueCtr
The data type to store the ring-buffer counters.
Definition qequeue.hpp:41
std::array< QActive *, QF_MAX_ACTIVE+1U > QActive_registry_
Internal array of pointers to the registered Active Objects.
Definition qf_qact.cpp:47
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.hpp:89
#define Q_STATE_CAST(handler_)
Perform cast to QP::QStateHandler.
Definition qp.hpp:389
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
QP/C++ Framework in C++ internal (package-scope) interface.
Sample QP/C++ port.
#define QXTHREAD_EQUEUE_SIGNAL_(me_)
Port-specific method to signal eXtended thread event queue (for internal use only).
Definition qp_port.hpp:347
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Port-specific method to signal Active Object event queue (for internal use only).
Definition qp_port.hpp:330
#define QACTIVE_EQUEUE_WAIT_(me_)
Port-specific method to wait on an empty Active Object event queue (for internal use only).
Definition qp_port.hpp:321
#define QS_TIME_PRE()
Definition qs.hpp:362
#define QS_LOC_CHECK_(qsId_)
Definition qs.hpp:303
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_OBJ_PRE(obj_)
Output pre-formatted object pointer element.
Definition qs_pkg.hpp:91
#define QS_EQC_PRE(ctr_)
Output pre-formatted event queue counter data element.
Definition qs_pkg.hpp:114
#define QS_SIG_PRE(sig_)
Output pre-formatted event signal data element.
Definition qs_pkg.hpp:92
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.hpp:83
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.hpp:79
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.hpp:73
Sample QS/C++ port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section).
Definition qsafe.h:54
#define QF_CRIT_EXIT()
Definition qsafe.h:44
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section).
Definition qsafe.h:98
#define QF_CRIT_STAT
Definition qsafe.h:36