QP/C 8.1.1
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// static helper function
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 status = (nFree > 0U);
72 if (margin == QF_NO_MARGIN) { // no margin requested?
73 // queue must not overflow
74 Q_ASSERT_INCRIT(130, status);
75 }
76 else {
77 status = (nFree > (QEQueueCtr)margin);
78 }
79
80#if (QF_MAX_EPOOL > 0U)
81 if (e->poolNum_ != 0U) { // is it a mutable event?
82 QEvt_refCtr_inc_(e); // increment the reference counter
83 }
84#endif // (QF_MAX_EPOOL > 0U)
85
86 if (status) { // can post the event?
87 QActive_postFIFO_(me, e, sender);
88#ifdef Q_UTEST
89 if (QS_LOC_CHECK_(me->prio)) {
91 QS_onTestPost(sender, me, e, true); // QUTest callback
93 }
94#endif // def Q_UTEST
96 }
97 else { // event cannot be posted
99 QS_TIME_PRE(); // timestamp
100 QS_OBJ_PRE(sender); // the sender object
101 QS_SIG_PRE(e->sig); // the signal of the event
102 QS_OBJ_PRE(me); // this active object (recipient)
103 QS_2U8_PRE(e->poolNum_, e->refCtr_);
104 QS_EQC_PRE(nFree); // # free entries
105 QS_EQC_PRE(margin); // margin requested
106 QS_END_PRE()
107
108#ifdef Q_UTEST
109 if (QS_LOC_CHECK_(me->prio)) {
110 QF_CRIT_EXIT();
111 QS_onTestPost(sender, me, e, status); // QUTEst callback
113 }
114#endif // def Q_USTEST
115
116 QF_CRIT_EXIT();
117
118#if (QF_MAX_EPOOL > 0U)
119 QF_gc(e); // recycle the event to avoid a leak
120#endif // (QF_MAX_EPOOL > 0U)
121 }
122
123 return status;
124}
125
126//............................................................................
127//! @private @memberof QActive
129 QEvt const * const e)
130{
131#ifdef Q_UTEST // test?
132#if (Q_UTEST != 0) // testing QP-stub?
133 if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
134 QActiveDummy_fakePostLIFO_(me, e);
135 return;
136 }
137#endif // (Q_UTEST != 0)
138#endif // def Q_UTEST
139
142
143 // the event to post must be be valid (which includes not NULL)
144 Q_REQUIRE_INCRIT(200, e != (QEvt *)0);
145
146 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
147
148 // the queue must NOT overflow for the LIFO posting policy.
149 Q_REQUIRE_INCRIT(230, nFree != 0U);
150
151 if (e->poolNum_ != 0U) { // is it a mutable event?
152 QEvt_refCtr_inc_(e); // increment the reference counter
153 }
154
155 --nFree; // one free entry just used up
156 me->eQueue.nFree = nFree; // update the original
157 if (me->eQueue.nMin > nFree) {
158 me->eQueue.nMin = nFree; // update minimum so far
159 }
160
162 QS_TIME_PRE(); // timestamp
163 QS_SIG_PRE(e->sig); // the signal of this event
164 QS_OBJ_PRE(me); // this active object
166 QS_EQC_PRE(nFree); // # free entries
167 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
168 QS_END_PRE()
169
170#ifdef Q_UTEST
171 // callback to examine the posted event under the same conditions
172 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
173 // the local filter for this AO ('me->prio') is set
174 if (QS_LOC_CHECK_(me->prio)) {
175 QF_CRIT_EXIT();
176 QS_onTestPost((QActive *)0, me, e, true);
178 }
179#endif // def Q_UTEST
180
181 QEvt const * const frontEvt = me->eQueue.frontEvt;
182 me->eQueue.frontEvt = e; // deliver the event directly to the front
183
184 if (frontEvt != (QEvt *)0) { // was the queue NOT empty?
185 QEQueueCtr tail = me->eQueue.tail; // get member into temporary
186 ++tail;
187 if (tail == me->eQueue.end) { // need to wrap the tail?
188 tail = 0U; // wrap around
189 }
190 me->eQueue.tail = tail;
191 me->eQueue.ring[tail] = frontEvt;
192 }
193 else { // queue was empty
194 QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
195 }
196
197 QF_CRIT_EXIT();
198}
199
200//............................................................................
201//! @private @memberof QActive
202QEvt const * QActive_get_(QActive * const me) {
205
206 // wait for event to arrive directly (depends on QP port)
207 // NOTE: might use assertion-IDs 400-409
209
210 // always remove event from the front
211 QEvt const * const e = me->eQueue.frontEvt;
212
213 // the queue must NOT be empty
214 Q_REQUIRE_INCRIT(310, e != (QEvt *)0);
215
216 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
217
218 ++nFree; // one more free event in the queue
219 me->eQueue.nFree = nFree; // update the # free
220
221 if (nFree <= me->eQueue.end) { // any events in the ring buffer?
222
223 // remove event from the tail
224 QEQueueCtr tail = me->eQueue.tail; // get member into temporary
225
226 QEvt const * const frontEvt = me->eQueue.ring[tail];
227
228 // the event queue must not be empty (frontEvt != NULL)
229 Q_ASSERT_INCRIT(350, frontEvt != (QEvt *)0);
230
232 QS_TIME_PRE(); // timestamp
233 QS_SIG_PRE(e->sig); // the signal of this event
234 QS_OBJ_PRE(me); // this active object
236 QS_EQC_PRE(nFree); // # free entries
237 QS_END_PRE()
238
239 me->eQueue.frontEvt = frontEvt; // update the original
240 if (tail == 0U) { // need to wrap the tail?
241 tail = me->eQueue.end;
242 }
243 --tail; // advance the tail (counter-clockwise)
244 me->eQueue.tail = tail; // update the original
245 }
246 else {
247 me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty
248
249 // all entries in the queue must be free (+1 for fronEvt)
250 Q_ASSERT_INCRIT(360, nFree == (me->eQueue.end + 1U));
251
253 QS_TIME_PRE(); // timestamp
254 QS_SIG_PRE(e->sig); // the signal of this event
255 QS_OBJ_PRE(me); // this active object
257 QS_END_PRE()
258 }
259
260 QF_CRIT_EXIT();
261
262 return e;
263}
264
265//............................................................................
266//! @private @memberof QActive
267static void QActive_postFIFO_(QActive * const me,
268 QEvt const * const e,
269 void const * const sender)
270{
271 // NOTE: this helper function is called *inside* critical section
272#ifndef Q_SPY
273 Q_UNUSED_PAR(sender);
274#endif
275
276 QEQueueCtr nFree = me->eQueue.nFree; // get member into temporary
277
278 --nFree; // one free entry just used up
279 me->eQueue.nFree = nFree; // update the original
280 if (me->eQueue.nMin > nFree) {
281 me->eQueue.nMin = nFree; // update minimum so far
282 }
283
285 QS_TIME_PRE(); // timestamp
286 QS_OBJ_PRE(sender); // the sender object
287 QS_SIG_PRE(e->sig); // the signal of the event
288 QS_OBJ_PRE(me); // this active object (recipient)
290 QS_EQC_PRE(nFree); // # free entries
291 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
292 QS_END_PRE()
293
294 if (me->eQueue.frontEvt == (QEvt *)0) { // is the queue empty?
295 me->eQueue.frontEvt = e; // deliver event directly
296
297#ifdef QXK_H_
298 if (me->super.state.act == Q_ACTION_CAST(0)) { // extended thread?
299 QXTHREAD_EQUEUE_SIGNAL_(me); // signal eXtended Thread
300 }
301 else { // basic thread (AO)
302 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
303 }
304#else
305 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
306#endif // def QXK_H_
307 }
308 else { // queue was not empty, insert event into the ring-buffer
309 QEQueueCtr head = me->eQueue.head; // get member into temporary
310 me->eQueue.ring[head] = e; // insert e into buffer
311
312 if (head == 0U) { // need to wrap the head?
313 head = me->eQueue.end;
314 }
315 --head; // advance the head (counter-clockwise)
316
317 me->eQueue.head = head; // update the original
318 }
319}
320
321//............................................................................
322//! @static @public @memberof QActive
323uint16_t QActive_getQueueUse(uint_fast8_t const prio) {
326
327 // prio must be in range. prio==0 is OK (special case)
329
330 uint16_t nUse = 0U;
331
332 if (prio > 0U) {
333 QActive const * const a = QActive_registry_[prio];
334 // the AO must be registered (started)
335 Q_REQUIRE_INCRIT(510, a != (QActive *)0);
336
337 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
338 nUse = QEQueue_getUse(&a->eQueue);
339 }
340 else { // special case of prio==0U: use of all AO event queues
341 for (uint_fast8_t p = QF_MAX_ACTIVE; p > 0U; --p) {
342 QActive const * const a = QActive_registry_[p];
343 if (a != (QActive *)0) { // is the AO registered?
344 // NOTE: QEQueue_getUse() does NOT apply crit.sect. internally
345 nUse += QEQueue_getUse(&a->eQueue);
346 }
347 }
348 }
349
350 QF_CRIT_EXIT();
351
352 return nUse;
353}
354
355//............................................................................
356//! @static @public @memberof QActive
357uint16_t QActive_getQueueFree(uint_fast8_t const prio) {
360
361 Q_REQUIRE_INCRIT(600, (0U < prio) && (prio <= QF_MAX_ACTIVE));
362
363 QActive const * const a = QActive_registry_[prio];
364 // the AO must be registered (started)
365 Q_REQUIRE_INCRIT(610, a != (QActive *)0);
366
367 // NOTE: critical section prevents asynchronous change of the free count
368 uint16_t const nFree = (uint16_t)a->eQueue.nFree;
369
370 QF_CRIT_EXIT();
371
372 return nFree;
373}
374
375//............................................................................
376//! @static @public @memberof QActive
377uint16_t QActive_getQueueMin(uint_fast8_t const prio) {
380
381 // the queried prio. must be in range (excluding the idle thread)
382 Q_REQUIRE_INCRIT(700, (0U < prio) && (prio <= QF_MAX_ACTIVE));
383
384 QActive const * const a = QActive_registry_[prio];
385 // the AO must be registered (started)
386 Q_REQUIRE_INCRIT(710, a != (QActive *)0);
387
388 // NOTE: critical section prevents asynchronous change of the min count
389 uint16_t const nMin = (uint16_t)a->eQueue.nMin;
390
391 QF_CRIT_EXIT();
392
393 return nMin;
394}
395
396//============================================================================
397#if (QF_MAX_TICK_RATE > 0U)
398
399//............................................................................
400//! @public @memberof QTicker
401void QTicker_ctor(QTicker * const me,
402 uint_fast8_t const tickRate)
403{
404 QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor
405
406 static struct QAsmVtable const vtable = { // QTicker virtual table
409 &QHsm_isIn_,
410 &QHsm_getStateHandler_
411 };
412 me->super.super.vptr = &vtable; // hook the vptr
413
414 // reuse super.eQueue.head for tick-rate
415 me->super.eQueue.head = (QEQueueCtr)tickRate;
416}
417
418//............................................................................
419//! @private @memberof QTicker
420void QTicker_init_(QAsm * const me,
421 void const * const par,
422 uint_fast8_t const qsId)
423{
424 Q_UNUSED_PAR(par);
425 Q_UNUSED_PAR(qsId);
426
429
430 // instead of the top-most initial transition, QTicker initializes
431 // the super.eQueue member inherited from QActive and reused
432 // to count the number of tick events posted to this QTicker.
433 // see also: QTicker_trig_()
434 QACTIVE_CAST_(me)->eQueue.tail = 0U;
435
436 QF_CRIT_EXIT();
437}
438
439//............................................................................
440//! @private @memberof QTicker
441void QTicker_dispatch_(QAsm * const me,
442 QEvt const * const e,
443 uint_fast8_t const qsId)
444{
445 Q_UNUSED_PAR(e);
446 Q_UNUSED_PAR(qsId);
447
450
451 // get members into temporaries
452 QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail;
453 QEQueueCtr const tickRate = QACTIVE_CAST_(me)->eQueue.head;
454
455 // QTicker_dispatch_() shall be called only when it has tick events
456 Q_REQUIRE_INCRIT(800, nTicks > 0U);
457
458 QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks
459
460 QF_CRIT_EXIT();
461
462 // instead of dispatching the event, QTicker calls QTimeEvt_tick_()
463 // processing for the number of times indicated in eQueue.tail.
464 for (; nTicks > 0U; --nTicks) {
465 QTimeEvt_tick_((uint_fast8_t)tickRate, me);
466 }
467}
468
469//............................................................................
470//! @private @memberof QTicker
471void QTicker_trig_(QTicker * const me,
472 void const * const sender)
473{
474#ifndef Q_SPY
475 Q_UNUSED_PAR(sender);
476#endif
477
478 // static immutable (const) event to post to the QTicker AO
479 static QEvt const tickEvt = QEVT_INITIALIZER(0);
480
483
484 QEQueueCtr nTicks = me->super.eQueue.tail; // get member into temporary
485
486 if (nTicks == 0U) { // no ticks accumulated yet?
487 // when no ticks accumulated, eQueue.fronEvt must be NULL
488 Q_REQUIRE_INCRIT(930, me->super.eQueue.frontEvt == (QEvt *)0);
489
490 me->super.eQueue.frontEvt = &tickEvt; // deliver event directly
491 me->super.eQueue.nFree = 0U;
492
493 QACTIVE_EQUEUE_SIGNAL_(&me->super); // signal the event queue
494 }
495 else { // some tick have accumulated (and were not processed yet)
496 // when some ticks accumulated, eQueue.fronEvt must be &tickEvt
497 Q_REQUIRE_INCRIT(940, me->super.eQueue.frontEvt == &tickEvt);
498
499 // the nTicks counter must accept one more count without overflowing
500 Q_REQUIRE_INCRIT(950, nTicks < 0xFFU);
501 }
502
503 ++nTicks; // account for one more tick event
504 me->super.eQueue.tail = nTicks; // update the original
505
507 QS_TIME_PRE(); // timestamp
508 QS_OBJ_PRE(sender); // the sender object
509 QS_SIG_PRE(0U); // the signal of the event
510 QS_OBJ_PRE(me); // this active object
511 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
512 QS_EQC_PRE(0U); // # free entries
513 QS_EQC_PRE(0U); // min # free entries
514 QS_END_PRE()
515
516 QF_CRIT_EXIT();
517}
518
519#endif // (QF_MAX_TICK_RATE > 0U)
uint16_t QEQueueCtr
The data type to store the ring-buffer counters.
Definition qequeue.h:39
static void QActive_postFIFO_(QActive *const me, QEvt const *const e, void const *const sender)
#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:674
#define Q_STATE_CAST(handler_)
Perform cast to QStateHandler.
Definition qp.h:172
#define Q_ACTION_CAST(action_)
Perform cast to QActionHandler.
Definition qp.h:173
#define QEVT_INITIALIZER(sig_)
Initializer for immutable (constant) QEvt instances.
Definition qp.h:107
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
Definition qp_config.h:99
QP 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 QACTIVE_EQUEUE_SIGNAL_(me_)
Definition qp_port.h:169
#define QACTIVE_EQUEUE_WAIT_(me_)
Definition qp_port.h:168
#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:428
QAsm super
Definition qp.h:429
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:202
uint16_t QActive_getQueueFree(uint_fast8_t const prio)
Definition qf_actq.c:357
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:128
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:442
static void QActive_postFIFO_(QActive *const me, QEvt const *const e, void const *const sender)
Definition qf_actq.c:267
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:430
uint16_t QActive_getQueueUse(uint_fast8_t const prio)
Definition qf_actq.c:323
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:377
Abstract State Machine class (state machine interface).
Definition qp.h:178
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OO).
Definition qp.h:179
union QAsmAttr state
Current state (pointer to the current state-handler function).
Definition qp.h:180
union QAsmAttr temp
Temporary storage for target/act-table etc..
Definition qp.h:181
Virtual table for the QAsm class.
Definition qp.h:208
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:616
void QTicker_init_(QAsm *const me, void const *const par, uint_fast8_t const qsId)
Definition qf_actq.c:420
QActive super
Definition qp.h:617
void QTicker_trig_(QTicker *const me, void const *const sender)
Asynchronously trigger the QTicker active object to perform tick processing.
Definition qf_actq.c:471
void QTicker_dispatch_(QAsm *const me, QEvt const *const e, uint_fast8_t const qsId)
Definition qf_actq.c:441
void QTicker_ctor(QTicker *const me, uint_fast8_t const tickRate)
Constructor of the QTicker Active Object class.
Definition qf_actq.c:401
QStateHandler fun
Definition qp.h:162
QActionHandler act
Definition qp.h:163