QP/C++ 8.1.1
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_time.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#if (QF_MAX_TICK_RATE > 0U)
42
43// unnamed namespace for local definitions with internal linkage
44namespace {
45Q_DEFINE_THIS_MODULE("qf_time")
46} // unnamed namespace
47
48namespace QP {
49
50//............................................................................
51std::array<QTimeEvt, QF_MAX_TICK_RATE> QTimeEvt_head_;
52
53//............................................................................
55 QActive * const act,
56 QSignal const sig,
57 std::uint_fast8_t const tickRate) noexcept
58 : QEvt(sig), // signal for this time event
59 m_next(nullptr), // empty next link
60 m_act(act), // associated AO
61 m_ctr(0U), // time event disarmed
62 m_interval(0U), // not periodic
63 m_tickRate(static_cast<std::uint8_t>(tickRate)), // associated tickRate
64 m_flags(0U) // not armed
65{
68
69 // the signal must be != 0, but other reserved signals are allowed
70 Q_REQUIRE_INCRIT(300, sig != 0U);
71
72 // the tick rate must be in the configured range
73 Q_REQUIRE_INCRIT(310, tickRate < QF_MAX_TICK_RATE);
75
76 refCtr_ = 0U; // adjust from the QEvt(sig) ctor
77}
78
79//............................................................................
81 std::uint32_t const nTicks,
82 std::uint32_t const interval) noexcept
83{
86
87 // nTicks and interval parameters must fit in the configured dynamic range
88#if (QF_TIMEEVT_CTR_SIZE == 1U)
89 Q_REQUIRE_INCRIT(400, nTicks < 0xFFU);
90 Q_REQUIRE_INCRIT(410, interval < 0xFFU);
91#elif (QF_TIMEEVT_CTR_SIZE == 2U)
92 Q_REQUIRE_INCRIT(400, nTicks < 0xFFFFU);
93 Q_REQUIRE_INCRIT(410, interval < 0xFFFFU);
94#endif
95
96#ifndef Q_UNSAFE
97 QTimeEvtCtr const ctr = m_ctr;
98#endif
99
100 std::uint8_t const tickRate = m_tickRate;
101
102 // nTicks must be != 0 for arming a time event
103 Q_REQUIRE_INCRIT(440, nTicks != 0U);
104
105 // the time event must not be already armed
106 Q_REQUIRE_INCRIT(450, ctr == 0U);
107
108 // the AO associated with this time event must be valid
109 Q_REQUIRE_INCRIT(460, m_act != nullptr);
110
111 // the tick rate of this time event must be in range
112 Q_REQUIRE_INCRIT(470, tickRate < QF_MAX_TICK_RATE);
113
114 m_ctr = static_cast<QTimeEvtCtr>(nTicks);
115 m_interval = static_cast<QTimeEvtCtr>(interval);
116
117 // is the time event unlinked?
118 // NOTE: For the duration of a single clock tick of the specified tick
119 // rate a time event can be disarmed and yet still linked into the list
120 // because un-linking is performed exclusively in the QTimeEvt::tick().
121 if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) {
122 // The time event is initially inserted into the separate
123 // "freshly armed" list based on QTimeEvt_head_[tickRate].act.
124 // Only later, inside QTimeEvt::tick(), the "freshly armed"
125 // list is appended to the main list of armed time events based on
126 // QTimeEvt_head_[tickRate].next. Again, this is to keep any
127 // changes to the main list exclusively inside QTimeEvt::tick().
128
129 m_flags |= QTE_FLAG_IS_LINKED; // mark as linked
130 m_next = QTimeEvt_head_[tickRate].toTimeEvt();
131 QTimeEvt_head_[tickRate].m_act = this;
132 }
133
135 static_cast<QActive const *>(m_act)->m_prio)
136 QS_TIME_PRE(); // timestamp
137 QS_OBJ_PRE(this); // this time event object
138 QS_OBJ_PRE(m_act); // the active object
139 QS_TEC_PRE(nTicks); // the # ticks
140 QS_TEC_PRE(interval); // the interval
141 QS_U8_PRE(tickRate); // tick rate
142 QS_END_PRE()
143
144 QF_CRIT_EXIT();
145}
146
147//............................................................................
148bool QTimeEvt::disarm() noexcept {
151
152 QTimeEvtCtr const ctr = m_ctr; // get member into temporary
153
154#ifdef Q_SPY
155 std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio;
156#endif
157
158 // was the time event actually armed?
159 bool wasArmed = false;
160 if (ctr != 0U) {
161 wasArmed = true;
163 m_ctr = 0U; // schedule removal from the list
164
166 QS_TIME_PRE(); // timestamp
167 QS_OBJ_PRE(this); // this time event object
168 QS_OBJ_PRE(m_act); // the target AO
169 QS_TEC_PRE(ctr); // the # ticks
170 QS_TEC_PRE(m_interval); // the interval
171 QS_U8_PRE(m_tickRate); // tick rate
172 QS_END_PRE()
173 }
174 else { // the time event was already disarmed automatically
175 m_flags &= static_cast<std::uint8_t>(~QTE_FLAG_WAS_DISARMED);
176
178 QS_TIME_PRE(); // timestamp
179 QS_OBJ_PRE(this); // this time event object
180 QS_OBJ_PRE(m_act); // the target AO
181 QS_U8_PRE(m_tickRate); // tick rate
182 QS_END_PRE()
183 }
184
185 QF_CRIT_EXIT();
186
187 return wasArmed;
188}
189
190//............................................................................
191bool QTimeEvt::rearm(std::uint32_t const nTicks) noexcept {
194
195 // nTicks parameter must fit in the configured dynamic range
196#if (QF_TIMEEVT_CTR_SIZE == 1U)
197 Q_REQUIRE_INCRIT(600, nTicks < 0xFFU);
198#elif (QF_TIMEEVT_CTR_SIZE == 2U)
199 Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU);
200#endif
201
202 std::uint8_t const tickRate = m_tickRate;
203 QTimeEvtCtr const ctr = m_ctr;
204
205 // nTicks must be != 0 for arming a time event
206 Q_REQUIRE_INCRIT(610, nTicks != 0U);
207
208 // the AO associated with this time event must be valid
209 Q_REQUIRE_INCRIT(620, m_act != nullptr);
210
211 // the tick rate of this time event must be in range
212 Q_REQUIRE_INCRIT(630, tickRate < QF_MAX_TICK_RATE);
213
214#ifdef Q_SPY
215 std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio;
216#endif
217
218 m_ctr = static_cast<QTimeEvtCtr>(nTicks);
219
220 // was the time evt not running?
221 bool wasArmed = false;
222 if (ctr == 0U) {
223 // NOTE: For the duration of a single clock tick of the specified
224 // tick rate a time event can be disarmed and yet still linked into
225 // the list, because unlinking is performed exclusively in the
226 // QTimeEvt::tick() function.
227
228 // is the time event unlinked?
229 if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) {
230 // The time event is initially inserted into the separate
231 // "freshly armed" list based on QTimeEvt_head_[tickRate].act.
232 // Only later, inside QTimeEvt::tick(), the "freshly armed"
233 // list is appended to the main list of armed time events based on
234 // QTimeEvt_head_[tickRate].next. Again, this is to keep any
235 // changes to the main list exclusively inside QTimeEvt::tick().
236
237 m_flags |= QTE_FLAG_IS_LINKED; // mark as linked
238 m_next = QTimeEvt_head_[tickRate].toTimeEvt();
239 QTimeEvt_head_[tickRate].m_act = this;
240 }
241 }
242 else { // the time event was armed
243 wasArmed = true;
244 }
245
247 QS_TIME_PRE(); // timestamp
248 QS_OBJ_PRE(this); // this time event object
249 QS_OBJ_PRE(m_act); // the target AO
250 QS_TEC_PRE(nTicks); // the # ticks
251 QS_TEC_PRE(m_interval); // the interval
252 QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U));
253 QS_END_PRE()
254
255 QF_CRIT_EXIT();
256
257 return wasArmed;
258}
259
260//............................................................................
261bool QTimeEvt::wasDisarmed() noexcept {
264
265 // was this time-event disarmed automatically upon expiration?
266 bool const wasDisarmed = (m_flags & QTE_FLAG_WAS_DISARMED) != 0U;
267
268 m_flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed (SIDE EFFECT!)
269
270 QF_CRIT_EXIT();
271
272 return wasDisarmed;
273}
274
275//............................................................................
277 std::uint_fast8_t const tickRate,
278 void const * const sender) noexcept
279{
280#ifndef Q_SPY
281 Q_UNUSED_PAR(sender);
282#endif
283
286
287 // the tick rate of this time event must be in range
288 Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE);
289
290 QTimeEvt *prev = &QTimeEvt_head_[tickRate];
291
292#ifdef Q_SPY
294 ++prev->m_ctr;
295 QS_TEC_PRE(prev->m_ctr); // tick ctr
296 QS_U8_PRE(tickRate); // tick rate
297 QS_END_PRE()
298#endif
299
300 // scan the linked-list of time events at this tick rate...
301 for (;;) {
302 QTimeEvt *te = prev->m_next; // advance down the time evt. list
303
304 if (te == nullptr) { // end of the list?
305 // set 'te' to the the newly-armed linked list
306 te = QTimeEvt_head_[tickRate].toTimeEvt();
307 if (te == nullptr) { // no newly-armed time events?
308 break; // terminate the loop
309 }
310
311 prev->m_next = te;
312 QTimeEvt_head_[tickRate].m_act = nullptr;
313 }
314
315 QTimeEvtCtr ctr = te->m_ctr; // get member into temporary
316
317 if (ctr == 0U) { // time event scheduled for removal?
318 prev->m_next = te->m_next;
319
320 // mark time event 'te' as NOT linked
321 te->m_flags &= static_cast<std::uint8_t>(~QTE_FLAG_IS_LINKED);
322 // do NOT advance the prev pointer
323 QF_CRIT_EXIT(); // exit crit. section to reduce latency
324 }
325 else if (ctr == 1U) { // is time event about to expire?
326 QActive * const act = te->toActive();
327 prev = te->expire_(prev, act, tickRate);
328
329#ifdef QXK_HPP_
330 if (te->sig < Q_USER_SIG) {
332 QF_CRIT_EXIT();
333 }
334 else {
335 QF_CRIT_EXIT(); // exit crit. section before posting
336
337 // act->POST() asserts if the queue overflows
338 act->POST(te, sender);
339 }
340#else // not QXK
341 QF_CRIT_EXIT(); // exit crit. section before posting
342
343 // act->POST() asserts if the queue overflows
344 act->POST(te, sender);
345#endif
346 }
347 else { // time event keeps timing out
348 --ctr; // decrement the tick counter
349 te->m_ctr = ctr; // update the member original
350
351 prev = te; // advance to this time event
352 QF_CRIT_EXIT(); // exit crit. section to reduce latency
353 }
354 QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop
355 }
356 QF_CRIT_EXIT();
357}
358
359//............................................................................
360bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept {
361 // NOTE: this function must be called *inside* critical section
362 Q_REQUIRE_INCRIT(900, tickRate < QF_MAX_TICK_RATE);
363
364 bool const noActive = (QTimeEvt_head_[tickRate].m_next == nullptr);
365
366 return noActive;
367}
368
369//............................................................................
370// private default ctor
372 : QEvt(0U), // signal for this time event
373 m_next(nullptr), // time event not linked
374 m_act(nullptr), // not associated with any AO
375 m_ctr(0U), // time event disarmed
376 m_interval(0U), // no periodic operation
377 m_tickRate(0U), // default tick rate
378 m_flags(0U) // not armed
379{}
380
381//............................................................................
383 QTimeEvt * const prev_link,
384 QActive const * const act,
385 std::uint_fast8_t const tickRate) noexcept
386{
387 // NOTE: this helper function is called *inside* critical section
388#ifndef Q_SPY
389 Q_UNUSED_PAR(act);
390 Q_UNUSED_PAR(tickRate);
391#endif
392
393 QTimeEvt *prev = prev_link;
394 if (m_interval != 0U) { // periodic time evt?
395 m_ctr = m_interval; // rearm the time event
396 prev = this; // advance to this time event
397 }
398 else { // one-shot time event: automatically disarm
399 m_ctr = 0U;
400 prev->m_next = m_next;
401
402 // mark this time event as NOT linked
403 m_flags &= static_cast<std::uint8_t>(~QTE_FLAG_IS_LINKED);
404 // do NOT advance the prev pointer
405
407 QS_OBJ_PRE(this); // this time event object
408 QS_OBJ_PRE(act); // the target AO
409 QS_U8_PRE(tickRate); // tick rate
410 QS_END_PRE()
411 }
412
413 QS_BEGIN_PRE(QS_QF_TIMEEVT_POST, act->m_prio)
414 QS_TIME_PRE(); // timestamp
415 QS_OBJ_PRE(this); // the time event object
416 QS_SIG_PRE(sig); // signal of this time event
417 QS_OBJ_PRE(act); // the target AO
418 QS_U8_PRE(tickRate); // tick rate
419 QS_END_PRE()
420
421 return prev;
422}
423
424} // namespace QP
425
426#endif // (QF_MAX_TICK_RATE > 0U)
Active object class (based on the QP::QHsm implementation strategy).
Definition qp.hpp:488
constexpr QEvt(QSignal const s) noexcept
Event constexpr constructor applicable to immutable and mutable event instances.
Definition qp.hpp:110
std::uint32_t refCtr_
Event reference counter.
Definition qp.hpp:105
std::uint32_t sig
Signal of the event (see Event Signal).
Definition qp.hpp:103
static void tick(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Processes all armed time events at every clock tick.
Definition qf_time.cpp:276
QTimeEvt * m_next
Link to the next time event in the list.
Definition qp.hpp:662
bool disarm() noexcept
Disarm a time event.
Definition qf_time.cpp:148
std::uint8_t m_flags
Definition qp.hpp:667
QTimeEvt() noexcept
Definition qf_time.cpp:371
QTimeEvt(QActive *const act, QSignal const sig, std::uint_fast8_t const tickRate=0U) noexcept
The "extended" constructor to initialize a Time Event.
Definition qf_time.cpp:54
static bool noActive(std::uint_fast8_t const tickRate) noexcept
Check if any time events are active at a given clock tick rate.
Definition qf_time.cpp:360
void * m_act
Active object that receives the time events.
Definition qp.hpp:663
QActive * toActive() noexcept
Definition qp.hpp:713
std::uint8_t m_tickRate
Definition qp.hpp:666
void armX(std::uint32_t const nTicks, std::uint32_t const interval=0U) noexcept
Arm a time event (extended version for one shot or periodic time event).
Definition qf_time.cpp:80
bool rearm(std::uint32_t const nTicks) noexcept
Rearm a time event.
Definition qf_time.cpp:191
QTimeEvt * expire_(QTimeEvt *const prev_link, QActive const *const act, std::uint_fast8_t const tickRate) noexcept
Definition qf_time.cpp:382
QTimeEvtCtr m_ctr
Down-counter of the time event.
Definition qp.hpp:664
QTimeEvtCtr m_interval
Interval for periodic time event (zero for one-shot time event).
Definition qp.hpp:665
bool wasDisarmed() noexcept
Check the "was disarmed" status of a time event.
Definition qf_time.cpp:261
static void timeout_(QActive *const act)
Process timeout in QXThread (either delay or during blocking).
Definition qxk_xthr.cpp:260
QP/C++ Framework namespace.
Definition qequeue.hpp:36
constexpr QSignal Q_USER_SIG
Definition qp.hpp:164
std::uint32_t QTimeEvtCtr
Data type to store the block-size defined based on the macro QF_TIMEEVT_CTR_SIZE.
Definition qp.hpp:427
constexpr std::uint8_t QTE_FLAG_WAS_DISARMED
Definition qp_pkg.hpp:47
@ QS_QF_TICK
QTimeEvt tick was called.
Definition qs.hpp:97
@ QS_QF_TIMEEVT_ARM
a time event was armed
Definition qs.hpp:100
@ QS_QF_TIMEEVT_POST
a time event posted itself directly to an AO
Definition qs.hpp:105
@ QS_QF_TIMEEVT_AUTO_DISARM
a time event expired and was disarmed
Definition qs.hpp:101
@ QS_QF_TIMEEVT_REARM
rearming of a time event
Definition qs.hpp:104
@ QS_QF_TIMEEVT_DISARM
true disarming of an armed time event
Definition qs.hpp:103
@ QS_QF_TIMEEVT_DISARM_ATTEMPT
attempt to disarm a disarmed QTimeEvt
Definition qs.hpp:102
std::uint16_t QSignal
The signal of event QP::QEvt.
Definition qp.hpp:98
std::array< QTimeEvt, QF_MAX_TICK_RATE > QTimeEvt_head_
Internal heads of the time-event linked-lists (by clock-tick rate).
Definition qf_time.cpp:51
constexpr std::uint8_t QTE_FLAG_IS_LINKED
Definition qp_pkg.hpp:46
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:89
#define QF_MAX_TICK_RATE
Maximum # clock tick rates in the system (0..15).
QP Framework in C++ internal (package-scope) interface
Sample QP/C++ port.
#define QS_TIME_PRE()
Definition qs.hpp:362
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_OBJ_PRE(obj_)
Output pre-formatted object pointer element.
Definition qs_pkg.hpp:89
#define QS_TEC_PRE(ctr_)
Output pre-formatted time event counter data element.
Definition qs_pkg.hpp:159
#define QS_SIG_PRE(sig_)
Output pre-formatted event signal data element.
Definition qs_pkg.hpp:90
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.hpp:81
#define QS_U8_PRE(data_)
Output pre-formatted unsigned 8-bit integer data element.
Definition qs_pkg.hpp:79
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.hpp:77
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.hpp:71
Sample QS/C++ port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#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