QP/C++  6.9.3
Real-Time Embedded Framework
qf_time.cpp
Go to the documentation of this file.
1 
39 #define QP_IMPL // this is QP implementation
40 #include "qf_port.hpp" // QF port
41 #include "qf_pkg.hpp" // QF package-scope interface
42 #include "qassert.h" // QP embedded systems-friendly assertions
43 #ifdef Q_SPY // QS software tracing enabled?
44  #include "qs_port.hpp" // QS port
45  #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
46 #else
47  #include "qs_dummy.hpp" // disable the QS software tracing
48 #endif // Q_SPY
49 
50 namespace QP {
51 
52 Q_DEFINE_THIS_MODULE("qf_time")
53 
54 // Package-scope objects *****************************************************
55 QTimeEvt QF::timeEvtHead_[QF_MAX_TICK_RATE]; // heads of time event lists
56 
57 #ifdef Q_SPY
58 //****************************************************************************
78 void QF::tickX_(std::uint_fast8_t const tickRate,
79  void const * const sender) noexcept
80 #else
81 void QF::tickX_(std::uint_fast8_t const tickRate) noexcept
82 #endif
83 {
84  QTimeEvt *prev = &timeEvtHead_[tickRate];
86 
87  QF_CRIT_E_();
88 
90  ++prev->m_ctr;
91  QS_TEC_PRE_(prev->m_ctr); // tick ctr
92  QS_U8_PRE_(tickRate); // tick rate
94 
95  // scan the linked-list of time events at this rate...
96  for (;;) {
97  QTimeEvt *t = prev->m_next; // advance down the time evt. list
98 
99  // end of the list?
100  if (t == nullptr) {
101 
102  // any new time events armed since the last run of QF::tickX_()?
103  if (timeEvtHead_[tickRate].m_act != nullptr) {
104 
105  // sanity check
106  Q_ASSERT_CRIT_(110, prev != nullptr);
107  prev->m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
108  timeEvtHead_[tickRate].m_act = nullptr;
109  t = prev->m_next; // switch to the new list
110  }
111  else {
112  break; // all currently armed time evts. processed
113  }
114  }
115 
116  // time event scheduled for removal?
117  if (t->m_ctr == 0U) {
118  prev->m_next = t->m_next;
119  // mark time event 't' as NOT linked
120  t->refCtr_ &= static_cast<std::uint8_t>(~TE_IS_LINKED);
121  // do NOT advance the prev pointer
122  QF_CRIT_X_(); // exit crit. section to reduce latency
123 
124  // prevent merging critical sections, see NOTE1 below
126  }
127  else {
128  --t->m_ctr;
129 
130  // is time evt about to expire?
131  if (t->m_ctr == 0U) {
132  QActive * const act = t->toActive(); // temporary for volatile
133 
134  // periodic time evt?
135  if (t->m_interval != 0U) {
136  t->m_ctr = t->m_interval; // rearm the time event
137  prev = t; // advance to this time event
138  }
139  // one-shot time event: automatically disarm
140  else {
141  prev->m_next = t->m_next;
142 
143  // mark time event 't' as NOT linked
144  t->refCtr_ &= static_cast<std::uint8_t>(~TE_IS_LINKED);
145  // do NOT advance the prev pointer
146 
148  act->m_prio)
149  QS_OBJ_PRE_(t); // this time event object
150  QS_OBJ_PRE_(act); // the target AO
151  QS_U8_PRE_(tickRate); // tick rate
153  }
154 
156  QS_TIME_PRE_(); // timestamp
157  QS_OBJ_PRE_(t); // the time event object
158  QS_SIG_PRE_(t->sig); // signal of this time event
159  QS_OBJ_PRE_(act); // the target AO
160  QS_U8_PRE_(tickRate); // tick rate
162 
163  QF_CRIT_X_(); // exit crit. section before posting
164 
165  // asserts if queue overflows
166  static_cast<void>(act->POST(t, sender));
167  }
168  else {
169  prev = t; // advance to this time event
170  QF_CRIT_X_(); // exit crit. section to reduce latency
171 
172  // prevent merging critical sections, see NOTE1 below
174  }
175  }
176  QF_CRIT_E_(); // re-enter crit. section to continue
177  }
178  QF_CRIT_X_();
179 }
180 
181 //****************************************************************************
182 // NOTE1:
183 // In some QF ports the critical section exit takes effect only on the next
184 // machine instruction. If this case, the next instruction is another entry
185 // to a critical section, the critical section won't be really exited, but
186 // rather the two adjacent critical sections would be merged.
187 //
188 // The QF_CRIT_EXIT_NOP() macro contains minimal code required
189 // to prevent such merging of critical sections in QF ports,
190 // in which it can occur.
191 
192 
193 //****************************************************************************
206 bool QF::noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept {
207  bool inactive;
208  if (timeEvtHead_[tickRate].m_next != nullptr) {
209  inactive = false;
210  }
211  else if (timeEvtHead_[tickRate].m_act != nullptr) {
212  inactive = false;
213  }
214  else {
215  inactive = true;
216  }
217  return inactive;
218 }
219 
220 //****************************************************************************
232  enum_t const sgnl, std::uint_fast8_t const tickRate) noexcept
233  :
234 #ifdef Q_EVT_CTOR
235  QEvt(static_cast<QSignal>(sgnl), QEvt::STATIC_EVT),
236 #else
237  QEvt(),
238 #endif
239  m_next(nullptr),
240  m_act(act),
241  m_ctr(0U),
242  m_interval(0U)
243 {
245  Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG)
246  && (tickRate < QF_MAX_TICK_RATE));
247 
248 #ifndef Q_EVT_CTOR
249  sig = static_cast<QSignal>(sgnl); // set QEvt::sig of this time event
250 #endif
251 
252  // Setting the POOL_ID event attribute to zero is correct only for
253  // events not allocated from event pools, which must be the case
254  // for Time Events.
255  //
256  poolId_ = 0U;
257 
258  // The refCtr_ attribute is not used in time events, so it is
259  // reused to hold the tickRate as well as other information
260  //
261  refCtr_ = static_cast<std::uint8_t>(tickRate);
262 }
263 
264 //****************************************************************************
269  :
270 #ifdef Q_EVT_CTOR
271  QEvt(0U, QEvt::STATIC_EVT),
272 #else
273  QEvt(),
274 #endif // Q_EVT_CTOR
275  m_next(nullptr),
276  m_act(nullptr),
277  m_ctr(0U),
278  m_interval(0U)
279 {
280 #ifndef Q_EVT_CTOR
281  sig = 0U;
282 
283  // Setting the POOL_ID event attribute to zero is correct only for
284  // events not allocated from event pools, which must be the case
285  // for Time Events.
286  //
287  poolId_ = 0U; // not from any event pool
288 
289  // The refCtr_ attribute is not used in time events, so it is
290  // reused to hold the tickRate as well as other information
291  //
292  refCtr_ = 0U; // default rate 0
293 
294 #endif // Q_EVT_CTOR
295 
296 }
297 
298 //****************************************************************************
324 void QTimeEvt::armX(QTimeEvtCtr const nTicks,
325  QTimeEvtCtr const interval) noexcept
326 {
327  std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE;
328  QTimeEvtCtr const ctr = m_ctr; // temporary to hold volatile
330 
334  Q_REQUIRE_ID(400, (m_act != nullptr)
335  && (ctr == 0U)
336  && (nTicks != 0U)
337  && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE))
338  && (static_cast<enum_t>(sig) >= Q_USER_SIG));
339 #ifdef Q_NASSERT
340  (void)ctr; // avoid compiler warning about unused variable
341 #endif
342 
343  QF_CRIT_E_();
344  m_ctr = nTicks;
345  m_interval = interval;
346 
347  // is the time event unlinked?
348  // NOTE: For the duration of a single clock tick of the specified tick
349  // rate a time event can be disarmed and yet still linked into the list,
350  // because un-linking is performed exclusively in the QF_tickX() function.
351  //
352  if (static_cast<std::uint_fast8_t>(
353  static_cast<std::uint_fast8_t>(refCtr_) & TE_IS_LINKED) == 0U)
354  {
355  refCtr_ |= TE_IS_LINKED; // mark as linked
356 
357  // The time event is initially inserted into the separate
358  // "freshly armed" link list based on QF::timeEvtHead_[tickRate].act.
359  // Only later, inside the QF::tickX() function, the "freshly armed"
360  // list is appended to the main list of armed time events based on
361  // QF::timeEvtHead_[tickRate].next. Again, this is to keep any
362  // changes to the main list exclusively inside the QF::tickX()
363  // function.
364  //
365  m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
366  QF::timeEvtHead_[tickRate].m_act = this;
367  }
368 
369 #ifdef Q_SPY
370  std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
371 #endif
373  QS_TIME_PRE_(); // timestamp
374  QS_OBJ_PRE_(this); // this time event object
375  QS_OBJ_PRE_(m_act); // the active object
376  QS_TEC_PRE_(nTicks); // the number of ticks
377  QS_TEC_PRE_(interval); // the interval
378  QS_U8_PRE_(tickRate); // tick rate
380 
381  QF_CRIT_X_();
382 }
383 
384 //****************************************************************************
400 bool QTimeEvt::disarm(void) noexcept {
402  QF_CRIT_E_();
403  bool wasArmed;
404 #ifdef Q_SPY
405  std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
406 #endif
407 
408  // is the time event actually armed?
409  if (m_ctr != 0U) {
410  wasArmed = true;
411  refCtr_ |= TE_WAS_DISARMED;
412 
414  QS_TIME_PRE_(); // timestamp
415  QS_OBJ_PRE_(this); // this time event object
416  QS_OBJ_PRE_(m_act); // the target AO
417  QS_TEC_PRE_(m_ctr); // the number of ticks
418  QS_TEC_PRE_(m_interval); // the interval
419  QS_U8_PRE_(refCtr_& TE_TICK_RATE);
421 
422  m_ctr = 0U; // schedule removal from the list
423  }
424  else { // the time event was already disarmed automatically
425  wasArmed = false;
426  refCtr_ &= static_cast<std::uint8_t>(~TE_WAS_DISARMED);
427 
429  QS_TIME_PRE_(); // timestamp
430  QS_OBJ_PRE_(this); // this time event object
431  QS_OBJ_PRE_(m_act); // the target AO
432  QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate
434 
435  }
436  QF_CRIT_X_();
437  return wasArmed;
438 }
439 
440 //****************************************************************************
460 bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept {
461  std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE;
463 
467  Q_REQUIRE_ID(600, (m_act != nullptr)
468  && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE))
469  && (nTicks != 0U)
470  && (static_cast<enum_t>(sig) >= Q_USER_SIG));
471 
472  QF_CRIT_E_();
473  bool wasArmed;
474 
475  // is the time evt not running?
476  if (m_ctr == 0U) {
477  wasArmed = false;
478 
479  // is the time event unlinked?
480  // NOTE: For a duration of a single clock tick of the specified
481  // tick rate a time event can be disarmed and yet still linked into
482  // the list, because unlinking is performed exclusively in the
483  // QF::tickX() function.
484  //
485  if ((refCtr_ & TE_IS_LINKED) == 0U) {
486  refCtr_ |= TE_IS_LINKED; // mark as linked
487 
488  // The time event is initially inserted into the separate
489  // "freshly armed" list based on QF_timeEvtHead_[tickRate].act.
490  // Only later, inside the QF_tickX() function, the "freshly armed"
491  // list is appended to the main list of armed time events based on
492  // QF_timeEvtHead_[tickRate].next. Again, this is to keep any
493  // changes to the main list exclusively inside the QF::tickX()
494  // function.
495  //
496  m_next = QF::timeEvtHead_[tickRate].toTimeEvt();
497  QF::timeEvtHead_[tickRate].m_act = this;
498  }
499  }
500  else { // the time event is being disarmed
501  wasArmed = true;
502  }
503  m_ctr = nTicks; // re-load the tick counter (shift the phasing)
504 
505 #ifdef Q_SPY
506  std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
507 #endif
509  QS_TIME_PRE_(); // timestamp
510  QS_OBJ_PRE_(this); // this time event object
511  QS_OBJ_PRE_(m_act); // the target AO
512  QS_TEC_PRE_(m_ctr); // the number of ticks
513  QS_TEC_PRE_(m_interval); // the interval
514  QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U));
516 
517  QF_CRIT_X_();
518  return wasArmed;
519 }
520 
521 //****************************************************************************
541 bool QTimeEvt::wasDisarmed(void) noexcept {
542  std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED;
543  refCtr_ |= TE_WAS_DISARMED; // set the flag
544  return isDisarmed != 0U;
545 }
546 
547 //****************************************************************************
560 QTimeEvtCtr QTimeEvt::currCtr(void) const noexcept {
562 
563  QF_CRIT_E_();
564  QTimeEvtCtr const ret = m_ctr;
565  QF_CRIT_X_();
566 
567  return ret;
568 }
569 
570 } // namespace QP
571 
unsigned char uint8_t
Definition: 16bit/stdint.h:29
unsigned int uint_fast8_t
Definition: 16bit/stdint.h:36
std::uint8_t m_prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.hpp:185
static bool noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept
Definition: qf_time.cpp:206
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]
heads of linked lists of time events, one for every clock tick rate
Definition: qf.hpp:615
static void tickX_(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Processes all armed time events at every clock tick.
Definition: qf_time.cpp:78
QTimeEvt * toTimeEvt(void) noexcept
encapsulate the cast the m_act attribute to QTimeEvt*
Definition: qf.hpp:469
QTimeEvtCtr currCtr(void) const noexcept
Get the current value of the down-counter of a time event.
Definition: qf_time.cpp:560
QTimeEvtCtr volatile m_ctr
Definition: qf.hpp:421
QActive * toActive(void) noexcept
encapsulate the cast the m_act attribute to QActive*
Definition: qf.hpp:464
QTimeEvtCtr m_interval
Definition: qf.hpp:429
QTimeEvt(void) noexcept
private default constructor only for friends
Definition: qf_time.cpp:268
QTimeEvt *volatile m_next
link to the next time event in the list
Definition: qf.hpp:408
void armX(QTimeEvtCtr const nTicks, QTimeEvtCtr const interval=0U) noexcept
Arm a time event (one shot or periodic) for event posting.
Definition: qf_time.cpp:324
bool disarm(void) noexcept
Disarm a time event.
Definition: qf_time.cpp:400
bool wasDisarmed(void) noexcept
Check the "was disarmed" status of a time event.
Definition: qf_time.cpp:541
bool rearm(QTimeEvtCtr const nTicks) noexcept
Rearm a time event.
Definition: qf_time.cpp:460
void *volatile m_act
Definition: qf.hpp:414
Definition: struct.dox:1
@ QS_QF_TIMEEVT_AUTO_DISARM
a time event expired and was disarmed
Definition: qs.hpp:111
@ QS_QF_TIMEEVT_DISARM
true disarming of an armed time event
Definition: qs.hpp:113
@ QS_QF_TIMEEVT_REARM
rearming of a time event
Definition: qs.hpp:114
@ QS_QF_TIMEEVT_POST
a time event posted itself directly to an AO
Definition: qs.hpp:115
@ QS_QF_TIMEEVT_DISARM_ATTEMPT
attempt to disarm a disarmed QTimeEvt
Definition: qs.hpp:112
@ QS_QF_TICK
QP::QF::tickX() was called.
Definition: qs.hpp:107
@ QS_QF_TIMEEVT_ARM
a time event was armed
Definition: qs.hpp:110
constexpr std::uint8_t TE_WAS_DISARMED
Definition: qf_pkg.hpp:133
constexpr std::uint8_t TE_TICK_RATE
Definition: qf_pkg.hpp:134
constexpr std::uint8_t TE_IS_LINKED
Definition: qf_pkg.hpp:132
constexpr enum_t Q_USER_SIG
Offset or the user signals.
Definition: qep.hpp:610
std::uint8_t QTimeEvtCtr
Definition: qf.hpp:99
std::uint16_t QSignal
Definition: qep.hpp:146
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:120
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:279
int enum_t
alias for enumerations used for event signals
Definition: qep.hpp:82
#define QF_CRIT_EXIT_NOP()
Definition: qf.hpp:674
Internal (package scope) QF/C++ interface.
#define QF_CRIT_STAT_
Definition: qf_pkg.hpp:56
#define Q_ASSERT_CRIT_(id_, test_)
Definition: qf_pkg.hpp:94
#define QF_CRIT_X_()
Definition: qf_pkg.hpp:77
#define QF_CRIT_E_()
Definition: qf_pkg.hpp:66
#define QS_TIME_PRE_()
Definition: qs.hpp:266
Dummy definitions of the QS macros that avoid code generation from the QS instrumentation.
#define QS_U8_PRE_(data_)
Definition: qs_dummy.hpp:100
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Definition: qs_dummy.hpp:98
#define QS_OBJ_PRE_(obj_)
Definition: qs_dummy.hpp:107
#define QS_TEC_PRE_(ctr_)
Definition: qs_dummy.hpp:112
#define QS_END_NOCRIT_PRE_()
Definition: qs_dummy.hpp:99
#define QS_2U8_PRE_(data1_, data2_)
Definition: qs_dummy.hpp:101
#define QS_SIG_PRE_(sig_)
Definition: qs_dummy.hpp:105
Internal (package scope) QS/C++ interface.
QS/C++ port to a 32-bit CPU, generic compiler.
QSignal sig
signal of the event instance
Definition: qep.hpp:210
std::uint8_t volatile refCtr_
reference counter
Definition: qep.hpp:212