QP/C  8.0.3
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//............................................................................
43static void QActive_postFIFO_(QActive * const me,
44 QEvt const * const e,
45 void const * const sender);
46
47//............................................................................
48//! @private @memberof QActive
49bool QActive_post_(QActive * const me,
50 QEvt const * const e,
51 uint_fast16_t const margin,
52 void const * const sender)
53{
54#ifdef Q_UTEST // test?
55#if (Q_UTEST != 0) // testing QP-stub?
56 if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
57 return QActiveDummy_fakePost_(me, e, margin, sender);
58 }
59#endif // (Q_UTEST != 0)
60#endif // def Q_UTEST
61
64
65 Q_REQUIRE_INCRIT(200, e != (QEvt *)0);
66
67 QEQueueCtr const nFree = me->eQueue.nFree; // get volatile into temporary
68
69 // required margin available?
70 bool status = false; // assume that event cannot be posted
71 if (margin == QF_NO_MARGIN) {
72 if (nFree > 0U) { // free entries available in the queue?
73 status = true; // can post
74 }
75 else { // no free entries available
76 // The queue overflows, but QF_NO_MARGIN indicates that
77 // the "event delivery guarantee" is required.
78 Q_ERROR_INCRIT(210); // must be able to post the event
79 }
80 }
81 else if (nFree > (QEQueueCtr)margin) { // enough free entries?
82 status = true; // can post
83 }
84 else {
85 // empty
86 }
87
88#if (QF_MAX_EPOOL > 0U)
89 if (e->poolNum_ != 0U) { // is it a mutable event?
90 Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE));
91 QEvt_refCtr_inc_(e); // increment the reference counter
92 }
93#endif // (QF_MAX_EPOOL > 0U)
94
95 if (status) { // can post the event?
96 QActive_postFIFO_(me, e, sender);
98 }
99 else { // event cannot be posted
100 QS_BEGIN_PRE(QS_QF_ACTIVE_POST_ATTEMPT, me->prio)
101 QS_TIME_PRE(); // timestamp
102 QS_OBJ_PRE(sender); // the sender object
103 QS_SIG_PRE(e->sig); // the signal of the event
104 QS_OBJ_PRE(me); // this active object (recipient)
105 QS_2U8_PRE(e->poolNum_, e->refCtr_);
106 QS_EQC_PRE(nFree); // # free entries
107 QS_EQC_PRE(margin); // margin requested
108 QS_END_PRE()
109
110#ifdef Q_UTEST
111 if (QS_LOC_CHECK_(me->prio)) {
112 QF_CRIT_EXIT();
113 QS_onTestPost(sender, me, e, status); // QUTEst callback
114 QF_CRIT_ENTRY();
115 }
116#endif // def Q_USTEST
117
118 QF_CRIT_EXIT();
119
120#if (QF_MAX_EPOOL > 0U)
121 QF_gc(e); // recycle the event to avoid a leak
122#endif // (QF_MAX_EPOOL > 0U)
123 }
124
125 return status;
126}
127
128//............................................................................
129//! @private @memberof QActive
131 QEvt const * const e)
132{
133#ifdef Q_UTEST // test?
134#if (Q_UTEST != 0) // testing QP-stub?
135 if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
136 QActiveDummy_fakePostLIFO_(me, e);
137 return;
138 }
139#endif // (Q_UTEST != 0)
140#endif // def Q_UTEST
141
144
145 // the posted event must be be valid (which includes not NULL)
146 Q_REQUIRE_INCRIT(300, e != (QEvt *)0);
147
148 QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary
149
150 // The queue must NOT overflow for the LIFO posting policy.
151 Q_REQUIRE_INCRIT(310, tmp != 0U);
152
153 if (e->poolNum_ != 0U) { // is it a mutable event?
154 Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE));
155 QEvt_refCtr_inc_(e); // increment the reference counter
156 }
157
158 --tmp; // one free entry just used up
159
160 me->eQueue.nFree = tmp; // update the original
161 if (me->eQueue.nMin > tmp) {
162 me->eQueue.nMin = tmp; // update minimum so far
163 }
164
165 QS_BEGIN_PRE(QS_QF_ACTIVE_POST_LIFO, me->prio)
166 QS_TIME_PRE(); // timestamp
167 QS_SIG_PRE(e->sig); // the signal of this event
168 QS_OBJ_PRE(me); // this active object
170 QS_EQC_PRE(tmp); // # free entries
171 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
172 QS_END_PRE()
173
174#ifdef Q_UTEST
175 // callback to examine the posted event under the same conditions
176 // as producing the #QS_QF_ACTIVE_POST trace record, which are:
177 // the local filter for this AO ('me->prio') is set
178 if (QS_LOC_CHECK_(me->prio)) {
179 QF_CRIT_EXIT();
180
181 QS_onTestPost((QActive *)0, me, e, true);
182
184 }
185#endif // def Q_UTEST
186
187 QEvt const * const frontEvt = me->eQueue.frontEvt;
188 me->eQueue.frontEvt = e; // deliver the event directly to the front
189
190 if (frontEvt != (QEvt *)0) { // was the queue NOT empty?
191 tmp = me->eQueue.tail; // get volatile into temporary
192 ++tmp;
193 if (tmp == me->eQueue.end) { // need to wrap the tail?
194 tmp = 0U; // wrap around
195 }
196 me->eQueue.tail = tmp;
197 me->eQueue.ring[tmp] = frontEvt;
198 }
199 else { // queue was empty
200 QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
201 }
202
203 QF_CRIT_EXIT();
204}
205
206//............................................................................
207//! @private @memberof QActive
208QEvt const * QActive_get_(QActive * const me) {
211
212 // wait for event to arrive directly (depends on QP port)
213 // NOTE: might use assertion-IDs 400-409
215
216 // always remove event from the front
217 QEvt const * const e = me->eQueue.frontEvt;
218 QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary
219
220 Q_REQUIRE_INCRIT(410, e != (QEvt *)0); // queue must NOT be empty
221
222 ++tmp; // one more free event in the queue
223
224 me->eQueue.nFree = tmp; // update the # free
225
226 if (tmp <= me->eQueue.end) { // any events in the ring buffer?
227
228 QS_BEGIN_PRE(QS_QF_ACTIVE_GET, me->prio)
229 QS_TIME_PRE(); // timestamp
230 QS_SIG_PRE(e->sig); // the signal of this event
231 QS_OBJ_PRE(me); // this active object
233 QS_EQC_PRE(tmp); // # free entries
234 QS_END_PRE()
235
236 // remove event from the tail
237 tmp = me->eQueue.tail; // get volatile into temporary
238 QEvt const * const frontEvt = me->eQueue.ring[tmp];
239
240 Q_ASSERT_INCRIT(430, frontEvt != (QEvt *)0);
241 me->eQueue.frontEvt = frontEvt; // update the original
242
243 if (tmp == 0U) { // need to wrap the tail?
244 tmp = me->eQueue.end;
245 }
246 --tmp; // advance the tail (counter-clockwise)
247
248 me->eQueue.tail = tmp; // update the original
249 }
250 else {
251 me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty
252
253 // all entries in the queue must be free (+1 for fronEvt)
254 Q_ASSERT_INCRIT(440, tmp == (me->eQueue.end + 1U));
255
256 QS_BEGIN_PRE(QS_QF_ACTIVE_GET_LAST, me->prio)
257 QS_TIME_PRE(); // timestamp
258 QS_SIG_PRE(e->sig); // the signal of this event
259 QS_OBJ_PRE(me); // this active object
261 QS_END_PRE()
262 }
263
264 QF_CRIT_EXIT();
265
266 return e;
267}
268
269//............................................................................
270//! @private @memberof QActive
271static void QActive_postFIFO_(QActive * const me,
272 QEvt const * const e,
273 void const * const sender)
274{
275 // NOTE: this helper function is called *inside* critical section
276#ifndef Q_SPY
277 Q_UNUSED_PAR(sender);
278#endif
279
280 QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary
281 --tmp; // one free entry just used up
282
283 me->eQueue.nFree = tmp; // update the original
284 if (me->eQueue.nMin > tmp) {
285 me->eQueue.nMin = tmp; // update minimum so far
286 }
287
288 QS_BEGIN_PRE(QS_QF_ACTIVE_POST, me->prio)
289 QS_TIME_PRE(); // timestamp
290 QS_OBJ_PRE(sender); // the sender object
291 QS_SIG_PRE(e->sig); // the signal of the event
292 QS_OBJ_PRE(me); // this active object (recipient)
294 QS_EQC_PRE(tmp); // # free entries
295 QS_EQC_PRE(me->eQueue.nMin); // min # free entries
296 QS_END_PRE()
297
298#ifdef Q_UTEST
299 if (QS_LOC_CHECK_(me->prio)) {
300 QF_CRIT_EXIT();
301 QS_onTestPost(sender, me, e, true); // QUTEst callback
303 }
304#endif // def Q_UTEST
305
306 if (me->eQueue.frontEvt == (QEvt *)0) { // is the queue empty?
307 me->eQueue.frontEvt = e; // deliver event directly
308
309#ifdef QXK_H_
310 if (me->super.state.act == Q_ACTION_CAST(0)) { // eXtended?
311 QXTHREAD_EQUEUE_SIGNAL_(me); // signal eXtended Thread
312 }
313 else {
314 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
315 }
316#else
317 QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object
318#endif // def QXK_H_
319 }
320 else { // queue was not empty, insert event into the ring-buffer
321 tmp = me->eQueue.head; // get volatile into temporary
322 me->eQueue.ring[tmp] = e; // insert e into buffer
323
324 if (tmp == 0U) { // need to wrap the head?
325 tmp = me->eQueue.end;
326 }
327 --tmp; // advance the head (counter-clockwise)
328
329 me->eQueue.head = tmp; // update the original
330 }
331}
332
333//............................................................................
334//! @static @public @memberof QActive
335uint_fast16_t QActive_getQueueMin(uint_fast8_t const prio) {
339 && (QActive_registry_[prio] != (QActive *)0));
340 uint_fast16_t const min =
341 (uint_fast16_t)QActive_registry_[prio]->eQueue.nMin;
342 QF_CRIT_EXIT();
343
344 return min;
345}
346
347//============================================================================
348//! @public @memberof QTicker
349void QTicker_ctor(QTicker * const me,
350 uint_fast8_t const tickRate)
351{
352 QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor
353
354 static struct QAsmVtable const vtable = { // QTicker virtual table
357 &QHsm_isIn_
358#ifdef Q_SPY
359 ,&QHsm_getStateHandler_
360#endif
361 };
362 me->super.super.vptr = &vtable; // hook the vptr
363
364 // reuse eQueue.head for tick-rate
365 me->super.eQueue.head = (QEQueueCtr)tickRate;
366}
367
368//............................................................................
369//! @private @memberof QTicker
371 QAsm * const me,
372 void const * const par,
373 uint_fast8_t const qsId)
374{
375 Q_UNUSED_PAR(me);
376 Q_UNUSED_PAR(par);
377 Q_UNUSED_PAR(qsId);
378
381
382 QACTIVE_CAST_(me)->eQueue.tail = 0U;
383
384 QF_CRIT_EXIT();
385}
386
387//............................................................................
388//! @private @memberof QTicker
390 QAsm * const me,
391 QEvt const * const e,
392 uint_fast8_t const qsId)
393{
394 Q_UNUSED_PAR(e);
395 Q_UNUSED_PAR(qsId);
396
399
400 // get volatile into temporaries
401 QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail;
402 QEQueueCtr const tickRate = QACTIVE_CAST_(me)->eQueue.head;
403
404 Q_REQUIRE_INCRIT(800, nTicks > 0U);
405
406 QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks
407
408 QF_CRIT_EXIT();
409
410 for (; nTicks > 0U; --nTicks) {
411 QTimeEvt_tick_((uint_fast8_t)tickRate, me);
412 }
413}
414
415//............................................................................
416//! @private @memberof QTicker
418 QActive * const me,
419 void const * const sender)
420{
421#ifndef Q_SPY
422 Q_UNUSED_PAR(sender);
423#endif
424
425 static QEvt const tickEvt = QEVT_INITIALIZER(0);
426
429
430 QEQueueCtr nTicks = me->eQueue.tail; // get volatile into temporary
431
432 if (me->eQueue.frontEvt == (QEvt *)0) { // no tick events?
434 (me->eQueue.nFree == 1U)
435 && (nTicks == 0U));
436
437 me->eQueue.frontEvt = &tickEvt; // deliver event directly
438 me->eQueue.nFree = 0U;
439
440 QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
441 }
442 else {
443 Q_REQUIRE_INCRIT(910, (nTicks > 0U) && (nTicks < 0xFFU));
444 Q_REQUIRE_INCRIT(920, me->eQueue.nFree == 0U);
445 }
446
447 ++nTicks; // account for one more tick event
448
449 me->eQueue.tail = nTicks; // update the original
450
451 QS_BEGIN_PRE(QS_QF_ACTIVE_POST, me->prio)
452 QS_TIME_PRE(); // timestamp
453 QS_OBJ_PRE(sender); // the sender object
454 QS_SIG_PRE(0U); // the signal of the event
455 QS_OBJ_PRE(me); // this active object
456 QS_2U8_PRE(0U, 0U); // poolNum & refCtr
457 QS_EQC_PRE(0U); // # free entries
458 QS_EQC_PRE(0U); // min # free entries
459 QS_END_PRE()
460
461 QF_CRIT_EXIT();
462}
uint16_t QEQueueCtr
Definition qequeue.h:39
#define QACTIVE_EQUEUE_SIGNAL_(me_)
Definition qk.h:98
#define QACTIVE_EQUEUE_WAIT_(me_)
Definition qk.h:97
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:94
#define QF_NO_MARGIN
Special value of margin that causes asserting failure in case event allocation or event posting fails...
Definition qp.h:745
#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:118
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64)
Definition qp_config.h:123
Internal (package scope) QP/C interface.
#define QACTIVE_CAST_(ptr_)
Definition qp_pkg.h:33
Sample QP/C port.
QS/C dummy public interface.
void QS_onTestPost(void const *sender, QActive *recipient, QEvt const *e, bool status)
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.h:149
#define QS_EQC_PRE(ctr_)
Definition qs_dummy.h:151
#define QS_SIG_PRE(sig_)
Definition qs_dummy.h:147
#define QS_TIME_PRE()
Definition qs_dummy.h:146
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.h:143
#define QS_END_PRE()
Definition qs_dummy.h:141
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.h:140
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
Active object class (based on the QHsm implementation strategy)
Definition qp.h:504
QAsm super
Definition qp.h:505
QEvt const * QActive_get_(QActive *const me)
Get an event from the event queue of an active object.
Definition qf_actq.c:208
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:130
void QActive_ctor(QActive *const me, QStateHandler const initial)
QActive constructor (abstract base class)
uint_fast16_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:335
QACTIVE_EQUEUE_TYPE eQueue
Port-dependent event-queue type (often QEQueue)
Definition qp.h:518
static void QActive_postFIFO_(QActive *const me, QEvt const *const e, void const *const sender)
Definition qf_actq.c:271
QActive * QActive_registry_[QF_MAX_ACTIVE+1U]
Static (one per-class) array of registered active objects.
Definition qp.h:523
uint8_t prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:506
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:221
Event class.
Definition qp.h:112
QSignal sig
Signal of the event (see Event Signal)
Definition qp.h:113
uint8_t volatile refCtr_
Event reference counter.
Definition qp.h:115
uint8_t poolNum_
Event pool number of this event.
Definition qp.h:114
"Ticker" Active Object class
Definition qp.h:694
void QTicker_init_(QAsm *const me, void const *const par, uint_fast8_t const qsId)
Definition qf_actq.c:370
QActive super
Definition qp.h:695
void QTicker_trig_(QActive *const me, void const *const sender)
Asynchronously trigger the QTicker active object to perform tick processing.
Definition qf_actq.c:417
void QTicker_dispatch_(QAsm *const me, QEvt const *const e, uint_fast8_t const qsId)
Definition qf_actq.c:389
void QTicker_ctor(QTicker *const me, uint_fast8_t const tickRate)
Constructor of the QTicker Active Object class.
Definition qf_actq.c:349
QStateHandler fun
Definition qp.h:165
QActionHandler act
Definition qp.h:166