QP/C++ 8.1.1
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 = (nFree > 0U);
71 if (margin == QF::NO_MARGIN) { // no margin requested?
72 // queue must not overflow
73 Q_ASSERT_INCRIT(130, status);
74 }
75 else {
76 status = (nFree > static_cast<QEQueueCtr>(margin));
77 }
78
79#if (QF_MAX_EPOOL > 0U)
80 if (e->poolNum_ != 0U) { // is it a mutable event?
81 QEvt_refCtr_inc_(e); // increment the reference counter
82 }
83#endif // (QF_MAX_EPOOL > 0U)
84
85 if (status) { // can post the event?
86 postFIFO_(e, sender);
87#ifdef Q_UTEST
88 if (QS_LOC_CHECK_(m_prio)) {
90 QS::onTestPost(sender, this, e, true); // QUTest callback
92 }
93#endif // def Q_UTEST
95 }
96 else { // event cannot be posted
98 QS_TIME_PRE(); // timestamp
99 QS_OBJ_PRE(sender); // the sender object
100 QS_SIG_PRE(e->sig); // the signal of the event
101 QS_OBJ_PRE(this); // this active object (recipient)
102 QS_2U8_PRE(e->poolNum_, e->refCtr_);
103 QS_EQC_PRE(nFree); // # free entries
104 QS_EQC_PRE(margin); // margin requested
105 QS_END_PRE()
106
107#ifdef Q_UTEST
108 if (QS_LOC_CHECK_(m_prio)) {
109 QF_CRIT_EXIT();
110 QS::onTestPost(sender, this, e, status);
112 }
113#endif // def Q_USTEST
114
115 QF_CRIT_EXIT();
116
117#if (QF_MAX_EPOOL > 0U)
118 QF::gc(e); // recycle the event to avoid a leak
119#endif // (QF_MAX_EPOOL > 0U)
120 }
121
122 return status;
123}
124
125//............................................................................
126void QActive::postLIFO(QEvt const * const e) noexcept {
127#ifdef Q_UTEST // test?
128#if (Q_UTEST != 0) // testing QP-stub?
129 if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
130 static_cast<QActiveDummy *>(this)->QActiveDummy::fakePostLIFO(e);
131 return;
132 }
133#endif // (Q_UTEST != 0)
134#endif // def Q_UTEST
135
138
139 // the event to post must be be valid (which includes not NULL)
140 Q_REQUIRE_INCRIT(200, e != nullptr);
141
142 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
143
144 // the queue must NOT overflow for the LIFO posting policy.
145 Q_REQUIRE_INCRIT(230, nFree != 0U);
146
147 if (e->poolNum_ != 0U) { // is it a mutable event?
148 QEvt_refCtr_inc_(e); // increment the reference counter
149 }
150
151 --nFree; // one free entry just used up
152 m_eQueue.m_nFree = nFree; // update the original
153 if (m_eQueue.m_nMin > nFree) {
154 m_eQueue.m_nMin = nFree; // update minimum so far
155 }
156
158 QS_TIME_PRE(); // timestamp
159 QS_SIG_PRE(e->sig); // the signal of this event
160 QS_OBJ_PRE(this); // this active object
161 QS_2U8_PRE(e->poolNum_, e->refCtr_);
162 QS_EQC_PRE(nFree); // # free entries
163 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
164 QS_END_PRE()
165
166#ifdef Q_UTEST
167 // callback to examine the posted event under the same conditions
168 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
169 // the local filter for this AO ('m_prio') is set
170 if (QS_LOC_CHECK_(m_prio)) {
171 QF_CRIT_EXIT();
172 QS::onTestPost(nullptr, this, e, true);
174 }
175#endif // def Q_UTEST
176
177 QEvt const * const frontEvt = m_eQueue.m_frontEvt;
178 m_eQueue.m_frontEvt = e; // deliver the event directly to the front
179
180 if (frontEvt != nullptr) { // was the queue NOT empty?
181 QEQueueCtr tail = m_eQueue.m_tail; // get member into temporary
182 ++tail;
183 if (tail == m_eQueue.m_end) { // need to wrap the tail?
184 tail = 0U; // wrap around
185 }
186 m_eQueue.m_tail = tail;
187 m_eQueue.m_ring[tail] = frontEvt;
188 }
189 else { // queue was empty
190 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
191 }
192
193 QF_CRIT_EXIT();
194}
195
196//............................................................................
197QEvt const * QActive::get_() noexcept {
200
201 // wait for event to arrive directly (depends on QP port)
202 // NOTE: might use assertion-IDs 400-409
204
205 // always remove event from the front
206 QEvt const * const e = m_eQueue.m_frontEvt;
207
208 // the queue must NOT be empty
209 Q_REQUIRE_INCRIT(310, e != nullptr); // queue must NOT be empty
210
211 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
212
213 ++nFree; // one more free event in the queue
214 m_eQueue.m_nFree = nFree; // update the # free
215
216 if (nFree <= m_eQueue.m_end) { // any events in the ring buffer?
217
218 // remove event from the tail
219 QEQueueCtr tail = m_eQueue.m_tail; // get member into temporary
220 QEvt const * const frontEvt = m_eQueue.m_ring[tail];
221
222 // the event queue must not be empty (frontEvt != NULL)
223 Q_ASSERT_INCRIT(350, frontEvt != nullptr);
224
226 QS_TIME_PRE(); // timestamp
227 QS_SIG_PRE(e->sig); // the signal of this event
228 QS_OBJ_PRE(this); // this active object
230 QS_EQC_PRE(nFree); // # free entries
231 QS_END_PRE()
232
233 m_eQueue.m_frontEvt = frontEvt; // update the original
234
235 if (tail == 0U) { // need to wrap the tail?
236 tail = m_eQueue.m_end;
237 }
238 --tail; // advance the tail (counter-clockwise)
239 m_eQueue.m_tail = tail; // update the original
240 }
241 else {
242 m_eQueue.m_frontEvt = nullptr; // queue becomes empty
243
244 // all entries in the queue must be free (+1 for fronEvt)
245 Q_ASSERT_INCRIT(360, nFree == (m_eQueue.m_end + 1U));
246
248 QS_TIME_PRE(); // timestamp
249 QS_SIG_PRE(e->sig); // the signal of this event
250 QS_OBJ_PRE(this); // this active object
252 QS_END_PRE()
253 }
254
255 QF_CRIT_EXIT();
256
257 return e;
258}
259
260//............................................................................
262 QEvt const * const e,
263 void const * const sender)
264{
265 // NOTE: this helper function is called *inside* critical section
266#ifndef Q_SPY
267 Q_UNUSED_PAR(sender);
268#endif
269
270 QEQueueCtr nFree = m_eQueue.m_nFree; // get member into temporary
271
272 --nFree; // one free entry just used up
273 m_eQueue.m_nFree = nFree; // update the original
274 if (m_eQueue.m_nMin > nFree) {
275 m_eQueue.m_nMin = nFree; // update minimum so far
276 }
277
279 QS_TIME_PRE(); // timestamp
280 QS_OBJ_PRE(sender); // the sender object
281 QS_SIG_PRE(e->sig); // the signal of the event
282 QS_OBJ_PRE(this); // this active object (recipient)
284 QS_EQC_PRE(nFree); // # free entries
285 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
286 QS_END_PRE()
287
288 if (m_eQueue.m_frontEvt == nullptr) { // is the queue empty?
289 m_eQueue.m_frontEvt = e; // deliver event directly
290
291#ifdef QXK_HPP_
292 if (m_state.act == nullptr) { // extended thread?
293 QXTHREAD_EQUEUE_SIGNAL_(this); // signal eXtended Thread
294 }
295 else { // basic thread (AO)
296 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
297 }
298#else
299 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
300#endif // def QXK_HPP_
301 }
302 else { // queue was not empty, insert event into the ring-buffer
303 QEQueueCtr head = m_eQueue.m_head; // get member into temporary
304 m_eQueue.m_ring[head] = e; // insert e into buffer
305
306 if (head == 0U) { // need to wrap the head?
307 head = m_eQueue.m_end;
308 }
309 --head; // advance the head (counter-clockwise)
310
311 m_eQueue.m_head = head; // update the original
312 }
313}
314
315//............................................................................
316std::uint16_t QActive::getQueueUse(std::uint_fast8_t const prio) noexcept {
319
320 // prio must be in range. prio==0 is OK (special case)
321 Q_REQUIRE_INCRIT(500, prio <= QF_MAX_ACTIVE);
322
323 std::uint16_t nUse = 0U;
324
325 if (prio > 0U) {
326 QActive const * const a = QActive_registry_[prio];
327 // the AO must be registered (started)
328 Q_REQUIRE_INCRIT(510, a != nullptr);
329
330 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
331 nUse = a->m_eQueue.getUse();
332 }
333 else { // special case of prio==0U: use of all AO event queues
334 for (std::uint_fast8_t p = QF_MAX_ACTIVE; p > 0U; --p) {
335 QActive const * const a = QActive_registry_[p];
336 if (a != nullptr) { // is the AO registered?
337 // NOTE: QEQueue::getUse() does NOT apply crit.sect. internally
338 nUse += a->m_eQueue.getUse();
339 }
340 }
341 }
342
343 QF_CRIT_EXIT();
344
345 return nUse;
346}
347
348//............................................................................
349std::uint16_t QActive::getQueueFree(std::uint_fast8_t const prio) noexcept {
352
353 Q_REQUIRE_INCRIT(600, (0U < prio) && (prio <= QF_MAX_ACTIVE));
354
355 QActive const * const a = QActive_registry_[prio];
356 // the AO must be registered (started)
357 Q_REQUIRE_INCRIT(610, a != nullptr);
358
359 // NOTE: critical section prevents asynchronous change of the free count
360 std::uint16_t const nFree =
361 static_cast<std::uint16_t>(a->m_eQueue.m_nFree);
362 QF_CRIT_EXIT();
363
364 return nFree;
365}
366
367//............................................................................
368std::uint16_t QActive::getQueueMin(std::uint_fast8_t const prio) noexcept {
371
372 // the queried prio. must be in range (excluding the idle thread)
373 Q_REQUIRE_INCRIT(700, (0U < prio) && (prio <= QF_MAX_ACTIVE));
374
375 QActive const * const a = QActive_registry_[prio];
376 // the AO must be registered (started)
377 Q_REQUIRE_INCRIT(710, a != nullptr);
378
379 // NOTE: critical section prevents asynchronous change of the min count
380 std::uint16_t const nMin = static_cast<std::uint16_t>(a->m_eQueue.m_nMin);
381
382 QF_CRIT_EXIT();
383
384 return nMin;
385}
386
387//============================================================================
388#if (QF_MAX_TICK_RATE > 0U)
389
390//............................................................................
391QTicker::QTicker(std::uint8_t const tickRate) noexcept
392 : QActive(nullptr)
393{
394 // reuse m_head for tick-rate
395 m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate);
396}
397
398//............................................................................
400 void const * const e,
401 std::uint_fast8_t const qsId)
402{
403 Q_UNUSED_PAR(e);
404 Q_UNUSED_PAR(qsId);
405
408
409 // instead of the top-most initial transition, QTicker initializes
410 // the super.eQueue member inherited from QActive and reused
411 // to count the number of tick events posted to this QTicker.
412 // see also: QTicker_trig_()
413 m_eQueue.m_tail = 0U;
414 QF_CRIT_EXIT();
415}
416
417//............................................................................
419 QEvt const * const e,
420 std::uint_fast8_t const qsId)
421{
422 Q_UNUSED_PAR(e);
423 Q_UNUSED_PAR(qsId);
424
427
428 // get members into temporaries
429 QEQueueCtr nTicks = m_eQueue.m_tail;
430 QEQueueCtr const tickRate = m_eQueue.m_head;
431
432 // QTicker_dispatch_() shall be called only when it has tick events
433 Q_REQUIRE_INCRIT(800, nTicks > 0U);
434
435 m_eQueue.m_tail = 0U; // clear # ticks
436
437 QF_CRIT_EXIT();
438
439 // instead of dispatching the event, QTicker calls QTimeEvt_tick_()
440 // processing for the number of times indicated in eQueue.tail.
441 for (; nTicks > 0U; --nTicks) {
442 QTimeEvt::tick(static_cast<std::uint_fast8_t>(tickRate), this);
443 }
444}
445
446//............................................................................
447void QTicker::trig_(void const * const sender) noexcept {
448#ifndef Q_SPY
449 Q_UNUSED_PAR(sender);
450#endif
451
452 // static immutable (const) event to post to the QTicker AO
453 static constexpr QEvt tickEvt(0U);
454
457
458 QEQueueCtr nTicks = m_eQueue.m_tail; // get member into temporary
459
460 if (nTicks == 0U) { // no ticks accumulated yet?
461 // when no ticks accumuilated, m_eQueue.m_fronEvt must be NULL
462 Q_REQUIRE_INCRIT(930, m_eQueue.m_frontEvt == nullptr);
463
464 m_eQueue.m_frontEvt = &tickEvt; // deliver event directly
465 m_eQueue.m_nFree = 0U;
466
467 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
468 }
469 else { // some tick have accumulated (and were not processed yet)
470 // when some ticks accumuilated, eQueue.fronEvt must be &tickEvt
471 Q_REQUIRE_INCRIT(940,m_eQueue.m_frontEvt == &tickEvt);
472
473 // the nTicks counter must accept one more count without overflowing
474 Q_REQUIRE_INCRIT(950, nTicks < 0xFFU);
475 }
476
477 ++nTicks; // account for one more tick event
478 m_eQueue.m_tail = nTicks; // update the original
479
481 QS_TIME_PRE(); // timestamp
482 QS_OBJ_PRE(sender); // the sender object
483 QS_SIG_PRE(0U); // the signal of the event
484 QS_OBJ_PRE(this); // this active object
485 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
486 QS_EQC_PRE(0U); // # free entries
487 QS_EQC_PRE(0U); // min # free entries
488 QS_END_PRE()
489
490 QF_CRIT_EXIT();
491}
492
493#endif // (QF_MAX_TICK_RATE > 0U)
494
495} // namespace QP
void fakePostLIFO(QEvt const *const e) noexcept
Definition qutest.cpp:549
friend class QActiveDummy
Definition qp.hpp:625
static std::uint16_t getQueueMin(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:368
friend class QTicker
Definition qp.hpp:620
void postFIFO_(QEvt const *const e, void const *const sender)
Definition qf_actq.cpp:261
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:502
QEvt const * get_() noexcept
Get an event from the event queue of an active object.
Definition qf_actq.cpp:197
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:126
static std::uint16_t getQueueUse(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:316
static std::uint16_t getQueueFree(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:349
std::uint8_t m_prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.hpp:490
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:447
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:418
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:484
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 clearly mark unused parameters of functions.
Definition qp.hpp:89
#define Q_STATE_CAST(handler_)
Perform cast to QP::QStateHandler.
Definition qp.hpp:385
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
Definition qp_config.hpp:98
QP Framework in C++ internal (package-scope) interface
Sample QP/C++ port.
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Definition qp_port.hpp:176
#define QACTIVE_EQUEUE_WAIT_(me_)
Definition qp_port.hpp:175
#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:89
#define QS_EQC_PRE(ctr_)
Output pre-formatted event queue counter data element.
Definition qs_pkg.hpp:112
#define QS_SIG_PRE(sig_)
Output pre-formatted event signal data element.
Definition qs_pkg.hpp:90
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.hpp:81
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.hpp:77
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.hpp:71
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