QP/C++  8.0.3
Real-Time Event Framework
Loading...
Searching...
No Matches
qk.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// protection against including this source file in a wrong project
41#ifndef QK_HPP_
42 #error Source file included in a project NOT based on the QK kernel
43#endif // QK_HPP_
44
45// unnamed namespace for local definitions with internal linkage
46namespace {
47Q_DEFINE_THIS_MODULE("qk")
48} // unnamed namespace
49
50namespace QP {
51namespace QK {
52
53//............................................................................
54QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept {
57
59
60 // first store the previous lock prio
61 QSchedStatus stat = 0xFFU; // assume scheduler NOT locked
62 if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling?
63 QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio)
64 QS_TIME_PRE(); // timestamp
65 // the previous lock ceiling & new lock ceiling
66 QS_2U8_PRE(static_cast<std::uint8_t>(QK_priv_.lockCeil),
67 static_cast<std::uint8_t>(ceiling));
69
70 // previous status of the lock
71 stat = static_cast<QSchedStatus>(QK_priv_.lockCeil);
72
73 // new status of the lock
74 QK_priv_.lockCeil = ceiling;
75 }
77
78 return stat; // return the status to be saved in a stack variable
79}
80
81//............................................................................
82void schedUnlock(QSchedStatus const prevCeil) noexcept {
83 // has the scheduler been actually locked by the last QK::schedLock()?
84 if (prevCeil != 0xFFU) {
87
89 && (QK_priv_.lockCeil > prevCeil));
90 QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio)
91 QS_TIME_PRE(); // timestamp
92 // current lock ceiling (old), previous lock ceiling (new)
93 QS_2U8_PRE(static_cast<std::uint8_t>(QK_priv_.lockCeil),
94 static_cast<std::uint8_t>(prevCeil));
96
97 // restore the previous lock ceiling
98 QK_priv_.lockCeil = prevCeil;
99
100 // find if any AOs should be run after unlocking the scheduler
101 if (QK_sched_() != 0U) { // preemption needed?
102 QK_activate_(); // activate any unlocked AOs
103 }
104
105 QF_CRIT_EXIT();
106 }
107}
108
109} // namespace QK
110} // namespace QP
111
112//============================================================================
113extern "C" {
114
116
117//............................................................................
118std::uint_fast8_t QK_sched_() noexcept {
119 // NOTE: this function is entered with interrupts DISABLED
120
121 std::uint_fast8_t p = 0U; // assume NO activation needed
122 if (QK_priv_.readySet.notEmpty()) {
123 // find the highest-prio AO with non-empty event queue
124 p = QK_priv_.readySet.findMax();
125
126 // is the AO's prio. below the active preemption-threshold?
127 if (p <= QK_priv_.actThre) {
128 p = 0U; // no activation needed
129 }
130 else {
131 // is the AO's prio. below the lock-ceiling?
132 if (p <= QK_priv_.lockCeil) {
133 p = 0U; // no activation needed
134 }
135 else {
136 QK_priv_.nextPrio = p; // next AO to run
137 }
138 }
139 }
140
141 return p;
142}
143
144//............................................................................
145std::uint_fast8_t QK_sched_act_(
146 QP::QActive const * const act,
147 std::uint_fast8_t const pthre_in) noexcept
148{
149 // NOTE: this function is entered with interrupts DISABLED
150
151 std::uint_fast8_t p = act->getPrio();
152 if (act->getEQueue().isEmpty()) { // empty queue?
153 QK_priv_.readySet.remove(p);
154 }
155
156 if (QK_priv_.readySet.isEmpty()) {
157 p = 0U; // no activation needed
158 }
159 else {
160 // find new highest-prio AO ready to run...
161 p = QK_priv_.readySet.findMax();
162 // NOTE: p is guaranteed to be <= QF_MAX_ACTIVE
163
164 // is the new prio. below the initial preemption-threshold?
165 if (p <= pthre_in) {
166 p = 0U; // no activation needed
167 }
168 else {
169 // is the AO's prio. below the lock preemption-threshold?
170 if (p <= QK_priv_.lockCeil) {
171 p = 0U; // no activation needed
172 }
173 }
174 }
175
176 return p;
177}
178
179//............................................................................
180void QK_activate_() noexcept {
181 // NOTE: this function is entered with interrupts DISABLED
182
183 std::uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio.
184 std::uint_fast8_t p = QK_priv_.nextPrio; // next prio to run
185
186 Q_REQUIRE_INCRIT(500, (prio_in <= QF_MAX_ACTIVE)
187 && (0U < p) && (p <= QF_MAX_ACTIVE));
188
189#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
190 std::uint_fast8_t pprev = prio_in;
191#endif // QF_ON_CONTEXT_SW || Q_SPY
192
193 QK_priv_.nextPrio = 0U; // clear for the next time
194
195 std::uint_fast8_t pthre_in = 0U; // assume preempting the idle thread
196 if (prio_in != 0U) { // preempting NOT the idle thread
197 QP::QActive const * const a = QP::QActive::registry_[prio_in];
198 Q_ASSERT_INCRIT(510, a != nullptr);
199
200 pthre_in = static_cast<std::uint_fast8_t>(a->getPThre());
201 }
202
203 // loop until no more ready-to-run AOs of higher pthre than the initial
204 do {
205 QP::QActive * const a = QP::QActive::registry_[p];
206 Q_ASSERT_INCRIT(520, a != nullptr); // the AO must be registered
207 std::uint_fast8_t const pthre
208 = static_cast<std::uint_fast8_t>(a->getPThre());
209
210 // set new active prio. and preemption-threshold
211 QK_priv_.actPrio = p;
212 QK_priv_.actThre = pthre;
213
214#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
215 if (p != pprev) { // changing threads?
216
217 QS_BEGIN_PRE(QP::QS_SCHED_NEXT, p)
218 QS_TIME_PRE(); // timestamp
219 QS_2U8_PRE(p, // prio. of the scheduled AO
220 pprev); // previous prio.
221 QS_END_PRE()
222
223#ifdef QF_ON_CONTEXT_SW
225#endif // QF_ON_CONTEXT_SW
226
227 pprev = p; // update previous prio.
228 }
229#endif // QF_ON_CONTEXT_SW || Q_SPY
230
231 QF_INT_ENABLE(); // unconditionally enable interrupts
232
233 QP::QEvt const * const e = a->get_();
234
235 // dispatch event (virtual call)
236 a->dispatch(e, a->getPrio());
237#if (QF_MAX_EPOOL > 0U)
238 QP::QF::gc(e);
239#endif
240
241 // determine the next highest-prio. AO ready to run...
242 QF_INT_DISABLE(); // unconditionally disable interrupts
243 p = QK_sched_act_(a, pthre_in); // schedule next AO
244
245 } while (p != 0U);
246
247 // restore the active prio. and preemption-threshold
248 QK_priv_.actPrio = prio_in;
249 QK_priv_.actThre = pthre_in;
250
251#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
252 if (prio_in != 0U) { // resuming an active object?
253 QS_BEGIN_PRE(QP::QS_SCHED_NEXT, prio_in)
254 QS_TIME_PRE(); // timestamp
255 // prio. of the resumed AO, previous prio.
256 QS_2U8_PRE(prio_in, pprev);
257 QS_END_PRE()
258
259#ifdef QF_ON_CONTEXT_SW
261 QP::QActive::registry_[prio_in]);
262#endif // QF_ON_CONTEXT_SW
263 }
264 else { // resuming prio.==0 --> idle
265 QS_BEGIN_PRE(QP::QS_SCHED_IDLE, pprev)
266 QS_TIME_PRE(); // timestamp
267 QS_U8_PRE(pprev); // previous prio.
268 QS_END_PRE()
269
270#ifdef QF_ON_CONTEXT_SW
271 QF_onContextSw(QP::QActive::registry_[pprev], nullptr);
272#endif // QF_ON_CONTEXT_SW
273 }
274
275
276#endif // QF_ON_CONTEXT_SW || Q_SPY
277}
278} // extern "C"
279
280//............................................................................
281namespace QP {
282namespace QF {
283
284//............................................................................
285void init() {
286 bzero_(&QF::priv_, sizeof(QF::priv_));
287 bzero_(&QK_priv_, sizeof(QK_priv_));
289
290 // setup the QK scheduler as initially locked and not running
291 QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
292
293#ifdef QK_INIT
294 QK_INIT(); // port-specific initialization of the QK kernel
295#endif
296}
297
298//............................................................................
299void stop() {
300 onCleanup(); // application-specific cleanup callback
301 // nothing else to do for the preemptive QK kernel
302}
303
304//............................................................................
305int_t run() {
307#ifdef Q_SPY
308 // produce the QS_QF_RUN trace record
309 QS::beginRec_(QS_REC_NUM_(QS_QF_RUN));
310 QS::endRec_();
311#endif // Q_SPY
312
313#ifdef QK_START
314 QK_START(); // port-specific startup of the QK kernel
315#endif
316
317 QK_priv_.lockCeil = 0U; // unlock the QK scheduler
318
319#ifdef QF_ON_CONTEXT_SW
320 // officially switch to the idle context
322#endif
323
324 // activate AOs to process events posted so far
325 if (QK_sched_() != 0U) {
326 QK_activate_();
327 }
328
330
331 onStartup(); // app. callback: configure and enable interrupts
332
333 for (;;) { // QK idle loop...
334 QK::onIdle(); // application-specific QK on-idle callback
335 }
336
337#ifdef __GNUC__ // GNU compiler?
338 return 0;
339#endif
340}
341
342} // namespace QF
343
344//............................................................................
345void QActive::start(
346 QPrioSpec const prioSpec,
347 QEvtPtr * const qSto,
348 std::uint_fast16_t const qLen,
349 void * const stkSto,
350 std::uint_fast16_t const stkSize,
351 void const * const par)
352{
353 Q_UNUSED_PAR(stkSto); // not needed in QK
354 Q_UNUSED_PAR(stkSize); // not needed in QK
355
358
360 (stkSto == nullptr));
361 QF_CRIT_EXIT();
362
363 m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio.
364 m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre.
365 register_(); // make QF aware of this AO
366
367 m_eQueue.init(qSto, qLen); // init the built-in queue
368
369 // top-most initial tran. (virtual call)
370 this->init(par, m_prio);
371 QS_FLUSH(); // flush the trace buffer to the host
372
373 // See if this AO needs to be scheduled if QK is already running
375 if (QK_sched_() != 0U) { // activation needed?
376 QK_activate_();
377 }
378 QF_CRIT_EXIT();
379}
380
381} // namespace QP
Private attributes of the QK kernel.
Definition qk.hpp:41
std::uint_fast8_t nextPrio
Definition qk.hpp:45
std::uint_fast8_t lockCeil
Definition qk.hpp:47
Active object class (based on the QHsm implementation strategy)
Definition qp.hpp:563
void start(QPrioSpec const prioSpec, QEvtPtr *const qSto, std::uint_fast16_t const qLen, void *const stkSto, std::uint_fast16_t const stkSize, void const *const par)
Definition qv.cpp:216
void init(void const *const e, std::uint_fast8_t const qsId) override
Virtual function to take the top-most initial transition in the state machine.
Definition qp.hpp:607
static QActive * registry_[QF_MAX_ACTIVE+1U]
Definition qp.hpp:581
void register_() noexcept
Definition qf_qact.cpp:48
std::uint_fast8_t getPThre() const noexcept
Definition qp.hpp:684
void dispatch(QEvt const *const e, std::uint_fast8_t const qsId) override
Virtual function to dispatch an event to the state machine.
Definition qp.hpp:616
QACTIVE_EQUEUE_TYPE m_eQueue
Definition qp.hpp:577
QEvt const * get_() noexcept
Definition qf_actq.cpp:208
std::uint8_t m_pthre
Definition qp.hpp:566
std::uint_fast8_t getPrio() const noexcept
Definition qp.hpp:677
std::uint8_t m_prio
Definition qp.hpp:565
Event class.
Definition qp.hpp:115
QF Active Object Framework.
Definition qp.hpp:861
void onCleanup()
void gc(QEvt const *const e) noexcept
Recycle a mutable (mutable) event.
Definition qf_dyn.cpp:188
void bzero_(void *const start, std::uint_fast16_t const len) noexcept
Definition qf_act.cpp:56
QF::Attr priv_
Definition qf_act.cpp:54
void init()
Definition qv.cpp:97
int_t run()
Definition qv.cpp:114
void stop()
Definition qv.cpp:108
void onStartup()
preemptive, non-blocking kernel
Definition qk.hpp:65
QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept
Definition qk.cpp:54
void schedUnlock(QSchedStatus const prevCeil) noexcept
Definition qk.cpp:82
void onIdle()
QP/C++ framework.
Definition qequeue.hpp:36
QEvt const * QEvtPtr
Pointer to const event instances passed around in QP Framework.
Definition qp.hpp:141
std::uint_fast8_t QSchedStatus
Definition qk.hpp:34
std::uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.hpp:456
void QK_activate_() noexcept
Definition qk.cpp:180
std::uint_fast8_t QK_sched_act_(QP::QActive const *const act, std::uint_fast8_t const pthre_in) noexcept
Definition qk.cpp:145
std::uint_fast8_t QK_sched_() noexcept
Definition qk.cpp:118
void QK_activate_() noexcept
Definition qk.cpp:180
QK_Attr QK_priv_
Definition qk.cpp:115
std::uint_fast8_t QK_sched_() noexcept
Definition qk.cpp:118
int int_t
Alias for assertion-ID numbers in QP assertions and return from QP::QF::run()
Definition qp.hpp:91
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:94
void QF_onContextSw(QP::QActive *prev, QP::QActive *next)
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64)
Internal (package scope) QP/C++ interface.
Sample QP/C++ port.
#define QK_ISR_CONTEXT_()
Definition qp_port.hpp:111
#define QF_INT_DISABLE()
Disable interrupts.
Definition qp_port.hpp:36
#define QF_INT_ENABLE()
Enable interrupts.
Definition qp_port.hpp:39
QS/C++ dummy public interface.
#define QS_TIME_PRE()
Definition qs_dummy.hpp:154
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.hpp:151
#define QS_FLUSH()
Definition qs_dummy.hpp:75
#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