QP/C++ 8.1.4
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
50//============================================================================
51namespace QP {
52
54
55//............................................................................
56QK::QK() noexcept
57 : readySet(),
58 actPrio(0U),
59 nextPrio(0U),
60 actThre(0U),
61 lockCeil(0U),
62 intNest(0U)
63{}
64//............................................................................
65QSchedStatus QK::schedLock(std::uint8_t const ceiling) noexcept {
68
69 // scheduler should never be locked inside an ISR
71
72 QSchedStatus stat = 0xFFU; // assume scheduler NOT locked
73 if (ceiling > priv_.lockCeil) { // increasing the lock ceiling?
75 QS_TIME_PRE(); // timestamp
76 // the previous lock ceiling & new lock ceiling
77 QS_2U8_PRE(priv_.lockCeil, ceiling);
79
80 // previous status of the lock
81 stat = static_cast<QSchedStatus>(priv_.lockCeil);
82
83 // new status of the lock
84 priv_.lockCeil = static_cast<std::uint8_t>(ceiling);
85 }
87
88 return stat; // return the status to be saved in a stack variable
89}
90
91//............................................................................
92void QK::schedUnlock(QSchedStatus const prevCeil) noexcept {
93 // has the scheduler been actually locked by the last QK::schedLock()?
94 if (prevCeil != 0xFFU) {
97
98 // scheduler should never be unlocked inside an ISR
100
101 // the current lock-ceiling must be higher than the previous ceiling
102 Q_REQUIRE_INCRIT(220, priv_.lockCeil > prevCeil);
103
105 QS_TIME_PRE(); // timestamp
106 // current lock ceiling (old), previous lock ceiling (new)
107 QS_2U8_PRE(priv_.lockCeil,
108 static_cast<std::uint8_t>(prevCeil));
109 QS_END_PRE()
110
111 // restore the previous lock ceiling
112 priv_.lockCeil = prevCeil;
113
114 // find if any AOs should be run after unlocking the scheduler
115 if (sched_() != 0U) { // preemption needed?
116 activate_(); // activate any unlocked AOs
117 }
118
119 QF_CRIT_EXIT();
120 }
121}
122
123//............................................................................
124std::uint_fast8_t QK::sched_() noexcept {
125 // NOTE: this function is entered with interrupts DISABLED
126
127 std::uint8_t p = 0U; // assume NO activation needed
128 if (priv_.readySet.notEmpty()) {
129 // find the highest-prio AO with non-empty event queue
130 p = static_cast<std::uint8_t>(priv_.readySet.findMax());
131
132 // is the AO's prio. below the active preemption-threshold?
133 if (p <= priv_.actThre) {
134 p = 0U; // no activation needed
135 }
136 else {
137 // is the AO's prio. below the lock-ceiling?
138 if (p <= priv_.lockCeil) {
139 p = 0U; // no activation needed
140 }
141 else {
142 priv_.nextPrio = p; // next AO to run
143 }
144 }
145 }
146
147 return p; // the next priority or 0
148}
149
150//............................................................................
151std::uint_fast8_t QK::sched_act_(
152 QActive const * const act,
153 std::uint_fast8_t const pthre_in) noexcept
154{
155 // NOTE: this function is entered with interrupts DISABLED
156
157 std::uint8_t p = act->m_prio;
158 if (act->m_eQueue.isEmpty()) { // empty queue?
159 priv_.readySet.remove(p);
160 }
161
162 if (priv_.readySet.isEmpty()) { // no AOs ready to run?
163 p = 0U; // no activation needed
164 }
165 else {
166 // find new highest-prio AO ready to run...
167 p = static_cast<std::uint8_t>(priv_.readySet.findMax());
168 // NOTE: p is guaranteed to be <= QF_MAX_ACTIVE
169
170 // is the new prio. below the initial preemption-threshold?
171 if (p <= pthre_in) {
172 p = 0U; // no activation needed
173 }
174 else {
175 // is the AO's prio. below the lock preemption-threshold?
176 if (p <= priv_.lockCeil) {
177 p = 0U; // no activation needed
178 }
179 }
180 }
181
182 return p;
183}
184
185//............................................................................
187 // NOTE: this function is entered with interrupts DISABLED
188
189 std::uint8_t const prio_in = priv_.actPrio; // save initial prio.
190 std::uint8_t p = priv_.nextPrio; // next prio to run
191
192
193 // the activated AO's prio must be in range and cannot be 0 (idle thread)
194 Q_REQUIRE_INCRIT(520, (0U < p) && (p <= QF_MAX_ACTIVE));
195
196 // the initial prio. must be lower than the activated AO's prio.
197 Q_REQUIRE_INCRIT(530, prio_in < p);
198
199#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
200 std::uint8_t pprev = prio_in;
201#endif // QF_ON_CONTEXT_SW || Q_SPY
202
203 priv_.nextPrio = 0U; // clear for the next time
204
205 std::uint8_t pthre_in = 0U; // assume preempting the idle thread
206 if (prio_in > 0U) { // preempting a regular thread (NOT the idle thread)?
207 QActive const * const a = QActive_registry_[prio_in];
208
209 // the AO must be registered at prio. prio_in
210 Q_ASSERT_INCRIT(540, a != nullptr);
211
212 pthre_in = a->m_pthre;
213 }
214
215 // loop until no more ready-to-run AOs of higher pthre than the initial
216 do {
217 QActive * const a = QActive_registry_[p];
218
219 // the AO must be registered at prio. p
220 Q_ASSERT_INCRIT(570, a != nullptr);
221 std::uint8_t const pthre = a->m_pthre;
222
223 // set new active prio. and preemption-threshold
224 priv_.actPrio = p;
225 priv_.actThre = pthre;
226
227#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
228 if (p != pprev) { // changing threads?
229
231 QS_TIME_PRE(); // timestamp
232 QS_2U8_PRE(p, pprev);
233 QS_END_PRE()
234
235#ifdef QF_ON_CONTEXT_SW
237#endif // QF_ON_CONTEXT_SW
238
239 pprev = p; // update previous prio.
240 }
241#endif // QF_ON_CONTEXT_SW || Q_SPY
242
243 QF_INT_ENABLE(); // unconditionally enable interrupts
244
245 QEvt const * const e = a->get_(); // queue not empty
246 a->dispatch(e, p); // dispatch event (virtual call)
247#if (QF_MAX_EPOOL > 0U)
248 QF::gc(e); // check if the event is garbage, and collect it if so
249#endif
250
251 // determine the next highest-prio. AO ready to run...
252 QF_INT_DISABLE(); // unconditionally disable interrupts
253
254 // schedule next AO
255 p = static_cast<std::uint8_t>(sched_act_(a, pthre_in));
256
257 } while (p != 0U);
258
259 // restore the active prio. and preemption-threshold
260 priv_.actPrio = prio_in;
261 priv_.actThre = pthre_in;
262
263#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
264 if (prio_in != 0U) { // resuming an active object?
266 QS_TIME_PRE(); // timestamp
267 // prio. of the resumed AO, previous prio.
268 QS_2U8_PRE(prio_in, pprev);
269 QS_END_PRE()
270
271#ifdef QF_ON_CONTEXT_SW
273 QActive_registry_[prio_in]);
274#endif // QF_ON_CONTEXT_SW
275 }
276 else { // resuming prio.==0 --> idle
278 QS_TIME_PRE(); // timestamp
279 QS_U8_PRE(pprev); // previous prio.
280 QS_END_PRE()
281
282#ifdef QF_ON_CONTEXT_SW
283 QF_onContextSw(QActive_registry_[pprev], nullptr);
284#endif // QF_ON_CONTEXT_SW
285 }
286
287#endif // QF_ON_CONTEXT_SW || Q_SPY
288}
289
290//----------------------------------------------------------------------------
291namespace QF {
292
293//............................................................................
294void init() {
295 // setup the QK scheduler as initially locked and not running
296 QK::priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
297
298#ifdef QK_INIT
299 QK_INIT(); // port-specific initialization of the QK kernel
300#endif
301}
302//............................................................................
303void stop() {
304 onCleanup(); // application-specific cleanup callback
305 // nothing else to do for the preemptive QK kernel
306}
307//............................................................................
308int_t run() {
310#ifdef Q_SPY
311 // produce the QS_QF_RUN trace record
312 QS::beginRec_(static_cast<std::uint_fast8_t>(QS_QF_RUN));
313 QS::endRec_();
314#endif // Q_SPY
315
316#ifdef QK_START
317 QK_START(); // port-specific startup of the QK kernel
318#endif
319
320 QK::priv_.lockCeil = 0U; // unlock the QK scheduler
321
322#ifdef QF_ON_CONTEXT_SW
323 // officially switch to the idle context
324 QF_onContextSw(nullptr, QActive_registry_[QK::priv_.nextPrio]);
325#endif
326
327 // activate AOs to process events posted so far
328 if (QK::sched_() != 0U) {
330 }
331
333
334 onStartup(); // app. callback: configure and enable interrupts
335
336 for (;;) { // QK idle loop...
337 QK::onIdle(); // application-specific QK idle callback
338 }
339}
340
341} // namespace QF
342
343//............................................................................
344void QActive::start(
345 QPrioSpec const prioSpec,
346 QEvtPtr * const qSto,
347 std::uint_fast16_t const qLen,
348 void * const stkSto,
349 std::uint_fast16_t const stkSize,
350 void const * const par)
351{
352 Q_UNUSED_PAR(stkSto); // not needed in QK
353 Q_UNUSED_PAR(stkSize); // not needed in QK
354
357
358 // stack storage must NOT be provided for an AO (QK does not need it)
359 Q_REQUIRE_INCRIT(910, stkSto == nullptr);
360
361 QF_CRIT_EXIT();
362
363 m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // prio. of the AO
364 m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre.
365 register_(); // register this AO with the framework
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?
377 }
378 QF_CRIT_EXIT();
379}
380
381} // namespace QP
Active object class (based on the QP::QHsm implementation strategy).
Definition qp.hpp:501
void register_() noexcept
Register this active object to be managed by the framework.
Definition qf_qact.cpp:68
void dispatch(QEvt const *const e, std::uint_fast8_t const qsId) override
Virtual function to dispatch an event to the state machine.
Definition qf_qact.cpp:142
friend void QF::init()
QACTIVE_EQUEUE_TYPE m_eQueue
Port-dependent event-queue type (often QP::QEQueue).
Definition qp.hpp:515
QEvt const * get_() noexcept
Get an event from the event queue of an active object.
Definition qf_actq.cpp:191
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=nullptr)
Starts execution of an active object and registers the object with the framework.
Definition qv.cpp:216
std::uint8_t m_pthre
Preemption-threshold [1..QF_MAX_ACTIVE] of this AO.
Definition qp.hpp:504
std::uint8_t m_prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.hpp:503
Event class.
Definition qp.hpp:101
QK preemptive non-blocking kernel.
Definition qk.hpp:38
static QK priv_
Definition qk.hpp:59
std::uint8_t intNest
Up-down counter indicating current interrupt nesting (used in some QK ports).
Definition qk.hpp:45
static std::uint_fast8_t sched_act_(QActive const *const act, std::uint_fast8_t const pthre_in) noexcept
QK internal helper function to determine whether activation is needed.
Definition qk.cpp:151
static void schedUnlock(QSchedStatus const prevCeil) noexcept
QK selective scheduler unlock.
Definition qk.cpp:92
QP::QPSet readySet
Set of active-objects/threads that are ready to run in the QK kernel.
Definition qk.hpp:40
QK() noexcept
Definition qk.cpp:56
std::uint8_t lockCeil
Scheduler lock-ceiling (0 if scheduler unlocked).
Definition qk.hpp:44
static std::uint_fast8_t sched_() noexcept
QK scheduler finds the highest-priority AO ready to run.
Definition qk.cpp:124
std::uint8_t actPrio
Priority of the currently active AO.
Definition qk.hpp:41
static QSchedStatus schedLock(std::uint8_t const ceiling) noexcept
QK selective scheduler lock.
Definition qk.cpp:65
std::uint8_t nextPrio
Next AO priority scheduled by QK.
Definition qk.hpp:42
static void activate_()
QK activator activates the next active object. The activated AO preempts the currently executing AOs.
Definition qk.cpp:186
static void onIdle()
QK idle callback (customized in BSPs for QK).
std::uint8_t actThre
Preemption threshold of the currently active AO.
Definition qk.hpp:43
QF Active Object Framework namespace.
Definition qp.hpp:489
void onCleanup()
Cleanup QF callback.
void gc(QEvt const *const e) noexcept
Recycle a mutable (mutable) event.
Definition qf_dyn.cpp:276
void init()
QF initialization.
Definition qv.cpp:102
int_t run()
Transfers control to QF to run the application.
Definition qv.cpp:115
void stop()
Invoked by the application layer to stop the QF framework and return control to the OS/Kernel (used i...
Definition qv.cpp:109
void onStartup()
Startup QF callback.
QP/C++ Framework namespace.
Definition qequeue.hpp:36
@ QS_SCHED_LOCK
scheduler was locked
Definition qs.hpp:128
@ QS_QF_RUN
QF_run() was entered.
Definition qs.hpp:154
@ QS_SCHED_NEXT
scheduler started next task
Definition qs.hpp:130
@ QS_SCHED_UNLOCK
scheduler was unlocked
Definition qs.hpp:129
@ QS_SCHED_IDLE
scheduler restored the idle task
Definition qs.hpp:131
std::array< QActive *, QF_MAX_ACTIVE+1U > QActive_registry_
Internal array of pointers to the registered Active Objects.
Definition qf_qact.cpp:47
std::uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.hpp:423
std::uint8_t QSchedStatus
The scheduler lock status for QK::schedLock() and QK::schedUnlock().
Definition qk.hpp:35
int int_t
Alias for assertion-ID numbers in QP assertions and return from QP::QF::run().
Definition qp.hpp:87
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.hpp:89
void QF_onContextSw(QP::QActive *prev, QP::QActive *next)
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
QP/C++ Framework in C++ internal (package-scope) interface.
Sample QP/C++ port.
#define QK_ISR_CONTEXT_()
Port-specific method to check if the QK kernel executes in the ISR context (used internally in QK onl...
Definition qp_port.hpp:201
#define QF_INT_DISABLE()
Port-specific interrupt disable.
Definition qp_port.hpp:56
#define QF_INT_ENABLE()
Port-specific interrupt enable.
Definition qp_port.hpp:64
#define QS_TIME_PRE()
Definition qs.hpp:362
#define QS_FLUSH()
Definition qs.hpp:273
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.hpp:83
#define QS_U8_PRE(data_)
Output pre-formatted unsigned 8-bit integer data element.
Definition qs_pkg.hpp:81
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.hpp:79
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.hpp:73
Sample QS/C++ port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section).
Definition qsafe.h:54
#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