QP/C 8.1.4
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_actq.c
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.h" // QP port
31#include "qp_pkg.h" // 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.h" // QS port
35 #include "qs_pkg.h" // QS facilities for pre-defined trace records
36#else
37 #include "qs_dummy.h" // disable the QS software tracing
38#endif // Q_SPY
39
40Q_DEFINE_THIS_MODULE("qf_actq")
41
42//............................................................................
43//! @private @memberof QActive
44static void QActive_postFIFO_(QActive * const me,
45 QEvt const * const e,
46 void const * const sender);
47
48//............................................................................
49//! @private @memberof QActive
50bool QActive_post_(QActive * const me,
51 QEvt const * const e,
52 uint_fast16_t const margin,
53 void const * const sender)
54{
55#ifdef Q_UTEST // test?
56#if (Q_UTEST != 0) // testing QP-stub?
57 if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
58 return QActiveDummy_fakePost_(me, e, margin, sender);
59 }
60#endif // (Q_UTEST != 0)
61#endif // def Q_UTEST
62
65
66 // the event to post must not be NULL
67 Q_REQUIRE_INCRIT(100, e != (QEvt *)0);
68
69 QEQueueCtr const nFree = me->eQueue.nFree; // get member into temporary
70
71 bool const status = ((margin == QF_NO_MARGIN)
72 || (nFree > (QEQueueCtr)margin));
73 if (status) { // should try to post the event?
74
75 // the queue must have a free slot
76 Q_ASSERT_INCRIT(130, nFree != 0U);
77
78#if (QF_MAX_EPOOL > 0U)
79 if (e->poolNum_ != 0U) { // is it a mutable event?
80 QEvt_refCtr_inc_(e); // increment the reference counter
81 }
82#endif // (QF_MAX_EPOOL > 0U)
83
84 QActive_postFIFO_(me, e, sender); // called in crit.sect.
85
87#ifdef Q_UTEST
88 if (QS_LOC_CHECK_(me->prio)) {
89 QS_onTestPost(sender, me, e, true); // QUTest callback
90 }
91#endif // def Q_UTEST
92 }
93 else { // event cannot be posted, but it is OK
95 QS_TIME_PRE(); // timestamp
96 QS_OBJ_PRE(sender); // the sender object
97 QS_SIG_PRE(e->sig); // the signal of the event
98 QS_OBJ_PRE(me); // this active object (recipient)
99 QS_2U8_PRE(e->poolNum_, e->refCtr_);
100 QS_EQC_PRE(nFree); // # free entries
101 QS_EQC_PRE(margin); // margin requested
102 QS_END_PRE()
103
104 QF_CRIT_EXIT();
105
106#ifdef Q_UTEST
107 if (QS_LOC_CHECK_(me->prio)) {
108 QS_onTestPost(sender, me, e, status); // QUTEst callback
109 }
110#endif // def Q_USTEST
111
112#if (QF_MAX_EPOOL > 0U)
113 QF_gc(e); // recycle the event to avoid a leak
114#endif // (QF_MAX_EPOOL > 0U)
115 }
116
117 return status;
118}
119
120//............................................................................
121//! @private @memberof QActive
122void QActive_postLIFO_(QActive * const me, QEvt const * const e) {
123#ifdef Q_UTEST // test?
124#if (Q_UTEST != 0) // testing QP-stub?
125 if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
126 QActiveDummy_fakePostLIFO_(me, e);
127 return;
128 }
129#endif // (Q_UTEST != 0)
130#endif // def Q_UTEST
131
134
135 // the event to post must be be valid (which includes not NULL)
136 Q_REQUIRE_INCRIT(200, e != (QEvt *)0);
137
138 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
139
140 // the queue must NOT overflow for the LIFO posting policy.
141 Q_REQUIRE_INCRIT(230, nFree != 0U);
142
143 if (e->poolNum_ != 0U) { // is it a mutable event?
144 QEvt_refCtr_inc_(e); // increment the reference counter
145 }
146
147 --nFree; // one free entry just used up
148 me->eQueue.nFree = nFree; // update the original
149 if (me->eQueue.nMin > nFree) {
150 me->eQueue.nMin = nFree; // update minimum so far
151 }
152
154 QS_TIME_PRE(); // timestamp
155 QS_SIG_PRE(e->sig); // the signal of this event
156 QS_OBJ_PRE(me); // this active object
158 QS_EQC_PRE(nFree); // # free entries
159 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
160 QS_END_PRE()
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 if (QS_LOC_CHECK_(me->prio)) {
167 QF_CRIT_EXIT();
168 QS_onTestPost((QActive *)0, me, e, true);
170 }
171#endif // def Q_UTEST
172
173 QEvt const * const frontEvt = me->eQueue.frontEvt.e;
174 me->eQueue.frontEvt.e = e; // deliver the event directly to the front
175
176 if (frontEvt != (QEvt *)0) { // was the queue NOT empty?
177 QEQueueCtr tail = me->eQueue.tail; // get member into temporary
178 ++tail;
179 if (tail == me->eQueue.end) { // need to wrap the tail?
180 tail = 0U; // wrap around
181 }
182 me->eQueue.tail = tail;
183 me->eQueue.ring[tail].e = frontEvt;
184 }
185 else { // queue was empty
186 QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
187 }
188
189 QF_CRIT_EXIT();
190}
191
192//............................................................................
193//! @private @memberof QActive
194QEvt const * QActive_get_(QActive * const me) {
197
198 // wait for event to arrive directly (depends on QP port)
199 // NOTE: might use assertion-IDs 400-409
201
202 // always remove event from the front
203 QEvt const * const e = me->eQueue.frontEvt.e;
204
205 // the queue must NOT be empty
206 Q_REQUIRE_INCRIT(310, e != (QEvt *)0);
207
208 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
209
210 ++nFree; // one more free event in the queue
211 me->eQueue.nFree = nFree; // update the # free
212
213 if (nFree <= me->eQueue.end) { // any events in the ring buffer?
214
215 // remove event from the tail
216 QEQueueCtr tail = me->eQueue.tail; // get member into temporary
217
218 QEvt const * const frontEvt = me->eQueue.ring[tail].e;
219
220 // the event queue must not be empty (frontEvt != NULL)
221 Q_ASSERT_INCRIT(350, frontEvt != (QEvt *)0);
222
224 QS_TIME_PRE(); // timestamp
225 QS_SIG_PRE(e->sig); // the signal of this event
226 QS_OBJ_PRE(me); // this active object
228 QS_EQC_PRE(nFree); // # free entries
229 QS_END_PRE()
230
231 me->eQueue.frontEvt.e = frontEvt; // update the original
232 if (tail == 0U) { // need to wrap the tail?
233 tail = me->eQueue.end;
234 }
235 --tail; // advance the tail (counter-clockwise)
236 me->eQueue.tail = tail; // update the original
237 }
238 else {
239 me->eQueue.frontEvt.e = (QEvt *)0; // queue becomes empty
240
241 // all entries in the queue must be free (+1 for fronEvt)
242 Q_ASSERT_INCRIT(370, nFree == (me->eQueue.end + 1U));
243
245 QS_TIME_PRE(); // timestamp
246 QS_SIG_PRE(e->sig); // the signal of this event
247 QS_OBJ_PRE(me); // this active object
249 QS_END_PRE()
250 }
251
252 QF_CRIT_EXIT();
253
254 return e;
255}
256
257//............................................................................
258//! @private @memberof QActive
259static void QActive_postFIFO_(QActive * const me,
260 QEvt const * const e,
261 void const * const sender)
262{
263 // NOTE: this helper function is called *inside* critical section
264#ifndef Q_SPY
265 Q_UNUSED_PAR(sender);
266#endif
267
268 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
269
270 --nFree; // one free entry just used up
271 me->eQueue.nFree = nFree; // update the original
272 if (me->eQueue.nMin > nFree) {
273 me->eQueue.nMin = nFree; // update minimum so far
274 }
275
277 QS_TIME_PRE(); // timestamp
278 QS_OBJ_PRE(sender); // the sender object
279 QS_SIG_PRE(e->sig); // the signal of the event
280 QS_OBJ_PRE(me); // this active object (recipient)
282 QS_EQC_PRE(nFree); // # free entries
283 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
284 QS_END_PRE()
285
286 if (me->eQueue.frontEvt.e == (QEvt *)0) { // is the queue empty?
287 me->eQueue.frontEvt.e = e; // deliver event directly
288
289#ifdef QXK_H_
290 if (me->super.state.act == Q_ACTION_CAST(0)) { // extended thread?
291 QXTHREAD_EQUEUE_SIGNAL_(me); // signal eXtended Thread
292 }
293 else { // basic thread (AO)
294 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
295 }
296#else
297 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
298#endif // def QXK_H_
299 }
300 else { // queue was not empty, insert event into the ring-buffer
301 QEQueueCtr head = me->eQueue.head; // get member into temporary
302 me->eQueue.ring[head].e = e; // insert e into buffer
303
304 if (head == 0U) { // need to wrap the head?
305 head = me->eQueue.end;
306 }
307 --head; // advance the head (counter-clockwise)
308
309 me->eQueue.head = head; // update the original
310 }
311}
312
313//............................................................................
314//! @static @public @memberof QActive
315uint16_t QActive_getQueueUse(uint_fast8_t const prio) {
318
319 // prio must be in range. prio==0 is OK (special case)
321
322 uint16_t nUse = 0U;
323
324 if (prio > 0U) {
325 QActive const * const a = QActive_registry_[prio];
326 // the AO must be registered (started)
327 Q_REQUIRE_INCRIT(510, a != (QActive *)0);
328
329 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
330 nUse = QEQueue_getUse(&a->eQueue);
331 }
332 else { // special case of prio==0U: use of all AO event queues
333 for (uint_fast8_t p = QF_MAX_ACTIVE; p > 0U; --p) {
334 QActive const * const a = QActive_registry_[p];
335 if (a != (QActive *)0) { // is the AO registered?
336 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
337 nUse += QEQueue_getUse(&a->eQueue);
338 }
339 }
340 }
341
342 QF_CRIT_EXIT();
343
344 return nUse;
345}
346
347//............................................................................
348//! @static @public @memberof QActive
349uint16_t QActive_getQueueFree(uint_fast8_t const prio) {
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 != (QActive *)0);
358
359 // NOTE: critical section prevents asynchronous change of the free count
360 uint16_t const nFree = (uint16_t)a->eQueue.nFree;
361
362 QF_CRIT_EXIT();
363
364 return nFree;
365}
366
367//............................................................................
368//! @static @public @memberof QActive
369uint16_t QActive_getQueueMin(uint_fast8_t const prio) {
372
373 // the queried prio. must be in range (excluding the idle thread)
374 Q_REQUIRE_INCRIT(700, (0U < prio) && (prio <= QF_MAX_ACTIVE));
375
376 QActive const * const a = QActive_registry_[prio];
377 // the AO must be registered (started)
378 Q_REQUIRE_INCRIT(710, a != (QActive *)0);
379
380 // NOTE: critical section prevents asynchronous change of the min count
381 uint16_t const nMin = (uint16_t)a->eQueue.nMin;
382
383 QF_CRIT_EXIT();
384
385 return nMin;
386}
387
388//============================================================================
389#if (QF_MAX_TICK_RATE > 0U)
390
391//............................................................................
392//! @public @memberof QTicker
393void QTicker_ctor(QTicker * const me, uint_fast8_t const tickRate) {
394 QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor
395
396 static struct QAsmVtable const vtable = { // QTicker virtual table
399 &QHsm_isIn_,
400 &QHsm_getStateHandler_
401 };
402 me->super.super.vptr = &vtable; // hook the vptr
403
404 // reuse super.eQueue.head for tick-rate
405 me->super.eQueue.head = (QEQueueCtr)tickRate;
406}
407
408//............................................................................
409//! @private @memberof QTicker
410void QTicker_init_(QAsm * const me,
411 void const * const par,
412 uint_fast8_t const qsId)
413{
414 Q_UNUSED_PAR(par);
415 Q_UNUSED_PAR(qsId);
416
419
420 // instead of the top-most initial transition, QTicker initializes
421 // the super.eQueue member inherited from QActive and reused
422 // to count the number of tick events posted to this QTicker.
423 // see also: QTicker_trig_()
424 QACTIVE_CAST_(me)->eQueue.tail = 0U;
425
426 QF_CRIT_EXIT();
427}
428
429//............................................................................
430//! @private @memberof QTicker
431void QTicker_dispatch_(QAsm * const me,
432 QEvt const * const e,
433 uint_fast8_t const qsId)
434{
435 Q_UNUSED_PAR(e);
436 Q_UNUSED_PAR(qsId);
437
440
441 // get members into temporaries
442 QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail;
443 QEQueueCtr const tickRate = QACTIVE_CAST_(me)->eQueue.head;
444
445 // QTicker_dispatch_() shall be called only when it has tick events
446 Q_REQUIRE_INCRIT(800, nTicks > 0U);
447
448 QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks
449
450 QF_CRIT_EXIT();
451
452 // instead of dispatching the event, QTicker calls QTimeEvt_tick_()
453 // processing for the number of times indicated in eQueue.tail.
454 for (; nTicks > 0U; --nTicks) {
455 QTimeEvt_tick_((uint_fast8_t)tickRate, me);
456 }
457}
458
459//............................................................................
460//! @private @memberof QTicker
461void QTicker_trig_(QTicker * const me, void const * const sender) {
462#ifndef Q_SPY
463 Q_UNUSED_PAR(sender);
464#endif
465
466 // static immutable (const) event to post to the QTicker AO
467 static QEvt const tickEvt = QEVT_INITIALIZER(0);
468
471
472 QEQueueCtr nTicks = me->super.eQueue.tail; // get member into temporary
473
474 if (nTicks == 0U) { // no ticks accumulated yet?
475 // when no ticks accumulated, eQueue.fronEvt must be NULL
476 Q_REQUIRE_INCRIT(930, me->super.eQueue.frontEvt.e == (QEvt *)0);
477
478 me->super.eQueue.frontEvt.e = &tickEvt; // deliver event directly
479 me->super.eQueue.nFree = 0U;
480
481 QACTIVE_EQUEUE_SIGNAL_(&me->super); // signal the event queue
482 }
483 else { // some tick have accumulated (and were not processed yet)
484 // when some ticks accumulated, eQueue.fronEvt must be &tickEvt
485 Q_REQUIRE_INCRIT(940, me->super.eQueue.frontEvt.e == &tickEvt);
486
487 // the nTicks counter must accept one more count without overflowing
488 Q_REQUIRE_INCRIT(950, nTicks < 0xFFU);
489 }
490
491 ++nTicks; // account for one more tick event
492 me->super.eQueue.tail = nTicks; // update the original
493
495 QS_TIME_PRE(); // timestamp
496 QS_OBJ_PRE(sender); // the sender object
497 QS_SIG_PRE(0U); // the signal of the event
498 QS_OBJ_PRE(me); // this active object
499 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
500 QS_EQC_PRE(0U); // # free entries
501 QS_EQC_PRE(0U); // min # free entries
502 QS_END_PRE()
503
504 QF_CRIT_EXIT();
505}
506
507#endif // (QF_MAX_TICK_RATE > 0U)
uint16_t QEQueueCtr
The data type to store the ring-buffer counters.
Definition qequeue.h:39
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.h:90
#define QF_NO_MARGIN
Special value of margin that causes asserting failure in case event allocation or event posting fails...
Definition qp.h:698
#define Q_STATE_CAST(handler_)
Perform cast to QStateHandler.
Definition qp.h:177
#define Q_ACTION_CAST(action_)
Perform cast to QActionHandler.
Definition qp.h:178
#define QEVT_INITIALIZER(sig_)
Initializer for QEvt instances (base class).
Definition qp.h:107
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
Definition qp_config.h:100
QP/C Framework in C internal (package-scope) interface.
#define QACTIVE_CAST_(ptr_)
Internal helper macro to encapsulate MISRA deviation.
Definition qp_pkg.h:35
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.h:342
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Port-specific method to signal Active Object event queue (for internal use only).
Definition qp_port.h:325
#define QACTIVE_EQUEUE_WAIT_(me_)
Port-specific method to wait on an empty Active Object event queue (for internal use only).
Definition qp_port.h:316
#define QS_TIME_PRE()
Definition qs.h:340
@ QS_QF_ACTIVE_POST_LIFO
an event was posted (LIFO) directly to AO
Definition qs.h:71
@ QS_QF_ACTIVE_GET
AO got an event and its queue is not empty.
Definition qs.h:72
@ QS_QF_ACTIVE_POST_ATTEMPT
attempt to post an evt to AO failed
Definition qs.h:115
@ QS_QF_ACTIVE_POST
an event was posted (FIFO) directly to AO
Definition qs.h:70
@ QS_QF_ACTIVE_GET_LAST
AO got an event and its queue is empty.
Definition qs.h:73
#define QS_LOC_CHECK_(qsId_)
Definition qs.h:292
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_OBJ_PRE(obj_)
Output pre-formatted object pointer element.
Definition qs_pkg.h:46
#define QS_EQC_PRE(ctr_)
Output pre-formatted event queue counter data element.
Definition qs_pkg.h:63
#define QS_SIG_PRE(sig_)
Output pre-formatted event signal data element.
Definition qs_pkg.h:47
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.h:41
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.h:38
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.h:32
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
Active object class (based on the QHsm implementation strategy).
Definition qp.h:452
QAsm super
Definition qp.h:453
bool QActive_post_(QActive *const me, QEvt const *const e, uint_fast16_t const margin, void const *const sender)
Posts an event e directly to the event queue of the active object using the First-In-First-Out (FIFO)...
Definition qf_actq.c:50
QEvt const * QActive_get_(QActive *const me)
Get an event from the event queue of an active object.
Definition qf_actq.c:194
uint16_t QActive_getQueueFree(uint_fast8_t const prio)
Definition qf_actq.c:349
void QActive_postLIFO_(QActive *const me, QEvt const *const e)
Posts an event e directly to the event queue of the active object using the Last-In-First-Out (LIFO) ...
Definition qf_actq.c:122
void QActive_ctor(QActive *const me, QStateHandler const initial)
QActive constructor (abstract base class).
Definition qf_qact.c:50
QACTIVE_EQUEUE_TYPE eQueue
Port-dependent event-queue type (often QEQueue).
Definition qp.h:466
static void QActive_postFIFO_(QActive *const me, QEvt const *const e, void const *const sender)
Definition qf_actq.c:259
QActive * QActive_registry_[QF_MAX_ACTIVE+1U]
Static (one per-class) array of registered active objects.
Definition qp_pkg.h:38
uint8_t prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:454
uint16_t QActive_getQueueUse(uint_fast8_t const prio)
Definition qf_actq.c:315
uint16_t QActive_getQueueMin(uint_fast8_t const prio)
This function returns the minimum of free entries of the given event queue.
Definition qf_actq.c:369
Abstract State Machine class (state machine interface).
Definition qp.h:183
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OOA).
Definition qp.h:184
union QAsmAttr state
Current state (pointer to the current state-handler function).
Definition qp.h:185
union QAsmAttr temp
Temporary storage for target/act-table etc..
Definition qp.h:186
Virtual table for the QAsm class.
Definition qp.h:213
Event class.
Definition qp.h:100
uint32_t refCtr_
Event reference counter.
Definition qp.h:103
uint32_t poolNum_
Event pool number of this event.
Definition qp.h:102
uint32_t sig
Event signal (see Event Signal).
Definition qp.h:101
"Ticker" Active Object class
Definition qp.h:640
void QTicker_init_(QAsm *const me, void const *const par, uint_fast8_t const qsId)
Definition qf_actq.c:410
QActive super
Definition qp.h:641
void QTicker_trig_(QTicker *const me, void const *const sender)
Asynchronously trigger the QTicker active object to perform tick processing.
Definition qf_actq.c:461
void QTicker_dispatch_(QAsm *const me, QEvt const *const e, uint_fast8_t const qsId)
Definition qf_actq.c:431
void QTicker_ctor(QTicker *const me, uint_fast8_t const tickRate)
Constructor of the QTicker Active Object class.
Definition qf_actq.c:393
QStateHandler fun
Definition qp.h:167
QActionHandler act
Definition qp.h:168