QP/C++  8.0.3
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#ifndef Q_SPY
55 Q_UNUSED_PAR(sender);
56#endif
57
58#ifdef Q_UTEST // test?
59#if (Q_UTEST != 0) // testing QP-stub?
60 if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
61 return static_cast<QActiveDummy *>(this)->fakePost(e, margin, sender);
62 }
63#endif // (Q_UTEST != 0)
64#endif // def Q_UTEST
65
68
69 Q_REQUIRE_INCRIT(200, e != nullptr);
70
71 QEQueueCtr const nFree = m_eQueue.m_nFree; // get volatile into temporary
72
73 // required margin available?
74 bool status = false; // assume that event cannot be posted
75 if (margin == QF::NO_MARGIN) {
76 if (nFree > 0U) { // free entries available in the queue?
77 status = true; // can post
78 }
79 else { // no free entries available
80 // The queue overflows, but QF_NO_MARGIN indicates that
81 // the "event delivery guarantee" is required.
82 Q_ERROR_INCRIT(210); // must be able to post the event
83 }
84 }
85 else if (nFree > static_cast<QEQueueCtr>(margin)) { // enough free?
86 status = true; // can post
87 }
88 else {
89 // empty
90 }
91
92#if (QF_MAX_EPOOL > 0U)
93 if (e->poolNum_ != 0U) { // is it a mutable event?
94 Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE));
95 QEvt_refCtr_inc_(e); // increment the reference counter
96 }
97#endif // (QF_MAX_EPOOL > 0U)
98
99 if (status) { // can post the event?
100 postFIFO_(e, sender);
101 QF_CRIT_EXIT();
102 }
103 else { // event cannot be posted
104 QS_BEGIN_PRE(QS_QF_ACTIVE_POST_ATTEMPT, m_prio)
105 QS_TIME_PRE(); // timestamp
106 QS_OBJ_PRE(sender); // the sender object
107 QS_SIG_PRE(e->sig); // the signal of the event
108 QS_OBJ_PRE(this); // this active object (recipient)
109 QS_2U8_PRE(e->poolNum_, e->refCtr_);
110 QS_EQC_PRE(nFree); // # free entries
111 QS_EQC_PRE(margin); // margin requested
112 QS_END_PRE()
113
114#ifdef Q_UTEST
115 if (QS_LOC_CHECK_(m_prio)) {
116 QF_CRIT_EXIT();
117 QS::onTestPost(sender, this, e, status);
119 }
120#endif // def Q_USTEST
121
122 QF_CRIT_EXIT();
123
124#if (QF_MAX_EPOOL > 0U)
125 QF::gc(e); // recycle the event to avoid a leak
126#endif // (QF_MAX_EPOOL > 0U)
127 }
128
129 return status;
130}
131
132//............................................................................
133void QActive::postLIFO(QEvt const * const e) noexcept {
134#ifdef Q_UTEST // test?
135#if (Q_UTEST != 0) // testing QP-stub?
136 if (m_temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
137 static_cast<QActiveDummy *>(this)->QActiveDummy::fakePostLIFO(e);
138 return;
139 }
140#endif // (Q_UTEST != 0)
141#endif // def Q_UTEST
142
145
146 // the posted event must be be valid (which includes not NULL)
147 Q_REQUIRE_INCRIT(300, e != nullptr);
148
149 QEQueueCtr tmp = m_eQueue.m_nFree; // get volatile into temporary
150
151 // The queue must NOT overflow for the LIFO posting policy.
152 Q_REQUIRE_INCRIT(310, tmp != 0U);
153
154 if (e->poolNum_ != 0U) { // is it a mutable event?
155 Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE));
156 QEvt_refCtr_inc_(e); // increment the reference counter
157 }
158
159 --tmp; // one free entry just used up
160
161 m_eQueue.m_nFree = tmp; // update the original
162 if (m_eQueue.m_nMin > tmp) {
163 m_eQueue.m_nMin = tmp; // update minimum so far
164 }
165
166 QS_BEGIN_PRE(QS_QF_ACTIVE_POST_LIFO, m_prio)
167 QS_TIME_PRE(); // timestamp
168 QS_SIG_PRE(e->sig); // the signal of this event
169 QS_OBJ_PRE(this); // this active object
170 QS_2U8_PRE(e->poolNum_, e->refCtr_);
171 QS_EQC_PRE(tmp); // # free entries
172 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
173 QS_END_PRE()
174
175#ifdef Q_UTEST
176 // callback to examine the posted event under the same conditions
177 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
178 // the local filter for this AO ('m_prio') is set
179 if (QS_LOC_CHECK_(m_prio)) {
180 QF_CRIT_EXIT();
181
182 QS::onTestPost(nullptr, this, e, true);
183
185 }
186#endif // def Q_UTEST
187
188 QEvt const * const frontEvt = m_eQueue.m_frontEvt;
189 m_eQueue.m_frontEvt = e; // deliver the event directly to the front
190
191 if (frontEvt != nullptr) { // was the queue NOT empty?
192 tmp = m_eQueue.m_tail; // get volatile into temporary;
193 ++tmp;
194 if (tmp == m_eQueue.m_end) { // need to wrap the tail?
195 tmp = 0U; // wrap around
196 }
197 m_eQueue.m_tail = tmp;
198 m_eQueue.m_ring[tmp] = frontEvt;
199 }
200 else { // queue was empty
201 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
202 }
203
204 QF_CRIT_EXIT();
205}
206
207//............................................................................
208QEvt const * QActive::get_() noexcept {
211
212 // wait for event to arrive directly (depends on QP port)
213 // NOTE: might use assertion-IDs 400-409
214 QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly
215
216 // always remove event from the front
217 QEvt const * const e = m_eQueue.m_frontEvt;
218 QEQueueCtr tmp = m_eQueue.m_nFree; // get volatile into temporary
219
220 Q_REQUIRE_INCRIT(410, e != nullptr); // queue must NOT be empty
221
222 ++tmp; // one more free event in the queue
223
224 m_eQueue.m_nFree = tmp; // update the # free
225
226 if (tmp <= m_eQueue.m_end) { // any events in the ring buffer?
227
228 QS_BEGIN_PRE(QS_QF_ACTIVE_GET, m_prio)
229 QS_TIME_PRE(); // timestamp
230 QS_SIG_PRE(e->sig); // the signal of this event
231 QS_OBJ_PRE(this); // this active object
233 QS_EQC_PRE(tmp); // # free entries
234 QS_END_PRE()
235
236 // remove event from the tail
237 tmp = m_eQueue.m_tail; // get volatile into temporary
238 QEvt const * const frontEvt = m_eQueue.m_ring[tmp];
239
240 Q_ASSERT_INCRIT(430, frontEvt != nullptr);
241 m_eQueue.m_frontEvt = frontEvt; // update the original
242
243 if (tmp == 0U) { // need to wrap the tail?
244 tmp = m_eQueue.m_end;
245 }
246 --tmp; // advance the tail (counter-clockwise)
247
248 m_eQueue.m_tail = tmp; // update the original
249 }
250 else {
251 m_eQueue.m_frontEvt = nullptr; // the queue becomes empty
252
253 // all entries in the queue must be free (+1 for fronEvt)
254 Q_ASSERT_INCRIT(440, tmp == (m_eQueue.m_end + 1U));
255
256 QS_BEGIN_PRE(QS_QF_ACTIVE_GET_LAST, m_prio)
257 QS_TIME_PRE(); // timestamp
258 QS_SIG_PRE(e->sig); // the signal of this event
259 QS_OBJ_PRE(this); // this active object
261 QS_END_PRE()
262 }
263
264 QF_CRIT_EXIT();
265
266 return e;
267}
268
269//............................................................................
271 QEvt const * const e,
272 void const * const sender)
273{
274 // NOTE: this helper function is called *inside* critical section
275#ifndef Q_SPY
276 Q_UNUSED_PAR(sender);
277#endif
278
279 QEQueueCtr tmp = m_eQueue.m_nFree; // get volatile into temporary
280 --tmp; // one free entry just used up
281
282 m_eQueue.m_nFree = tmp; // update the original
283 if (m_eQueue.m_nMin > tmp) {
284 m_eQueue.m_nMin = tmp; // update minimum so far
285 }
286
287 QS_BEGIN_PRE(QS_QF_ACTIVE_POST, m_prio)
288 QS_TIME_PRE(); // timestamp
289 QS_OBJ_PRE(sender); // the sender object
290 QS_SIG_PRE(e->sig); // the signal of the event
291 QS_OBJ_PRE(this); // this active object (recipient)
293 QS_EQC_PRE(tmp); // # free entries
294 QS_EQC_PRE(m_eQueue.m_nMin); // min # free entries
295 QS_END_PRE()
296
297#ifdef Q_UTEST
298 if (QS_LOC_CHECK_(m_prio)) {
299 QF_CRIT_EXIT();
300 QS::onTestPost(sender, this, e, true); // QUTEst callback
302 }
303#endif // def Q_UTEST
304
305 if (m_eQueue.m_frontEvt == nullptr) { // is the queue empty?
306 m_eQueue.m_frontEvt = e; // deliver event directly
307
308#ifdef QXK_HPP_
309 if (m_state.act == nullptr) { // eXtended thread?
310 QXTHREAD_EQUEUE_SIGNAL_(this); // signal eXtended Thread
311 }
312 else {
313 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
314 }
315#else
316 QACTIVE_EQUEUE_SIGNAL_(this); // signal the Active Object
317#endif // def QXK_HPP_
318 }
319 else { // queue was not empty, insert event into the ring-buffer
320 tmp = m_eQueue.m_head; // get volatile into temporary
321 m_eQueue.m_ring[tmp] = e; // insert e into buffer
322
323 if (tmp == 0U) { // need to wrap the head?
324 tmp = m_eQueue.m_end;
325 }
326 --tmp; // advance the head (counter-clockwise)
327
328 m_eQueue.m_head = tmp; // update the original
329 }
330}
331
332//............................................................................
333std::uint_fast16_t QActive::getQueueMin(std::uint_fast8_t const prio) noexcept {
336 Q_REQUIRE_INCRIT(600, (prio <= QF_MAX_ACTIVE)
337 && (registry_[prio] != nullptr));
338 std::uint_fast16_t const min =
339 static_cast<std::uint_fast16_t>(registry_[prio]->m_eQueue.m_nMin);
340 QF_CRIT_EXIT();
341
342 return min;
343}
344
345//============================================================================
346
347QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept
348: QActive(nullptr)
349{
350 // reuse m_head for tick-rate
351 m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate);
352}
353
354//............................................................................
356 void const * const e,
357 std::uint_fast8_t const qsId)
358{
359 Q_UNUSED_PAR(e);
360 Q_UNUSED_PAR(qsId);
361
364 m_eQueue.m_tail = 0U;
365 QF_CRIT_EXIT();
366}
367
368//............................................................................
370 QEvt const * const e,
371 std::uint_fast8_t const qsId)
372{
373 Q_UNUSED_PAR(e);
374 Q_UNUSED_PAR(qsId);
375
378
379 // get volatile into temporaries
380 QEQueueCtr nTicks = m_eQueue.m_tail;
381 QEQueueCtr const tickRate = m_eQueue.m_head;
382
383 Q_REQUIRE_INCRIT(800, nTicks > 0U);
384 m_eQueue.m_tail = 0U; // clear # ticks
385 QF_CRIT_EXIT();
386
387 for (; nTicks > 0U; --nTicks) {
388 QTimeEvt::tick(static_cast<std::uint_fast8_t>(tickRate),
389 this);
390 }
391}
392
393//............................................................................
394void QTicker::trig_(void const * const sender) noexcept {
395#ifndef Q_SPY
396 Q_UNUSED_PAR(sender);
397#endif
398
399 static QEvt const tickEvt(0U); // immutable event
400
403
404 QEQueueCtr nTicks = m_eQueue.m_tail; // get volatile into temporary
405
406 if (m_eQueue.m_frontEvt == nullptr) {
408 (m_eQueue.m_nFree == 1U)
409 && (nTicks == 0U));
410
411 m_eQueue.m_frontEvt = &tickEvt; // deliver event directly
412 m_eQueue.m_nFree = 0U;
413
414 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
415 }
416 else {
417 Q_REQUIRE_INCRIT(910, (nTicks > 0U) && (nTicks < 0xFFU));
418 Q_REQUIRE_INCRIT(920, m_eQueue.m_nFree == 0U);
419 }
420
421 ++nTicks; // account for one more tick event
422
423 m_eQueue.m_tail = nTicks; // update the original
424
425 QS_BEGIN_PRE(QS_QF_ACTIVE_POST, m_prio)
426 QS_TIME_PRE(); // timestamp
427 QS_OBJ_PRE(sender); // the sender object
428 QS_SIG_PRE(0U); // the signal of the event
429 QS_OBJ_PRE(this); // this active object
430 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
431 QS_EQC_PRE(0U); // # free entries
432 QS_EQC_PRE(0U); // min # free entries
433 QS_END_PRE()
434
435 QF_CRIT_EXIT();
436}
437
438} // namespace QP
friend class QActiveDummy
Definition qp.hpp:591
friend class QTicker
Definition qp.hpp:587
static QActive * registry_[QF_MAX_ACTIVE+1U]
Definition qp.hpp:581
void postFIFO_(QEvt const *const e, void const *const sender)
Definition qf_actq.cpp:270
bool post_(QEvt const *const e, std::uint_fast16_t const margin, void const *const sender) noexcept
Definition qf_actq.cpp:49
QActive(QStateHandler const initial) noexcept
Definition qp.hpp:597
QACTIVE_EQUEUE_TYPE m_eQueue
Definition qp.hpp:577
QEvt const * get_() noexcept
Definition qf_actq.cpp:208
void postLIFO(QEvt const *const e) noexcept
Definition qf_actq.cpp:133
std::uint8_t m_prio
Definition qp.hpp:565
static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept
Definition qf_actq.cpp:333
QAsmAttr m_temp
Temporary storage for target/act-table etc.
Definition qp.hpp:181
QAsmAttr m_state
Current state (pointer to the current state-handler function)
Definition qp.hpp:180
Event class.
Definition qp.hpp:115
QSignal sig
Signal of the event (see Event Signal)
Definition qp.hpp:117
std::uint8_t poolNum_
Event pool number of this event.
Definition qp.hpp:118
std::uint8_t volatile refCtr_
Event reference counter.
Definition qp.hpp:119
void trig_(void const *const sender) noexcept
Definition qf_actq.cpp:394
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:369
void init(void const *const e, std::uint_fast8_t const qsId) override
Virtual function to take the top-most initial transition in the state machine.
Definition qf_actq.cpp:355
static void tick(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Definition qf_time.cpp:258
void gc(QEvt const *const e) noexcept
Recycle a mutable (mutable) event.
Definition qf_dyn.cpp:188
constexpr std::uint_fast16_t NO_MARGIN
Definition qp.hpp:901
QP/C++ framework.
Definition qequeue.hpp:36
void QEvt_refCtr_inc_(QEvt const *const e) noexcept
Definition qp_pkg.hpp:66
std::uint16_t QEQueueCtr
Definition qequeue.hpp:41
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Definition qk.hpp:96
#define QACTIVE_EQUEUE_WAIT_(me_)
Definition qk.hpp:95
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:94
#define Q_STATE_CAST(handler_)
Definition qp.hpp:418
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64)
Internal (package scope) QP/C++ interface.
Sample QP/C++ port.
QS/C++ dummy public interface.
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.hpp:157
#define QS_EQC_PRE(ctr_)
Definition qs_dummy.hpp:159
#define QS_SIG_PRE(sig_)
Definition qs_dummy.hpp:155
#define QS_TIME_PRE()
Definition qs_dummy.hpp:154
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.hpp:151
#define QS_END_PRE()
Definition qs_dummy.hpp:149
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.hpp:148
Sample QS/C++ port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:39
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section)
Definition qsafe.h:49
#define QF_CRIT_EXIT()
Definition qsafe.h:43
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section)
Definition qsafe.h:86
#define Q_ERROR_INCRIT(id_)
Assertion with user-specified ID for a wrong path through the code (in critical section)
Definition qsafe.h:52
#define QF_CRIT_STAT
Definition qsafe.h:35