QP/C++  7.3.4
Real-Time Embedded Framework
Loading...
Searching...
No Matches
qxk_mutex.cpp
Go to the documentation of this file.
1//$file${src::qxk::qxk_mutex.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2//
3// Model: qpcpp.qm
4// File: ${src::qxk::qxk_mutex.cpp}
5//
6// This code has been generated by QM 6.1.1 <www.state-machine.com/qm>.
7// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
8//
9// This code is covered by the following QP license:
10// License # : LicenseRef-QL-dual
11// Issued to : Any user of the QP/C++ real-time embedded framework
12// Framework(s) : qpcpp
13// Support ends : 2024-12-31
14// License scope:
15//
16// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
17//
18// Q u a n t u m L e a P s
19// ------------------------
20// Modern Embedded Software
21//
22// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
23//
24// This software is dual-licensed under the terms of the open source GNU
25// General Public License version 3 (or any later version), or alternatively,
26// under the terms of one of the closed source Quantum Leaps commercial
27// licenses.
28//
29// The terms of the open source GNU General Public License version 3
30// can be found at: <www.gnu.org/licenses/gpl-3.0>
31//
32// The terms of the closed source Quantum Leaps commercial licenses
33// can be found at: <www.state-machine.com/licensing>
34//
35// Redistributions in source code must retain this top-level comment block.
36// Plagiarizing this software to sidestep the license obligations is illegal.
37//
38// Contact information:
39// <www.state-machine.com/licensing>
40// <info@state-machine.com>
41//
42//$endhead${src::qxk::qxk_mutex.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
43#define QP_IMPL // this is QP implementation
44#include "qp_port.hpp" // QP port
45#include "qp_pkg.hpp" // QP package-scope interface
46#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
47#ifdef Q_SPY // QS software tracing enabled?
48 #include "qs_port.hpp" // QS port
49 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
50#else
51 #include "qs_dummy.hpp" // disable the QS software tracing
52#endif // Q_SPY
53
54// protection against including this source file in a wrong project
55#ifndef QXK_HPP_
56 #error "Source file included in a project NOT based on the QXK kernel"
57#endif // QXK_HPP_
58
59// unnamed namespace for local definitions with internal linkage
60namespace {
61Q_DEFINE_THIS_MODULE("qxk_mutex")
62} // unnamed namespace
63
64//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
65// Check for the minimum required QP version
66#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
67#error qpcpp version 7.3.0 or higher required
68#endif
69//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
70
71//$define${QXK::QXMutex} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
72namespace QP {
73
74//${QXK::QXMutex} ............................................................
75
76//${QXK::QXMutex::QXMutex} ...................................................
78 : m_ao(Q_STATE_CAST(0))
79{}
80
81//${QXK::QXMutex::init} ......................................................
82void QXMutex::init(QPrioSpec const prioSpec) noexcept {
85 QF_MEM_SYS();
86
87 Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U);
88
89 m_ao.m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio.
90 m_ao.m_pthre = 0U; // not used
91 QActive &ao = m_ao;
92
93 QF_MEM_APP();
95
96 ao.register_(); // register this mutex as AO
97}
98
99//${QXK::QXMutex::lock} ......................................................
100bool QXMutex::lock(QTimeEvtCtr const nTicks) noexcept {
103 QF_MEM_SYS();
104
105 QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr);
106
107 // precondition, this mutex operation must:
108 // - NOT be called from an ISR;
109 // - be called from an eXtended thread;
110 // - the mutex-prio. must be in range;
111 // - the thread must NOT be already blocked on any object.
113 && (curr != nullptr)
114 && (m_ao.m_prio <= QF_MAX_ACTIVE)
115 && (curr->m_temp.obj == nullptr));
116 // also: the thread must NOT be holding a scheduler lock.
118
119 // is the mutex available?
120 bool locked = true; // assume that the mutex will be locked
121 if (m_ao.m_eQueue.m_nFree == 0U) {
122 m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting
123
124 // also: the newly locked mutex must have no holder yet
125 Q_REQUIRE_INCRIT(203, m_ao.m_osObject == nullptr);
126
127 // set the new mutex holder to the curr thread and
128 // save the thread's prio in the mutex
129 // NOTE: reuse the otherwise unused eQueue data member.
130 m_ao.m_osObject = curr;
131 m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio);
132
133 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio)
134 QS_TIME_PRE_(); // timestamp
135 QS_OBJ_PRE_(this); // this mutex
136 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
137 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
138 QS_END_PRE_()
139
140 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
141 // the holder prio. must be lower than that of the mutex
142 // and the prio. slot must be occupied by this mutex
143 Q_ASSERT_INCRIT(210, (curr->m_prio < m_ao.m_prio)
144 && (QActive::registry_[m_ao.m_prio] == &m_ao));
145
146 // remove the thread's original prio from the ready set
147 // and insert the mutex's prio into the ready set
149 static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head));
151 static_cast<std::uint_fast8_t>(m_ao.m_prio));
152 #ifndef Q_UNSAFE
154 #endif
155 // put the thread into the AO registry in place of the mutex
156 QActive::registry_[m_ao.m_prio] = curr;
157
158 // set thread's prio to that of the mutex
159 curr->m_prio = m_ao.m_prio;
160 #ifndef Q_UNSAFE
161 curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio);
162 #endif
163 }
164 }
165 // is the mutex locked by this thread already (nested locking)?
166 else if (m_ao.m_osObject == curr) {
167
168 // the nesting level beyond the arbitrary but high limit
169 // most likely means cyclic or recursive locking of a mutex.
170 Q_ASSERT_INCRIT(220, m_ao.m_eQueue.m_nFree < 0xFFU);
171
172 // lock one more level
173 m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U;
174
175 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio)
176 QS_TIME_PRE_(); // timestamp
177 QS_OBJ_PRE_(this); // this mutex
178 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
179 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
180 QS_END_PRE_()
181 }
182 else { // the mutex is already locked by a different thread
183 // the mutex holder must be valid
184 Q_ASSERT_INCRIT(230, m_ao.m_osObject != nullptr);
185
186 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
187 // the prio slot must be occupied by the thr. holding the mutex
188 Q_ASSERT_INCRIT(240, QActive::registry_[m_ao.m_prio]
189 == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject));
190 }
191
192 // remove the curr thread's prio from the ready set (will block)
193 // and insert it to the waiting set on this mutex
194 std::uint_fast8_t const p =
195 static_cast<std::uint_fast8_t>(curr->m_prio);
197 #ifndef Q_UNSAFE
199 #endif
200 m_waitSet.insert(p);
201
202 // set the blocking object (this mutex)
203 curr->m_temp.obj = QXK_PTR_CAST_(QMState*, this);
204 curr->teArm_(static_cast<enum_t>(QXK::TIMEOUT_SIG), nTicks);
205
206 QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->m_prio)
207 QS_TIME_PRE_(); // timestamp
208 QS_OBJ_PRE_(this); // this mutex
209 QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head),
210 curr->m_prio);
211 QS_END_PRE_()
212
213 // schedule the next thread if multitasking started
214 static_cast<void>(QXK_sched_()); // schedule other threads
215
216 QF_MEM_APP();
217 QF_CRIT_EXIT();
218 QF_CRIT_EXIT_NOP(); // BLOCK here !!!
219
220 // AFTER unblocking...
222 QF_MEM_SYS();
223 // the blocking object must be this mutex
224 Q_ASSERT_INCRIT(250, curr->m_temp.obj
225 == QXK_PTR_CAST_(QMState*, this));
226
227 // did the blocking time-out? (signal of zero means that it did)
228 if (curr->m_timeEvt.sig == 0U) {
229 if (m_waitSet.hasElement(p)) { // still waiting?
230 m_waitSet.remove(p); // remove unblocked thread
231 locked = false; // the mutex was NOT locked
232 }
233 }
234 else { // blocking did NOT time out
235 // the thread must NOT be waiting on this mutex
236 Q_ASSERT_INCRIT(260, !m_waitSet.hasElement(p));
237 }
238 curr->m_temp.obj = nullptr; // clear blocking obj.
239 }
240 QF_MEM_APP();
241 QF_CRIT_EXIT();
242
243 return locked;
244}
245
246//${QXK::QXMutex::tryLock} ...................................................
247bool QXMutex::tryLock() noexcept {
250 QF_MEM_SYS();
251
252 QActive *curr = QXK_priv_.curr;
253 if (curr == nullptr) { // called from a basic thread?
255 }
256
257 // precondition, this mutex must:
258 // - NOT be called from an ISR;
259 // - the calling thread must be valid;
260 // - the mutex-prio. must be in range
262 && (curr != nullptr)
263 && (m_ao.m_prio <= QF_MAX_ACTIVE));
264 // also: the thread must NOT be holding a scheduler lock.
266 QXK_priv_.lockHolder != static_cast<std::uint_fast8_t>(curr->m_prio));
267
268 // is the mutex available?
269 if (m_ao.m_eQueue.m_nFree == 0U) {
270 m_ao.m_eQueue.m_nFree = 1U; // mutex lock nesting
271
272 // also the newly locked mutex must have no holder yet
273 Q_REQUIRE_INCRIT(303, m_ao.m_osObject == nullptr);
274
275 // set the new mutex holder to the curr thread and
276 // save the thread's prio in the mutex
277 // NOTE: reuse the otherwise unused eQueue data member.
278 m_ao.m_osObject = curr;
279 m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(curr->m_prio);
280
281 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio)
282 QS_TIME_PRE_(); // timestamp
283 QS_OBJ_PRE_(this); // this mutex
284 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
285 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
286 QS_END_PRE_()
287
288 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
289 // the holder prio. must be lower than that of the mutex
290 // and the prio. slot must be occupied by this mutex
291 Q_ASSERT_INCRIT(310, (curr->m_prio < m_ao.m_prio)
293
294 // remove the thread's original prio from the ready set
295 // and insert the mutex's prio into the ready set
297 static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head));
299 static_cast<std::uint_fast8_t>(m_ao.m_prio));
300 #ifndef Q_UNSAFE
302 #endif
303 // put the thread into the AO registry in place of the mutex
305
306 // set thread's prio to that of the mutex
307 curr->m_prio = m_ao.m_prio;
308 #ifndef Q_UNSAFE
309 curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio);
310 #endif
311 }
312 }
313 // is the mutex locked by this thread already (nested locking)?
314 else if (m_ao.m_osObject == curr) {
315 // the nesting level must not exceed the specified limit
316 Q_ASSERT_INCRIT(320, m_ao.m_eQueue.m_nFree < 0xFFU);
317
318 // lock one more level
319 m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree + 1U;
320
321 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->m_prio)
322 QS_TIME_PRE_(); // timestamp
323 QS_OBJ_PRE_(this); // this mutex
324 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
325 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
326 QS_END_PRE_()
327 }
328 else { // the mutex is already locked by a different thread
329 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
330 // the prio slot must be occupied by the thr. holding the mutex
332 == QXK_PTR_CAST_(QActive const *, m_ao.m_osObject));
333 }
334
335 QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->m_prio)
336 QS_TIME_PRE_(); // timestamp
337 QS_OBJ_PRE_(this); // this mutex
338 QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head),
339 curr->m_prio); // trying thread prio
340 QS_END_PRE_()
341
342 curr = nullptr; // means that mutex is NOT available
343 }
344 QF_MEM_APP();
345 QF_CRIT_EXIT();
346
347 return curr != nullptr;
348}
349
350//${QXK::QXMutex::unlock} ....................................................
351void QXMutex::unlock() noexcept {
354 QF_MEM_SYS();
355
356 QActive *curr = QXK_priv_.curr;
357 if (curr == nullptr) { // called from a basic thread?
359 }
360
362 && (curr != nullptr));
363 Q_REQUIRE_INCRIT(401, m_ao.m_eQueue.m_nFree > 0U);
364 Q_REQUIRE_INCRIT(403, m_ao.m_osObject == curr);
365
366 // is this the last nesting level?
367 if (m_ao.m_eQueue.m_nFree == 1U) {
368
369 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
370
372
373 // restore the holding thread's prio from the mutex
374 curr->m_prio =
375 static_cast<std::uint8_t>(m_ao.m_eQueue.m_head);
376 #ifndef Q_UNSAFE
377 curr->m_prio_dis = static_cast<std::uint8_t>(~curr->m_prio);
378 #endif
379
380 // put the mutex back into the AO registry
382
383 // remove the mutex' prio from the ready set
384 // and insert the original thread's prio.
386 static_cast<std::uint_fast8_t>(m_ao.m_prio));
388 static_cast<std::uint_fast8_t>(m_ao.m_eQueue.m_head));
389 #ifndef Q_UNSAFE
391 #endif
392 }
393
394 QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->m_prio)
395 QS_TIME_PRE_(); // timestamp
396 QS_OBJ_PRE_(this); // this mutex
397 QS_2U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head),
398 0U);
399 QS_END_PRE_()
400
401 // are any other threads waiting on this mutex?
402 if (m_waitSet.notEmpty()) {
403 // find the highest-prio. thread waiting on this mutex
404 std::uint_fast8_t const p = m_waitSet.findMax();
405
406 // remove this thread from waiting on the mutex
407 // and insert it into the ready set.
408 m_waitSet.remove(p);
410 #ifndef Q_UNSAFE
412 #endif
413
414 QXThread * const thr =
416
417 // the waiting thread must:
418 // - be registered in QF
419 // - have the prio. corresponding to the registration
420 // - be an extended thread
421 // - be blocked on this mutex
422 Q_ASSERT_INCRIT(420, (thr != nullptr)
423 && (thr->m_prio == static_cast<std::uint8_t>(p))
424 && (thr->m_state.act == Q_ACTION_CAST(0))
425 && (thr->m_temp.obj == QXK_PTR_CAST_(QMState*, this)));
426
427 // disarm the internal time event
428 static_cast<void>(thr->teDisarm_());
429
430 // set the new mutex holder to the curr thread and
431 // save the thread's prio in the mutex
432 // NOTE: reuse the otherwise unused eQueue data member.
433 m_ao.m_osObject = thr;
434 m_ao.m_eQueue.m_head = static_cast<QEQueueCtr>(thr->m_prio);
435
436 QS_BEGIN_PRE_(QS_MTX_LOCK, thr->m_prio)
437 QS_TIME_PRE_(); // timestamp
438 QS_OBJ_PRE_(this); // this mutex
439 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
440 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
441 QS_END_PRE_()
442
443 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
444 // the holder prio. must be lower than that of the mutex
446 && (thr->m_prio < m_ao.m_prio));
447
448 // put the thread into AO registry in place of the mutex
450 }
451 }
452 else { // no threads are waiting for this mutex
453 m_ao.m_eQueue.m_nFree = 0U; // free up the nesting count
454
455 // the mutex no longer held by any thread
456 m_ao.m_osObject = nullptr;
457 m_ao.m_eQueue.m_head = 0U;
458 m_ao.m_eQueue.m_tail = 0U;
459
460 if (m_ao.m_prio != 0U) { // prio.-ceiling protocol used?
461 // the AO priority must be in range
463
464 // put the mutex back at the original mutex slot
466 }
467 }
468
469 // schedule the next thread if multitasking started
470 if (QXK_sched_() != 0U) { // activation needed?
471 QXK_activate_(); // synchronously activate basic-thred(s)
472 }
473 }
474 else { // releasing one level of nested mutex lock
475 // unlock one level
476 m_ao.m_eQueue.m_nFree = m_ao.m_eQueue.m_nFree - 1U;
477
478 QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->m_prio)
479 QS_TIME_PRE_(); // timestamp
480 QS_OBJ_PRE_(this); // this mutex
481 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_head));
482 QS_U8_PRE_(static_cast<std::uint8_t>(m_ao.m_eQueue.m_nFree));
483 QS_END_PRE_()
484 }
485 QF_MEM_APP();
486 QF_CRIT_EXIT();
487}
488
489} // namespace QP
490//$enddef${QXK::QXMutex} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Active object class (based on the QHsm implementation strategy)
Definition qp.hpp:724
QACTIVE_EQUEUE_TYPE m_eQueue
Definition qp.hpp:738
static QActive * registry_[QF_MAX_ACTIVE+1U]
Definition qp.hpp:750
std::uint8_t m_prio_dis
Definition qp.hpp:744
std::uint8_t m_prio
Definition qp.hpp:726
void register_() noexcept
Definition qf_qact.cpp:76
QACTIVE_OS_OBJ_TYPE m_osObject
Definition qp.hpp:734
QAsmAttr m_state
Definition qp.hpp:222
QAsmAttr m_temp
Definition qp.hpp:223
QSignal sig
Definition qp.hpp:141
void update_(QPSet *const dis) const noexcept
Definition qp.hpp:681
void remove(std::uint_fast8_t const n) noexcept
Definition qp.hpp:658
std::uint_fast8_t findMax() const noexcept
Definition qp.hpp:670
void insert(std::uint_fast8_t const n) noexcept
Definition qp.hpp:646
bool notEmpty() const noexcept
Definition qp.hpp:630
bool lock(QTimeEvtCtr const nTicks=QXTHREAD_NO_TIMEOUT) noexcept
void unlock() noexcept
QActive m_ao
Definition qxk.hpp:166
void init(QPrioSpec const prioSpec) noexcept
Definition qxk_mutex.cpp:82
QPSet m_waitSet
Definition qxk.hpp:167
bool tryLock() noexcept
eXtended (blocking) thread of the QXK preemptive kernel
Definition qxk.hpp:88
bool teDisarm_() noexcept
Definition qxk_xthr.cpp:357
QTimeEvt m_timeEvt
Definition qxk.hpp:90
void teArm_(enum_t const sig, QTimeEvtCtr const nTicks) noexcept
Definition qxk_xthr.cpp:312
QP::QPSet readySet_dis
Definition qxk.hpp:195
QP::QActive *volatile curr
Definition qxk.hpp:186
std::uint_fast8_t volatile actPrio
Definition qxk.hpp:189
std::uint_fast8_t volatile lockHolder
Definition qxk.hpp:191
QP::QPSet readySet
Definition qxk.hpp:192
@ TIMEOUT_SIG
Definition qxk.hpp:335
QP/C++ framework.
Definition qequeue.hpp:50
std::uint32_t QTimeEvtCtr
Definition qp.hpp:588
std::uint16_t QPrioSpec
Definition qp.hpp:574
std::uint16_t QEQueueCtr
Definition qequeue.hpp:55
@ QS_MTX_BLOCK_ATTEMPT
a mutex blocking was attempted
Definition qs.hpp:184
@ QS_MTX_BLOCK
a mutex blocked a thread
Definition qs.hpp:181
@ QS_MTX_UNLOCK_ATTEMPT
a mutex unlock was attempted
Definition qs.hpp:185
@ QS_MTX_UNLOCK
a mutex was unlocked
Definition qs.hpp:182
@ QS_MTX_LOCK
a mutex was locked
Definition qs.hpp:180
int enum_t
Definition qp.hpp:108
#define QF_MEM_APP()
Definition qp.hpp:1276
#define Q_STATE_CAST(handler_)
Definition qp.hpp:493
#define QF_MEM_SYS()
Definition qp.hpp:1271
#define QF_CRIT_EXIT_NOP()
Definition qp.hpp:1266
#define QF_MAX_ACTIVE
Internal (package scope) QP/C++ interface.
#define Q_ACTION_CAST(act_)
Definition qp_pkg.hpp:82
Sample QP/C++ port.
#define QXK_ISR_CONTEXT_()
Check if the code executes in the ISR context.
Definition qp_port.hpp:137
#define QS_TIME_PRE_()
Definition qs.hpp:473
QS/C++ package-scope interface.
QS/C++ port to a 32-bit CPU, generic C++ compiler.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:58
#define Q_ASSERT_INCRIT(id_, expr_)
Definition qsafe.h:72
#define QF_CRIT_EXIT()
Definition qsafe.h:62
#define Q_REQUIRE_INCRIT(id_, expr_)
Definition qsafe.h:136
#define QF_CRIT_STAT
Definition qsafe.h:54
void QXK_activate_() noexcept
Definition qxk.cpp:236
QXK_Attr QXK_priv_
Definition qxk.cpp:174
#define QXK_PTR_CAST_(type_, ptr_)
Definition qxk.hpp:308
std::uint_fast8_t QXK_sched_() noexcept
Definition qxk.cpp:177
State object for the QP::QMsm class (QM State Machine).
Definition qp.hpp:189
QMState const * obj
Definition qp.hpp:208
QActionHandler act
Definition qp.hpp:206