QP/C++  8.0.3
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_time.cpp
Go to the documentation of this file.
1//============================================================================
2// SafeQP/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: LicenseRef-QL-commercial
11//
12// This software is licensed under the terms of the Quantum Leaps commercial
13// licenses. Please contact Quantum Leaps for more information about the
14// available licensing options.
15//
16// RESTRICTIONS
17// You may NOT :
18// (a) redistribute, encumber, sell, rent, lease, sublicense, or otherwise
19// transfer rights in this software,
20// (b) remove or alter any trademark, logo, copyright or other proprietary
21// notices, legends, symbols or labels present in this software,
22// (c) plagiarize this software to sidestep the licensing obligations.
23//
24// Quantum Leaps contact information:
25// <www.state-machine.com/licensing>
26// <info@state-machine.com>
27//============================================================================
28#define QP_IMPL // this is QP implementation
29#include "qp_port.hpp" // QP port
30#include "qp_pkg.hpp" // QP package-scope interface
31#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
32#ifdef Q_SPY // QS software tracing enabled?
33 #include "qs_port.hpp" // QS port
34 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
35#else
36 #include "qs_dummy.hpp" // disable the QS software tracing
37#endif // Q_SPY
38
39// unnamed namespace for local definitions with internal linkage
40namespace {
41Q_DEFINE_THIS_MODULE("qf_time")
42} // unnamed namespace
43
44namespace QP {
45
46//${QF::QTimeEvt} ............................................................
48
49//............................................................................
51 QActive * const act,
52 QSignal const sig,
53 std::uint_fast8_t const tickRate) noexcept
54 : QEvt(sig),
55 m_next(nullptr),
56 m_act(act),
57 m_ctr(0U),
58 m_interval(0U),
59 m_tickRate(0U),
60 m_flags(0U)
61{
64 Q_REQUIRE_INCRIT(300, (sig != 0U)
65 && (tickRate < QF_MAX_TICK_RATE));
67
68 refCtr_ = 0U; // adjust from the QEvt(sig) ctor
69}
70
71//............................................................................
73 std::uint32_t const nTicks,
74 std::uint32_t const interval) noexcept
75{
78
79 // dynamic range checks
80#if (QF_TIMEEVT_CTR_SIZE == 1U)
81 Q_REQUIRE_INCRIT(400, (nTicks < 0xFFU) && (interval < 0xFFU));
82#elif (QF_TIMEEVT_CTR_SIZE == 2U)
83 Q_REQUIRE_INCRIT(400, (nTicks < 0xFFFFU) && (interval < 0xFFFFU));
84#endif
85
86 QTimeEvtCtr const ctr = m_ctr;
87 std::uint8_t const tickRate = m_tickRate;
88#ifdef Q_SPY
89 std::uint_fast8_t const qsId =
90 static_cast<QActive const *>(m_act)->m_prio;
91#endif // def Q_SPY
92
94 (nTicks != 0U)
95 && (ctr == 0U)
96 && (m_act != nullptr)
97 && (tickRate < QF_MAX_TICK_RATE));
98
99#ifdef Q_UNSAFE
100 Q_UNUSED_PAR(ctr);
101#endif // ndef Q_UNSAFE
102
103 m_ctr = static_cast<QTimeEvtCtr>(nTicks);
104 m_interval = static_cast<QTimeEvtCtr>(interval);
105
106 // is the time event unlinked?
107 // NOTE: For the duration of a single clock tick of the specified tick
108 // rate a time event can be disarmed and yet still linked into the list
109 // because un-linking is performed exclusively in the QTimeEvt::tick().
110 if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) {
111 m_flags |= QTE_FLAG_IS_LINKED; // mark as linked
112
113 // The time event is initially inserted into the separate
114 // "freshly armed" list based on timeEvtHead_[tickRate].act.
115 // Only later, inside QTimeEvt::tick(), the "freshly armed"
116 // list is appended to the main list of armed time events based on
117 // timeEvtHead_[tickRate].next. Again, this is to keep any
118 // changes to the main list exclusively inside QTimeEvt::tick().
119 m_next = timeEvtHead_[tickRate].toTimeEvt();
120 timeEvtHead_[tickRate].m_act = this;
121 }
122
123 QS_BEGIN_PRE(QS_QF_TIMEEVT_ARM, qsId)
124 QS_TIME_PRE(); // timestamp
125 QS_OBJ_PRE(this); // this time event object
126 QS_OBJ_PRE(m_act); // the active object
127 QS_TEC_PRE(nTicks); // the # ticks
128 QS_TEC_PRE(interval); // the interval
129 QS_U8_PRE(tickRate); // tick rate
130 QS_END_PRE()
131
132 QF_CRIT_EXIT();
133}
134
135//............................................................................
136bool QTimeEvt::disarm() noexcept {
139
140 QTimeEvtCtr const ctr = m_ctr;
141
142#ifdef Q_SPY
143 std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio;
144#endif
145
146 // was the time event actually armed?
147 bool wasArmed = false;
148 if (ctr != 0U) {
149 wasArmed = true;
151 m_ctr = 0U; // schedule removal from the list
152
153 QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM, qsId)
154 QS_TIME_PRE(); // timestamp
155 QS_OBJ_PRE(this); // this time event object
156 QS_OBJ_PRE(m_act); // the target AO
157 QS_TEC_PRE(ctr); // the # ticks
158 QS_TEC_PRE(m_interval); // the interval
159 QS_U8_PRE(m_tickRate); // tick rate
160 QS_END_PRE()
161 }
162 else { // the time event was already disarmed automatically
163 m_flags &= static_cast<std::uint8_t>(~QTE_FLAG_WAS_DISARMED);
164
165 QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM_ATTEMPT, qsId)
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_U8_PRE(m_tickRate); // tick rate
170 QS_END_PRE()
171 }
172
173 QF_CRIT_EXIT();
174
175 return wasArmed;
176}
177
178//............................................................................
179bool QTimeEvt::rearm(std::uint32_t const nTicks) noexcept {
182
183 // dynamic range checks
184#if (QF_TIMEEVT_CTR_SIZE == 1U)
185 Q_REQUIRE_INCRIT(600, nTicks < 0xFFU);
186#elif (QF_TIMEEVT_CTR_SIZE == 2U)
187 Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU);
188#endif
189
190 std::uint8_t const tickRate = m_tickRate;
191 QTimeEvtCtr const ctr = m_ctr;
192
194 (nTicks != 0U)
195 && (m_act != nullptr)
196 && (tickRate < QF_MAX_TICK_RATE));
197
198#ifdef Q_SPY
199 std::uint_fast8_t const qsId = static_cast<QActive *>(m_act)->m_prio;
200#endif
201
202 m_ctr = static_cast<QTimeEvtCtr>(nTicks);
203
204 // was the time evt not running?
205 bool wasArmed = false;
206 if (ctr == 0U) {
207 // NOTE: For the duration of a single clock tick of the specified
208 // tick rate a time event can be disarmed and yet still linked into
209 // the list, because unlinking is performed exclusively in the
210 // QTimeEvt::tick() function.
211
212 // is the time event unlinked?
213 if ((m_flags & QTE_FLAG_IS_LINKED) == 0U) {
214 m_flags |= QTE_FLAG_IS_LINKED; // mark as linked
215
216 // The time event is initially inserted into the separate
217 // "freshly armed" list based on timeEvtHead_[tickRate].act.
218 // Only later, inside QTimeEvt::tick(), the "freshly armed"
219 // list is appended to the main list of armed time events based on
220 // timeEvtHead_[tickRate].next. Again, this is to keep any
221 // changes to the main list exclusively inside QTimeEvt::tick().
222 m_next = timeEvtHead_[tickRate].toTimeEvt();
223 timeEvtHead_[tickRate].m_act = this;
224 }
225 }
226 else { // the time event was armed
227 wasArmed = true;
228 }
229
230 QS_BEGIN_PRE(QS_QF_TIMEEVT_REARM, qsId)
231 QS_TIME_PRE(); // timestamp
232 QS_OBJ_PRE(this); // this time event object
233 QS_OBJ_PRE(m_act); // the target AO
234 QS_TEC_PRE(nTicks); // the # ticks
235 QS_TEC_PRE(m_interval); // the interval
236 QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U));
237 QS_END_PRE()
238
239 QF_CRIT_EXIT();
240
241 return wasArmed;
242}
243
244//............................................................................
245bool QTimeEvt::wasDisarmed() noexcept {
248
249 bool const wasDisarmed = (m_flags & QTE_FLAG_WAS_DISARMED) != 0U;
251
252 QF_CRIT_EXIT();
253
254 return wasDisarmed;
255}
256
257//............................................................................
259 std::uint_fast8_t const tickRate,
260 void const * const sender) noexcept
261{
262#ifndef Q_SPY
263 Q_UNUSED_PAR(sender);
264#endif
265
268
269 Q_REQUIRE_INCRIT(800, tickRate < Q_DIM(timeEvtHead_));
270
271 QTimeEvt *prev = &timeEvtHead_[tickRate];
272
273#ifdef Q_SPY
274 QS_BEGIN_PRE(QS_QF_TICK, 0U)
275 prev->m_ctr = (prev->m_ctr + 1U);
276 QS_TEC_PRE(prev->m_ctr); // tick ctr
277 QS_U8_PRE(tickRate); // tick rate
278 QS_END_PRE()
279#endif // def Q_SPY
280
281 // scan the linked-list of time events at this rate...
282 while (true) {
283 Q_ASSERT_INCRIT(810, prev != nullptr); // sanity check
284
285 QTimeEvt *te = prev->m_next; // advance down the time evt. list
286
287 if (te == nullptr) { // end of the list?
288 // NO any new time events armed since the last QTimeEvt_tick_()?
289 if (timeEvtHead_[tickRate].m_act == nullptr) {
290 break; // terminate the while-loop
291 }
292
293 prev->m_next = timeEvtHead_[tickRate].toTimeEvt();
294 timeEvtHead_[tickRate].m_act = nullptr;
295
296 te = prev->m_next; // switch to the new list
297 }
298
299 // the time event 'te' must be valid
300 Q_ASSERT_INCRIT(840, te != nullptr);
301
302 QTimeEvtCtr ctr = te->m_ctr;
303
304 if (ctr == 0U) { // time event scheduled for removal?
305 prev->m_next = te->m_next;
306
307 // mark time event 'te' as NOT linked
308 te->m_flags &= static_cast<std::uint8_t>(~QTE_FLAG_IS_LINKED);
309 // do NOT advance the prev pointer
310 QF_CRIT_EXIT(); // exit crit. section to reduce latency
311 }
312 else if (ctr == 1U) { // is time event about to expire?
313 QActive * const act = te->toActive();
314 prev = te->expire_(prev, act, tickRate);
315
316#ifdef QXK_HPP_
317 if (te->sig < Q_USER_SIG) {
318 QXThread::timeout_(act);
319 QF_CRIT_EXIT();
320 }
321 else {
322 QF_CRIT_EXIT(); // exit crit. section before posting
323
324 // act->POST() asserts if the queue overflows
325 act->POST(te, sender);
326 }
327#else // not QXK
328 QF_CRIT_EXIT(); // exit crit. section before posting
329
330 // act->POST() asserts if the queue overflows
331 act->POST(te, sender);
332#endif
333 }
334 else { // time event keeps timing out
335 --ctr; // decrement the tick counter
336 te->m_ctr = ctr; // update the original
337
338 prev = te; // advance to this time event
339 QF_CRIT_EXIT(); // exit crit. section to reduce latency
340 }
341 QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop
342 }
343 QF_CRIT_EXIT();
344}
345
346//............................................................................
347bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept {
348 // NOTE: this function must be called *inside* critical section
349 Q_REQUIRE_INCRIT(900, tickRate < QF_MAX_TICK_RATE);
350
351 bool inactive = false;
352
353 if (timeEvtHead_[tickRate].m_next != nullptr) {
354 // empty
355 }
356 else if (timeEvtHead_[tickRate].m_act != nullptr) {
357 // empty
358 }
359 else {
360 inactive = true;
361 }
362
363 return inactive;
364}
365
366//............................................................................
368 : QEvt(0U),
369 m_next(nullptr),
370 m_act(nullptr),
371 m_ctr(0U),
372 m_interval(0U),
373 m_tickRate(0U),
374 m_flags(0U)
375{}
376
377//............................................................................
379 QTimeEvt * const prev_link,
380 QActive const * const act,
381 std::uint_fast8_t const tickRate) noexcept
382{
383 // NOTE: this helper function is called *inside* critical section
384#ifndef Q_SPY
385 Q_UNUSED_PAR(act);
386 Q_UNUSED_PAR(tickRate);
387#endif
388
389 QTimeEvt *prev = prev_link;
390 if (m_interval != 0U) { // periodic time evt?
391 m_ctr = m_interval; // rearm the time event
392 prev = this; // advance to this time event
393 }
394 else { // one-shot time event: automatically disarm
395 m_ctr = 0U;
396 prev->m_next = m_next;
397
398 // mark this time event as NOT linked
399 m_flags &=
400 static_cast<std::uint8_t>(~QTE_FLAG_IS_LINKED);
401 // do NOT advance the prev pointer
402
403 QS_BEGIN_PRE(QS_QF_TIMEEVT_AUTO_DISARM, act->m_prio)
404 QS_OBJ_PRE(this); // this time event object
405 QS_OBJ_PRE(act); // the target AO
406 QS_U8_PRE(tickRate); // tick rate
407 QS_END_PRE()
408 }
409
410 QS_BEGIN_PRE(QS_QF_TIMEEVT_POST, act->m_prio)
411 QS_TIME_PRE(); // timestamp
412 QS_OBJ_PRE(this); // the time event object
413 QS_SIG_PRE(sig); // signal of this time event
414 QS_OBJ_PRE(act); // the target AO
415 QS_U8_PRE(tickRate); // tick rate
416 QS_END_PRE()
417
418 return prev;
419}
420
421} // namespace QP
Active object class (based on the QHsm implementation strategy)
Definition qp.hpp:563
QSignal sig
Signal of the event (see Event Signal)
Definition qp.hpp:117
std::uint8_t volatile refCtr_
Event reference counter.
Definition qp.hpp:119
constexpr QEvt(QSignal const s) noexcept
Event constexpr constructor applicable to immutable and mutable event instances.
Definition qp.hpp:125
Time Event class.
Definition qp.hpp:772
static void tick(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Definition qf_time.cpp:258
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]
Definition qp.hpp:782
bool disarm() noexcept
Definition qf_time.cpp:136
std::uint8_t m_flags
Definition qp.hpp:779
QTimeEvt() noexcept
Definition qf_time.cpp:367
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:50
static bool noActive(std::uint_fast8_t const tickRate) noexcept
Definition qf_time.cpp:347
void * m_act
Active object that receives the time events.
Definition qp.hpp:775
QTimeEvt *volatile m_next
Link to the next time event in the list.
Definition qp.hpp:774
QActive * toActive() noexcept
Definition qp.hpp:827
std::uint8_t m_tickRate
Definition qp.hpp:778
void armX(std::uint32_t const nTicks, std::uint32_t const interval=0U) noexcept
Definition qf_time.cpp:72
bool rearm(std::uint32_t const nTicks) noexcept
Definition qf_time.cpp:179
QTimeEvtCtr volatile m_ctr
Down-counter of the time event.
Definition qp.hpp:776
QTimeEvt * expire_(QTimeEvt *const prev_link, QActive const *const act, std::uint_fast8_t const tickRate) noexcept
Definition qf_time.cpp:378
QTimeEvtCtr m_interval
Interval for periodic time event (zero for one-shot time event)
Definition qp.hpp:777
bool wasDisarmed() noexcept
Definition qf_time.cpp:245
QP/C++ framework.
Definition qequeue.hpp:36
constexpr enum_t Q_USER_SIG
Definition qp.hpp:175
std::uint32_t QTimeEvtCtr
Data type to store the block-size defined based on the macro QF_TIMEEVT_CTR_SIZE.
Definition qp.hpp:463
constexpr std::uint8_t QTE_FLAG_WAS_DISARMED
Definition qp_pkg.hpp:63
std::uint16_t QSignal
The signal of event QP::QEvt.
Definition qp.hpp:108
constexpr std::uint8_t QTE_FLAG_IS_LINKED
Definition qp_pkg.hpp:62
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:94
#define Q_DIM(array_)
Definition qp.hpp:95
#define QF_MAX_TICK_RATE
Maximum # clock tick rates in the system (0..15)
Internal (package scope) QP/C++ interface.
Sample QP/C++ port.
QS/C++ dummy public interface.
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.hpp:157
#define QS_TEC_PRE(ctr_)
Definition qs_dummy.hpp:162
#define QS_SIG_PRE(sig_)
Definition qs_dummy.hpp:155
#define QS_TIME_PRE()
Definition qs_dummy.hpp:154
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.hpp:151
#define QS_U8_PRE(data_)
Definition qs_dummy.hpp:150
#define QS_END_PRE()
Definition qs_dummy.hpp:149
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.hpp:148
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 QF_CRIT_STAT
Definition qsafe.h:35