QP/C++  7.0.1
Real-Time Embedded Framework
qf_actq.cpp
Go to the documentation of this file.
1//============================================================================
2// QP/C++ Real-Time Embedded Framework (RTEF)
3// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
4//
5// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
6//
7// This software is dual-licensed under the terms of the open source GNU
8// General Public License version 3 (or any later version), or alternatively,
9// under the terms of one of the closed source Quantum Leaps commercial
10// licenses.
11//
12// The terms of the open source GNU General Public License version 3
13// can be found at: <www.gnu.org/licenses/gpl-3.0>
14//
15// The terms of the closed source Quantum Leaps commercial licenses
16// can be found at: <www.state-machine.com/licensing>
17//
18// Redistributions in source code must retain this top-level comment block.
19// Plagiarizing this software to sidestep the license obligations is illegal.
20//
21// Contact information:
22// <www.state-machine.com>
23// <info@state-machine.com>
24//============================================================================
35
36#define QP_IMPL // this is QP implementation
37#include "qf_port.hpp" // QF port
38#include "qf_pkg.hpp" // QF package-scope interface
39#include "qassert.h" // QP embedded systems-friendly assertions
40#ifdef Q_SPY // QS software tracing enabled?
41 #include "qs_port.hpp" // QS port
42 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
43#else
44 #include "qs_dummy.hpp" // disable the QS software tracing
45#endif // Q_SPY
46
47// unnamed namespace for local definitions with internal linkage
48namespace {
49
50Q_DEFINE_THIS_MODULE("qf_actq")
51
52} // unnamed namespace
53
54//============================================================================
55namespace QP {
56
57#ifdef Q_SPY
58
59//............................................................................
60bool QActive::post_(QEvt const * const e,
61 std::uint_fast16_t const margin,
62 void const * const sender) noexcept
63#else
64bool QActive::post_(QEvt const * const e,
65 std::uint_fast16_t const margin) noexcept
66#endif
67{
69 Q_REQUIRE_ID(100, e != nullptr);
70
72 QF_CRIT_E_();
73 QEQueueCtr nFree = m_eQueue.m_nFree; // get volatile into the temporary
74
75 // test-probe#1 for faking queue overflow
78 nFree = 0U;
79 )
80
81 bool status;
82 if (margin == QF_NO_MARGIN) {
83 if (nFree > 0U) {
84 status = true; // can post
85 }
86 else {
87 status = false; // cannot post
88 Q_ERROR_CRIT_(110); // must be able to post the event
89 }
90 }
91 else if (nFree > static_cast<QEQueueCtr>(margin)) {
92 status = true; // can post
93 }
94 else {
95 status = false; // cannot post, but don't assert
96 }
97
98 // is it a dynamic event?
99 if (e->poolId_ != 0U) {
100 QF_EVT_REF_CTR_INC_(e); // increment the reference counter
101 }
102
103 if (status) { // can post the event?
104
105 --nFree; // one free entry just used up
106 m_eQueue.m_nFree = nFree; // update the volatile
107 if (m_eQueue.m_nMin > nFree) {
108 m_eQueue.m_nMin = nFree; // update minimum so far
109 }
110
112 QS_TIME_PRE_(); // timestamp
113 QS_OBJ_PRE_(sender); // the sender object
114 QS_SIG_PRE_(e->sig); // the signal of the event
115 QS_OBJ_PRE_(this); // this active object
116 QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr
117 QS_EQC_PRE_(nFree); // number of free entries
118 QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries
120
121#ifdef Q_UTEST
122 // callback to examine the posted event under the same conditions
123 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
124 // the local filter for this AO ('me->prio') is set
125 //
126 if (QS_LOC_CHECK_(m_prio)) {
127 QS::onTestPost(sender, this, e, status);
128 }
129#endif
130 // empty queue?
131 if (m_eQueue.m_frontEvt == nullptr) {
132 m_eQueue.m_frontEvt = e; // deliver event directly
133 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
134 }
135 // queue is not empty, insert event into the ring-buffer
136 else {
137 // insert event pointer e into the buffer (FIFO)
138 m_eQueue.m_ring[m_eQueue.m_head] = e;
139
140 // need to wrap head?
141 if (m_eQueue.m_head == 0U) {
142 m_eQueue.m_head = m_eQueue.m_end; // wrap around
143 }
144 // advance the head (counter clockwise)
145 m_eQueue.m_head = (m_eQueue.m_head - 1U);
146 }
147
148 QF_CRIT_X_();
149 }
150 else { // cannot post the event
151
153 QS_TIME_PRE_(); // timestamp
154 QS_OBJ_PRE_(sender); // the sender object
155 QS_SIG_PRE_(e->sig); // the signal of the event
156 QS_OBJ_PRE_(this); // this active object
157 QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr
158 QS_EQC_PRE_(nFree); // number of free entries
159 QS_EQC_PRE_(margin); // margin requested
161
162#ifdef Q_UTEST
163 // callback to examine the posted event under the same conditions
164 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
165 // the local filter for this AO ('me->prio') is set
166 //
167 if (QS_LOC_CHECK_(m_prio)) {
168 QS::onTestPost(sender, this, e, status);
169 }
170#endif
171
172 QF_CRIT_X_();
173
174 QF::gc(e); // recycle the event to avoid a leak
175 }
176
177 return status;
178}
179
180//............................................................................
181void QActive::postLIFO(QEvt const * const e) noexcept {
183 QF_CRIT_E_();
184 QEQueueCtr nFree = m_eQueue.m_nFree;// tmp to avoid UB for volatile access
185
188 nFree = 0U;
189 )
190
191 // the queue must be able to accept the event (cannot overflow)
192 Q_ASSERT_CRIT_(210, nFree != 0U);
193
194 // is it a dynamic event?
195 if (e->poolId_ != 0U) {
196 QF_EVT_REF_CTR_INC_(e); // increment the reference counter
197 }
198
199 --nFree; // one free entry just used up
200 m_eQueue.m_nFree = nFree; // update the volatile
201 if (m_eQueue.m_nMin > nFree) {
202 m_eQueue.m_nMin = nFree; // update minimum so far
203 }
204
206 QS_TIME_PRE_(); // timestamp
207 QS_SIG_PRE_(e->sig); // the signal of this event
208 QS_OBJ_PRE_(this); // this active object
209 QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr
210 QS_EQC_PRE_(nFree); // number of free entries
211 QS_EQC_PRE_(m_eQueue.m_nMin); // min number of free entries
213
214#ifdef Q_UTEST
215 // callback to examine the posted event under the same conditions
216 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
217 // the local filter for this AO ('me->prio') is set
218 //
219 if (QS_LOC_CHECK_(m_prio)) {
220 QS::onTestPost(nullptr, this, e, true);
221 }
222#endif
223
224 // read volatile into temporary
225 QEvt const * const frontEvt = m_eQueue.m_frontEvt;
226 m_eQueue.m_frontEvt = e; // deliver the event directly to the front
227
228 // was the queue empty?
229 if (frontEvt == nullptr) {
230 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
231 }
232 // queue was not empty, leave the event in the ring-buffer
233 else {
234 m_eQueue.m_tail = (m_eQueue.m_tail + 1U);
235 if (m_eQueue.m_tail == m_eQueue.m_end) { // need to wrap the tail?
236 m_eQueue.m_tail = 0U; // wrap around
237 }
238
239 m_eQueue.m_ring[m_eQueue.m_tail] = frontEvt;
240 }
241 QF_CRIT_X_();
242}
243
244//............................................................................
245QEvt const *QActive::get_(void) noexcept {
246
248 QF_CRIT_E_();
249 QACTIVE_EQUEUE_WAIT_(this); // wait for event to arrive directly
250
251 // always remove evt from the front
252 QEvt const * const e = m_eQueue.m_frontEvt;
253 QEQueueCtr const nFree = m_eQueue.m_nFree + 1U;
254 m_eQueue.m_nFree = nFree; // upate the number of free
255
256 // any events in the ring buffer?
257 if (nFree <= m_eQueue.m_end) {
258
259 // remove event from the tail
260 m_eQueue.m_frontEvt = m_eQueue.m_ring[m_eQueue.m_tail];
261 if (m_eQueue.m_tail == 0U) { // need to wrap?
262 m_eQueue.m_tail = m_eQueue.m_end; // wrap around
263 }
264 m_eQueue.m_tail = (m_eQueue.m_tail - 1U);
265
267 QS_TIME_PRE_(); // timestamp
268 QS_SIG_PRE_(e->sig); // the signal of this event
269 QS_OBJ_PRE_(this); // this active object
270 QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr
271 QS_EQC_PRE_(nFree); // number of free entries
273 }
274 else {
275 // the queue becomes empty
276 m_eQueue.m_frontEvt = nullptr;
277
278 // all entries in the queue must be free (+1 for fronEvt)
279 Q_ASSERT_CRIT_(310, nFree == (m_eQueue.m_end + 1U));
280
282 QS_TIME_PRE_(); // timestamp
283 QS_SIG_PRE_(e->sig); // the signal of this event
284 QS_OBJ_PRE_(this); // this active object
285 QS_2U8_PRE_(e->poolId_, e->refCtr_); // pool-Id & ref-ctr
287 }
288 QF_CRIT_X_();
289 return e;
290}
291
292//............................................................................
293std::uint_fast16_t QF::getQueueMin(std::uint_fast8_t const prio) noexcept {
294
295 Q_REQUIRE_ID(400, (prio <= QF_MAX_ACTIVE)
296 && (active_[prio] != nullptr));
297
299 QF_CRIT_E_();
300 std::uint_fast16_t const min =
301 static_cast<std::uint_fast16_t>(active_[prio]->m_eQueue.m_nMin);
302 QF_CRIT_X_();
303
304 return min;
305}
306
307//============================================================================
308QTicker::QTicker(std::uint_fast8_t const tickRate) noexcept
309 : QActive(nullptr)
310{
311 // reuse m_head for tick-rate
312 m_eQueue.m_head = static_cast<QEQueueCtr>(tickRate);
313}
314//............................................................................
315void QTicker::init(void const * const e,
316 std::uint_fast8_t const qs_id) noexcept
317{
318 static_cast<void>(e); // unused parameter
319 static_cast<void>(qs_id); // unused parameter
320 m_eQueue.m_tail = 0U;
321}
322//............................................................................
323void QTicker::dispatch(QEvt const * const e,
324 std::uint_fast8_t const qs_id) noexcept
325{
326 static_cast<void>(e); // unused parameter
327 static_cast<void>(qs_id); // unused parameter
328
330 QF_CRIT_E_();
331 QEQueueCtr nTicks = m_eQueue.m_tail; // # ticks since the last call
332 m_eQueue.m_tail = 0U; // clear the # ticks
333 QF_CRIT_X_();
334
335 for (; nTicks > 0U; --nTicks) {
336 QF::TICK_X(static_cast<std::uint_fast8_t>(m_eQueue.m_head), this);
337 }
338}
339//............................................................................
340void QTicker::init(std::uint_fast8_t const qs_id) noexcept {
341 QTicker::init(nullptr, qs_id);
342}
343
344//============================================================================
345#ifdef Q_SPY
346
347//............................................................................
348bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin,
349 void const * const sender) noexcept
350#else
351bool QTicker::post_(QEvt const * const e, std::uint_fast16_t const margin)
352 noexcept
353#endif
354{
355 static_cast<void>(e); // unused parameter
356 static_cast<void>(margin); // unused parameter
357
359 QF_CRIT_E_();
360 if (m_eQueue.m_frontEvt == nullptr) {
361
362#ifdef Q_EVT_CTOR
363 static QEvt const tickEvt(0U, QEvt::STATIC_EVT);
364#else
365 static QEvt const tickEvt = { 0U, 0U, 0U };
366#endif // Q_EVT_CTOR
367
368 m_eQueue.m_frontEvt = &tickEvt; // deliver event directly
369 m_eQueue.m_nFree = (m_eQueue.m_nFree - 1U); // one less free event
370
371 QACTIVE_EQUEUE_SIGNAL_(this); // signal the event queue
372 }
373
374 // account for one more tick event
375 m_eQueue.m_tail = (m_eQueue.m_tail + 1U);
376
378 QS_TIME_PRE_(); // timestamp
379 QS_OBJ_PRE_(sender); // the sender object
380 QS_SIG_PRE_(0U); // the signal of the event
381 QS_OBJ_PRE_(this); // this active object
382 QS_2U8_PRE_(0U, 0U); // pool-Id & ref-ctr
383 QS_EQC_PRE_(0U); // number of free entries
384 QS_EQC_PRE_(0U); // min number of free entries
386
387 QF_CRIT_X_();
388
389 return true; // the event is always posted correctly
390}
391
392//............................................................................
393void QTicker::postLIFO(QEvt const * const e) noexcept {
394 static_cast<void>(e); // unused parameter
395 Q_ERROR_ID(900); // operation not allowed
396}
397
398} // namespace QP
QActive active object class (based on QP::QHsm implementation strategy)
Definition: qf.hpp:139
friend class QTicker
Definition: qf.hpp:498
virtual void postLIFO(QEvt const *const e) noexcept
Posts an event directly to the event queue of the active object using the Last-In-First-Out (LIFO) po...
Definition: qf_actq.cpp:181
virtual bool post_(QEvt const *const e, std::uint_fast16_t const margin, void const *const sender) noexcept
Definition: qf_actq.cpp:60
QEvt const * get_(void) noexcept
Get an event from the event queue of an active object.
Definition: qf_actq.cpp:245
static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept
This function returns the minimum of free entries of the given event queue of an active object (indic...
Definition: qf_actq.cpp:293
static void gc(QEvt const *const e) noexcept
Recycle a dynamic event.
Definition: qf_dyn.cpp:140
static void onTestPost(void const *sender, QActive *recipient, QEvt const *e, bool status)
void init(void const *const e, std::uint_fast8_t const qs_id) noexcept override
executes the top-most initial transition in QP::QHsm
Definition: qf_actq.cpp:315
void postLIFO(QEvt const *const e) noexcept override
Posts an event directly to the event queue of the active object using the Last-In-First-Out (LIFO) po...
Definition: qf_actq.cpp:393
bool post_(QEvt const *const e, std::uint_fast16_t const margin, void const *const sender) noexcept override
Definition: qf_actq.cpp:348
void dispatch(QEvt const *const e, std::uint_fast8_t const qs_id) noexcept override
Dispatches an event to QHsm.
Definition: qf_actq.cpp:323
namespace associated with the QP/C++ framework
Definition: exa_native.dox:1
std::uint_fast16_t const QF_NO_MARGIN
special value of margin that causes asserting failure in case event allocation or event posting fails
Definition: qf.hpp:1211
@ QS_QF_ACTIVE_POST_LIFO
an event was posted (LIFO) directly to AO
Definition: qs.hpp:592
@ QS_QF_ACTIVE_GET
AO got an event and its queue is not empty.
Definition: qs.hpp:593
@ QS_QF_ACTIVE_POST_ATTEMPT
attempt to post an evt to AO failed
Definition: qs.hpp:636
@ QS_QF_ACTIVE_POST
an event was posted (FIFO) directly to AO
Definition: qs.hpp:591
@ QS_QF_ACTIVE_GET_LAST
AO got an event and its queue is empty.
Definition: qs.hpp:594
std::uint16_t QEQueueCtr
Definition: qequeue.hpp:65
void QF_EVT_REF_CTR_INC_(QEvt const *const e) noexcept
increment the refCtr_ of an event e
Definition: qf_pkg.hpp:142
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:102
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:252
#define Q_ERROR_ID(id_)
Definition: qassert.h:187
#define TICK_X(tickRate_, sender_)
Invoke the system clock tick processing QP::QF::tickX_()
Definition: qf.hpp:1411
Internal (package scope) QF/C++ interface.
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.hpp:48
#define Q_ASSERT_CRIT_(id_, test_)
Definition: qf_pkg.hpp:86
#define QF_CRIT_X_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.hpp:69
#define QF_CRIT_E_()
This is an internal macro for entering a critical section.
Definition: qf_pkg.hpp:58
#define Q_ERROR_CRIT_(id_)
Definition: qf_pkg.hpp:95
#define QS_TIME_PRE_()
Internal macro to output time stamp to a QS record.
Definition: qs.hpp:748
#define QS_TEST_PROBE_DEF(fun_)
QS macro to define the Test-Probe for a given fun_.
Definition: qs.hpp:1283
#define QS_LOC_CHECK_(qs_id_)
helper macro for checking the local QS filter
Definition: qs.hpp:944
#define QS_TEST_PROBE_ID(id_, code_)
QS macro to apply a Test-Probe.
Definition: qs.hpp:1292
Internal (package scope) QS/C++ interface.
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Internal QS macro to begin a predefined QS record without critical section.
Definition: qs_pkg.hpp:133
#define QS_OBJ_PRE_(obj_)
Internal QS macro to output object pointer data element.
Definition: qs_pkg.hpp:182
#define QS_END_NOCRIT_PRE_()
Internal QS macro to end a predefiend QS record without critical section.
Definition: qs_pkg.hpp:142
#define QS_2U8_PRE_(data1_, data2_)
Internal QS macro to output 2 unformatted uint8_t data elements.
Definition: qs_pkg.hpp:165
#define QS_EQC_PRE_(ctr_)
Definition: qs_pkg.hpp:216
#define QF_MAX_ACTIVE
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Definition: qv.hpp:95
#define QACTIVE_EQUEUE_WAIT_(me_)
Definition: qv.hpp:93
QEvt base class.
Definition: qep.hpp:155
QSignal sig
signal of the event instance
Definition: qep.hpp:156
std::uint8_t volatile refCtr_
reference counter
Definition: qep.hpp:158
std::uint8_t poolId_
pool ID (0 for static event)
Definition: qep.hpp:157