QP/C Real-Time Embedded Framework (RTEF) This model is used to generate the whole QP/C source code. Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved. Q u a n t u m L e a P s ------------------------ Modern Embedded Software SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial The QP/C software is dual-licensed under the terms of the open-source GNU General Public License (GPL) or under the terms of one of the closed- source Quantum Leaps commercial licenses. Redistributions in source code must retain this top-level comment block. Plagiarizing this software to sidestep the license obligations is illegal. NOTE: The GPL (see <www.gnu.org/licenses/gpl-3.0>) does NOT permit the incorporation of the QP/C software into proprietary programs. Please contact Quantum Leaps for commercial licensing options, which expressly supersede the GPL and are designed explicitly for licensees interested in using QP/C in closed-source proprietary applications. Quantum Leaps contact information: <www.state-machine.com/licensing> <info@state-machine.com> public qpc 2025-12-31 Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved. Q u a n t u m L e a P s ------------------------ Modern Embedded Software SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial The QP/C software is dual-licensed under the terms of the open-source GNU General Public License (GPL) or under the terms of one of the closed- source Quantum Leaps commercial licenses. Redistributions in source code must retain this top-level comment block. Plagiarizing this software to sidestep the license obligations is illegal. NOTE: The GPL does NOT permit the incorporation of this code into proprietary programs. Please contact Quantum Leaps for commercial licensing options, which expressly supersede the GPL and are designed explicitly for closed-source distribution. Quantum Leaps contact information: <www.state-machine.com/licensing> <info@state-machine.com> #2BACD81DCE8ED122C193E4F48A14170D660DFF1E \ static char const Q_this_module_[] = name_; \ ((expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_))) \ (Q_onError(&Q_this_module_[0], (id_))) do { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ (expr_) ? ((void)0) : Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } while (false) do { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } while (false) ((void)0) ((void)0) ((void)0) ((void)0) Q_DEFINE_THIS_MODULE(__FILE__) Q_ASSERT_ID(__LINE__, (expr_)) Q_ERROR_ID(__LINE__) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) Q_ASSERT_INCRIT((id_), (expr_)) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) Q_ASSERT_INCRIT((id_), (expr_)) Q_ASSERT_ID((id_), (expr_)) Q_ASSERT(expr_) Q_ASSERT_INCRIT((id_), (expr_)) extern char Q_static_assert_[(expr_) ? 1 : -1] _Noreturn void (sizeof(array_) / sizeof((array_)[0U])) //! the current QP version number string in ROM, based on #QP_VERSION_STR = QP_VERSION_STR; //! @class QEvt //! @public @memberof QEvt //! @private @memberof QEvt //! @private @memberof QEvt = { QEVT_INITIALIZER(Q_EMPTY_SIG), QEVT_INITIALIZER(Q_ENTRY_SIG), QEVT_INITIALIZER(Q_EXIT_SIG), QEVT_INITIALIZER(Q_INIT_SIG) }; //! @public @memberof QEvt //! @public @memberof QEvt me->sig = (QSignal)sig; me->evtTag_ = 0x0FU; me->refCtr_ = 0U; //! @public @memberof QEvt //! @public @memberof QEvt (void)dummy; return me; const //! @private @memberof QEvt //! @private @memberof QEvt uint8_t rc = me->refCtr_; return (rc <= 2U*QF_MAX_ACTIVE) && (((me->evtTag_ ^ rc) & 0x0FU) == 0x0FU); const //! @private @memberof QEvt //! @private @memberof QEvt return (uint_fast8_t)(me->evtTag_ >> 4U); //! All possible values returned from state/action handlers //! @note //! The order of enumeration matters for algorithmic correctness. { // unhandled and need to "bubble up" Q_RET_SUPER, //!< event passed to superstate to handle Q_RET_UNHANDLED, //!< event unhandled due to guard // handled and do not need to "bubble up" Q_RET_HANDLED, //!< event handled (internal transition) Q_RET_IGNORED, //!< event silently ignored (bubbled up to top) // entry/exit Q_RET_ENTRY, //!< state entry action executed Q_RET_EXIT, //!< state exit action executed // no side effects Q_RET_NULL, //!< return value without any effect // transitions need to execute transition-action table in ::QMsm Q_RET_TRAN, //!< regular transition Q_RET_TRAN_INIT, //!< initial transition in a state // transitions that additionally clobber me->state Q_RET_TRAN_HIST, //!< transition to history of a given state }; )(void * const me, QEvt const * const e); )(void * const me); // forward declaration )(struct QXThread * const me); { struct QMState const *superstate; //!< @private @memberof QMState QStateHandler const stateHandler; //!< @private @memberof QMState QActionHandler const entryAction; //!< @private @memberof QMState QActionHandler const exitAction; //!< @private @memberof QMState QActionHandler const initAction; //!< @private @memberof QMState } QMState; { QMState const *target; //!< @private @memberof QMTranActTable QActionHandler const act[1]; //!< @private @memberof QMTranActTable } QMTranActTable; { QStateHandler fun; //!< @private @memberof QAsmAttr QActionHandler act; //!< @private @memberof QAsmAttr QXThreadHandler thr; //!< @private @memberof QAsmAttr QMTranActTable const *tatbl; //!< @private @memberof QAsmAttr struct QMState const *obj; //!< @private @memberof QAsmAttr #ifndef Q_UNSAFE uintptr_t uint; //!< @private @memberof QAsmAttr #endif }; //! @class QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm //! @protected @memberof QAsm me->vptr = (QAsmVtable *)0; me->state.fun = Q_STATE_CAST(0); me->temp.fun = Q_STATE_CAST(0); { void (*init)(QAsm * const me, void const * const e, uint_fast8_t const qsId); void (*dispatch)(QAsm * const me, QEvt const * const e, uint_fast8_t const qsId); bool (*isIn)(QAsm * const me, QStateHandler const s); #ifdef Q_SPY QStateHandler (*getStateHandler)(QAsm * const me); #endif // Q_SPY }; //! @class QHsm //! @extends QAsm State machine implementation strategy suitable for manual coding //! @protected @memberof QHsm //! @protected @memberof QHsm static struct QAsmVtable const vtable = { // QAsm virtual table &QHsm_init_, &QHsm_dispatch_, &QHsm_isIn_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; // do not call the QAsm_ctor() here me->super.vptr = &vtable; me->super.state.fun = Q_STATE_CAST(&QHsm_top); me->super.temp.fun = initial; //! @private @memberof QHsm //! @private @memberof QHsm QF_CRIT_STAT QState r; // produce QS dictionary for QHsm_top() #ifdef Q_SPY QS_CRIT_ENTRY(); QS_MEM_SYS(); if ((QS_priv_.flags & 0x01U) == 0U) { QS_priv_.flags |= 0x01U; r = Q_RET_HANDLED; } else { r = Q_RET_IGNORED; } QS_MEM_APP(); QS_CRIT_EXIT(); if (r == Q_RET_HANDLED) { QS_FUN_DICTIONARY(&QHsm_top); } #else Q_UNUSED_PAR(qsId); #endif // def Q_SPY QStateHandler t = me->state.fun; QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (me->vptr != (struct QAsmVtable *)0) && (me->temp.fun != Q_STATE_CAST(0)) && (t == Q_STATE_CAST(&QHsm_top))); QF_CRIT_EXIT(); // execute the top-most initial tran. r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt)); QF_CRIT_ENTRY(); // the top-most initial tran. must be taken Q_ASSERT_INCRIT(210, r == Q_RET_TRAN); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t); // the source state QS_FUN_PRE(me->temp.fun); // the target of the initial tran. QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); // drill down into the state hierarchy with initial transitions... do { QStateHandler path[QHSM_MAX_NEST_DEPTH_]; // tran. entry path array int_fast8_t ip = 0; // tran. entry path index path[0] = me->temp.fun; (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); // note: ip is the fixed upper loop bound while ((me->temp.fun != t) && (ip < (QHSM_MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = me->temp.fun; (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); } QF_CRIT_ENTRY(); // too many state nesting levels or "malformed" HSM Q_ENSURE_INCRIT(220, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); me->temp.fun = path[0]; // retrace the entry path in reverse (desired) order... // note: ip is the fixed upper loop bound do { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } --ip; } while (ip >= 0); t = path[0]; // current state becomes the new source r = QHSM_RESERVED_EVT_(t, Q_INIT_SIG); // execute initial tran. #ifdef Q_SPY if (r == Q_RET_TRAN) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t); // the source state QS_FUN_PRE(me->temp.fun); // the target of the initial tran. QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY } while (r == Q_RET_TRAN); QF_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_INIT_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t); // the new active state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); me->state.fun = t; // change the current active state #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QHsm //! @private @memberof QHsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QStateHandler s = me->state.fun; QStateHandler t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (e != (QEvt *)0) && (s != Q_STATE_CAST(0)) && (me->state.uint == (uintptr_t)(~me->temp.uint))); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(301, QEvt_verify_(e)); #endif QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_DISPATCH, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s); // the current state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); // process the event hierarchically... QState r; me->temp.fun = s; int_fast8_t ip = QHSM_MAX_NEST_DEPTH_; // fixed upper loop bound do { s = me->temp.fun; r = (*s)(me, e); // invoke state handler s if (r == Q_RET_UNHANDLED) { // unhandled due to a guard? QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId) QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s); // the current state QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // superstate of s } --ip; } while ((r == Q_RET_SUPER) && (ip > 0)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(310, ip > 0); QF_CRIT_EXIT(); if (r >= Q_RET_TRAN) { // tran. (regular or history) taken? #ifdef Q_SPY if (r == Q_RET_TRAN_HIST) { // tran. to history? QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_TRAN_HIST, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s); // tran. to history source QS_FUN_PRE(me->temp.fun); // tran. to history target QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY QStateHandler path[QHSM_MAX_NEST_DEPTH_]; path[0] = me->temp.fun; // tran. target path[1] = t; // current state path[2] = s; // tran. source // exit current state to tran. source s... ip = QHSM_MAX_NEST_DEPTH_; // fixed upper loop bound for (; (t != s) && (ip > 0); t = me->temp.fun) { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qsId); // find superstate of t (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); } --ip; } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(320, ip > 0); QF_CRIT_EXIT(); ip = QHsm_tran_(me, path, qsId); // take the tran. // execute state entry actions in the desired order... // note: ip is the fixed upper loop bound for (; ip >= 0; --ip) { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } } t = path[0]; // stick the target into register me->temp.fun = t; // update the next state // drill into the target hierarchy... while (QHSM_RESERVED_EVT_(t, Q_INIT_SIG) == Q_RET_TRAN) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t); // the source (pseudo)state QS_FUN_PRE(me->temp.fun); // the target of the tran. QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); ip = 0; path[0] = me->temp.fun; // find superstate (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); // note: ip is the fixed upper loop bound while ((me->temp.fun != t) && (ip < (QHSM_MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = me->temp.fun; // find superstate (void)QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); } QF_CRIT_ENTRY(); // too many state nesting levels or "malformed" HSM Q_ENSURE_INCRIT(330, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); me->temp.fun = path[0]; // retrace the entry path in reverse (correct) order... // note: ip is the fixed upper loop bound do { // enter path[ip] if (QHSM_RESERVED_EVT_(path[ip], Q_ENTRY_SIG) == Q_RET_HANDLED) { QS_STATE_ENTRY_(path[ip], qsId); } --ip; } while (ip >= 0); t = path[0]; // current state becomes the new source } QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s); // the source of the tran. QS_FUN_PRE(t); // the new active state QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } #ifdef Q_SPY else if (r == Q_RET_HANDLED) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_INTERN_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s); // the source state QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } else { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_IGNORED, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(me->state.fun); // the current state QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY me->state.fun = t; // change the current active state #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QHsm //! @private @memberof QHsm return me->state.fun; //! @private @memberof QHsm //! @private @memberof QHsm QF_CRIT_STAT QF_CRIT_ENTRY(); Q_INVARIANT_INCRIT(602, me->state.uint == (uintptr_t)(~me->temp.uint)); QF_CRIT_EXIT(); bool inState = false; // assume that this HSM is not in 'state' // scan the state hierarchy bottom-up QStateHandler s = me->state.fun; int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_ + 1; // fixed upper loop bound QState r = Q_RET_SUPER; for (; (r != Q_RET_IGNORED) && (lbound > 0); --lbound) { if (s == state) { // do the states match? inState = true; // 'true' means that match found break; // break out of the for-loop } else { r = QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); s = me->temp.fun; } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(690, lbound > 0); QF_CRIT_EXIT(); #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif return inState; // return the status const //! @public @memberof QHsm //! @public @memberof QHsm return me->super.state.fun; //! @public @memberof QHsm //! @public @memberof QHsm QStateHandler child = me->super.state.fun; // start with current state bool isFound = false; // start with the child not found // establish stable state configuration me->super.temp.fun = child; QState r; int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_; // fixed upper loop bound do { // is this the parent of the current child? if (me->super.temp.fun == parent) { isFound = true; // child is found r = Q_RET_IGNORED; // break out of the loop } else { child = me->super.temp.fun; r = QHSM_RESERVED_EVT_(me->super.temp.fun, Q_EMPTY_SIG); } --lbound; } while ((r != Q_RET_IGNORED) // the top state not reached && (lbound > 0)); #ifndef Q_UNSAFE me->super.temp.uint = ~me->super.state.uint; #else Q_UNUSED_PAR(isFound); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); // NOTE: the following postcondition can only succeed when // (lbound > 0), so no extra check is necessary. Q_ENSURE_INCRIT(890, isFound); QF_CRIT_EXIT(); return child; //! @private @memberof QHsm //! @private @memberof QHsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif int_fast8_t ip = -1; // tran. entry path index QStateHandler t = path[0]; QStateHandler const s = path[2]; QF_CRIT_STAT // (a) check source==target (tran. to self)... if (s == t) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } ip = 0; // enter the target } else { // find superstate of target (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); t = me->temp.fun; // (b) check source==target->super... if (s == t) { ip = 0; // enter the target } else { // find superstate of src (void)QHSM_RESERVED_EVT_(s, Q_EMPTY_SIG); // (c) check source->super==target->super... if (me->temp.fun == t) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } ip = 0; // enter the target } else { // (d) check source->super==target... if (me->temp.fun == path[0]) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } } else { // (e) check rest of source==target->super->super.. // and store the entry path along the way int_fast8_t iq = 0; // indicate that LCA was found ip = 1; // enter target and its superstate path[1] = t; // save the superstate of target t = me->temp.fun; // save source->super // find target->super->super... // note: ip is the fixed upper loop bound QState r = QHSM_RESERVED_EVT_(path[1], Q_EMPTY_SIG); while ((r == Q_RET_SUPER) && (ip < (QHSM_MAX_NEST_DEPTH_ - 1))) { ++ip; path[ip] = me->temp.fun; // store the entry path if (me->temp.fun == s) { // is it the source? iq = 1; // indicate that the LCA found --ip; // do not enter the source r = Q_RET_HANDLED; // terminate the loop } else { // it is not the source, keep going up r = QHSM_RESERVED_EVT_(me->temp.fun, Q_EMPTY_SIG); } } QF_CRIT_ENTRY(); // NOTE: The following postcondition succeeds only when // ip < QHSM_MAX_NEST_DEPTH, so no additional check is necessary // too many state nesting levels or "malformed" HSM. Q_ENSURE_INCRIT(510, r != Q_RET_SUPER); QF_CRIT_EXIT(); // the LCA not found yet? if (iq == 0) { // exit source s if (QHSM_RESERVED_EVT_(s, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(s, qsId); } // (f) check the rest of source->super // == target->super->super... iq = ip; r = Q_RET_IGNORED; // indicate that the LCA NOT found // note: iq is the fixed upper loop bound do { if (t == path[iq]) { // is this the LCA? r = Q_RET_HANDLED; // indicate the LCA found ip = iq - 1; // do not enter the LCA iq = -1; // cause termination of the loop } else { --iq; // try lower superstate of target } } while (iq >= 0); // the LCA not found yet? if (r != Q_RET_HANDLED) { // (g) check each source->super->... // for each target->super... r = Q_RET_IGNORED; // keep looping int_fast8_t lbound = QHSM_MAX_NEST_DEPTH_; do { // exit from t if (QHSM_RESERVED_EVT_(t, Q_EXIT_SIG) == Q_RET_HANDLED) { QS_STATE_EXIT_(t, qsId); // find superstate of t (void)QHSM_RESERVED_EVT_(t, Q_EMPTY_SIG); } t = me->temp.fun; // set to super of t iq = ip; do { // is this the LCA? if (t == path[iq]) { ip = iq - 1; // do not enter the LCA iq = -1; // break out of inner loop r = Q_RET_HANDLED; // break outer loop } else { --iq; } } while (iq >= 0); --lbound; } while ((r != Q_RET_HANDLED) && (lbound > 0)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(530, lbound > 0); QF_CRIT_EXIT(); } } } } } } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(590, ip < QHSM_MAX_NEST_DEPTH_); QF_CRIT_EXIT(); return ip; const //! @protected @memberof QAsm //! @protected @memberof QAsm Q_UNUSED_PAR(me); Q_UNUSED_PAR(e); return Q_RET_IGNORED; // the top state ignores all events //! @class QMsm //! @extends QAsm State machine implementation strategy requiring support of a code generating tool and generally NOT suitable for manual coding //! @protected @memberof QMsm //! @protected @memberof QMsm static struct QAsmVtable const vtable = { // QAsm virtual table &QMsm_init_, &QMsm_dispatch_, &QMsm_isIn_ #ifdef Q_SPY ,&QMsm_getStateHandler_ #endif }; // do not call the QAsm_ctor() here me->super.vptr = &vtable; me->super.state.obj = &l_msm_top_s; // the current state (top) me->super.temp.fun = initial; // the initial tran. handler //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(200, (me->vptr != (struct QAsmVtable *)0) && (me->temp.fun != Q_STATE_CAST(0)) && (me->state.obj == &l_msm_top_s)); QF_CRIT_EXIT(); // execute the top-most initial tran. QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt)); QF_CRIT_ENTRY(); // the top-most initial tran. must be taken Q_ASSERT_INCRIT(210, r == Q_RET_TRAN_INIT); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(me->state.obj->stateHandler); // source state QS_FUN_PRE(me->temp.tatbl->target->stateHandler); // target state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); // set state to the last tran. target me->state.obj = me->temp.tatbl->target; // drill down into the state hierarchy with initial transitions... int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound do { // execute the tran. table r = QMsm_execTatbl_(me, me->temp.tatbl, qsId); --lbound; } while ((r >= Q_RET_TRAN_INIT) && (lbound > 0)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(290, lbound > 0); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_INIT_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(me->state.obj->stateHandler); // the new current state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QMState const *s = me->state.obj; // store the current state QMState const *t = s; QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (e != (QEvt *)0) && (s != (QMState *)0) && (me->state.uint == (uintptr_t)(~me->temp.uint))); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(301, QEvt_verify_(e)); #endif QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_DISPATCH, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s->stateHandler); // the current state handler QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); // scan the state hierarchy up to the top state... QState r; int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound do { r = (*t->stateHandler)(me, e); // call state handler function // event handled? (the most frequent case) if (r >= Q_RET_HANDLED) { break; // done scanning the state hierarchy } // event unhandled and passed to the superstate? else if (r == Q_RET_SUPER) { t = t->superstate; // advance to the superstate } else { // event unhandled due to a guard QF_CRIT_ENTRY(); // event must be unhandled due to a guard evaluating to 'false' Q_ASSERT_INCRIT(310, r == Q_RET_UNHANDLED); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId) QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t->stateHandler); // the current state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); t = t->superstate; // advance to the superstate } --lbound; } while ((t != (QMState *)0) && (lbound > 0)); QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(320, lbound > 0); QF_CRIT_EXIT(); if (r >= Q_RET_TRAN) { // any kind of tran. taken? QF_CRIT_ENTRY(); // the tran. source state must not be NULL Q_ASSERT_INCRIT(330, t != (QMState *)0); QF_CRIT_EXIT(); #ifdef Q_SPY QMState const * const ts = t; // tran. source for QS tracing #endif // Q_SPY struct QMTranActTable const *tatbl; // for saving tran. table if (r == Q_RET_TRAN_HIST) { // was it tran. to history? QMState const * const hist = me->state.obj; // save history me->state.obj = s; // restore the original state QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_TRAN_HIST, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t->stateHandler); // source state handler QS_FUN_PRE(hist->stateHandler); // target state handler QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); // save the tran-action table before it gets clobbered tatbl = me->temp.tatbl; QMsm_exitToTranSource_(me, s, t, qsId); (void)QMsm_execTatbl_(me, tatbl, qsId); r = QMsm_enterHistory_(me, hist, qsId); s = me->state.obj; t = s; // set target to the current state } lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound while ((r >= Q_RET_TRAN) && (lbound > 0)) { // save the tran-action table before it gets clobbered tatbl = me->temp.tatbl; me->temp.obj = (QMState *)0; // clear QMsm_exitToTranSource_(me, s, t, qsId); r = QMsm_execTatbl_(me, tatbl, qsId); s = me->state.obj; t = s; // set target to the current state --lbound; } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(360, lbound > 0); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(ts->stateHandler); // the tran. source QS_FUN_PRE(s->stateHandler); // the new active state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); } #ifdef Q_SPY // was the event handled? else if (r == Q_RET_HANDLED) { QF_CRIT_ENTRY(); // internal tran. source can't be NULL Q_ASSERT_INCRIT(380, t != (QMState *)0); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_INTERN_TRAN, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(t->stateHandler); // the source state QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); } // event bubbled to the 'top' state? else if (t == (QMState *)0) { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_IGNORED, qsId) QS_TIME_PRE(); // time stamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s->stateHandler); // the current state QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } #endif // Q_SPY else { // empty } #ifndef Q_UNSAFE me->temp.uint = ~me->state.uint; #endif //! @public @memberof QMsm //! @public @memberof QMsm return me->state.obj->stateHandler; //! @private @memberof QMsm //! @private @memberof QMsm bool inState = false; // assume that this SM is not in 'state' QMState const *s = me->state.obj; int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound for (; (s != (QMState *)0) && (lbound > 0); --lbound) { if (s->stateHandler == state) { // match found? inState = true; break; } else { s = s->superstate; // advance to the superstate } } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(490, lbound > 0); QF_CRIT_EXIT(); return inState; const //! @public @memberof QMsm //! @public @memberof QMsm return me->super.state.obj; const //! @public @memberof QMsm //! @public @memberof QMsm QMState const *child = me->super.state.obj; bool isFound = false; // start with the child not found QMState const *s; int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound for (s = me->super.state.obj; (s != (QMState *)0) && (lbound > 0); s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } --lbound; } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(680, lbound > 0); QF_CRIT_EXIT(); if (!isFound) { // still not found? lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound for (s = me->super.temp.obj; (s != (QMState *)0) && (lbound > 0); s = s->superstate) { if (s == parent) { isFound = true; // child is found break; } else { child = s; } --lbound; } } QF_CRIT_ENTRY(); // NOTE: the following postcondition can only succeed when // (lbound > 0), so no extra check is necessary. Q_ENSURE_INCRIT(690, isFound); QF_CRIT_EXIT(); return child; // return the child //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); // precondition: // - the tran-action table pointer must not be NULL Q_REQUIRE_INCRIT(700, tatbl != (struct QMTranActTable *)0); QF_CRIT_EXIT(); QState r = Q_RET_NULL; int_fast8_t lbound = QMSM_MAX_TRAN_LENGTH_; // fixed upper loop bound QActionHandler const *a = &tatbl->act[0]; for (; (*a != Q_ACTION_CAST(0)) && (lbound > 0); ++a) { r = (*(*a))(me); // call the action through the 'a' pointer --lbound; #ifdef Q_SPY QS_CRIT_ENTRY(); QS_MEM_SYS(); if (r == Q_RET_ENTRY) { QS_BEGIN_PRE(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(me->temp.obj->stateHandler); // entered state QS_END_PRE() } else if (r == Q_RET_EXIT) { QS_BEGIN_PRE(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(me->temp.obj->stateHandler); // exited state QS_END_PRE() } else if (r == Q_RET_TRAN_INIT) { QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(tatbl->target->stateHandler); // source QS_FUN_PRE(me->temp.tatbl->target->stateHandler); // target QS_END_PRE() } else { // empty } QS_MEM_APP(); QS_CRIT_EXIT(); #endif // Q_SPY } QF_CRIT_ENTRY(); // NOTE: the following postcondition can only succeed when // (lbound > 0), so no extra check is necessary. Q_ENSURE_INCRIT(790, *a == Q_ACTION_CAST(0)); QF_CRIT_EXIT(); me->state.obj = (r >= Q_RET_TRAN) ? me->temp.tatbl->target : tatbl->target; return r; //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT // exit states from the current state to the tran. source state QMState const *s = cs; int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_; // fixed upper loop bound for (; (s != ts) && (lbound > 0); --lbound) { // exit action provided in state 's'? if (s->exitAction != Q_ACTION_CAST(0)) { // execute the exit action (void)(*s->exitAction)(me); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_EXIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(s->stateHandler); // the exited state handler QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } s = s->superstate; // advance to the superstate } QF_CRIT_ENTRY(); Q_ENSURE_INCRIT(890, lbound > 0); QF_CRIT_EXIT(); //! @private @memberof QMsm //! @private @memberof QMsm #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif // record the entry path from current state to history QMState const *epath[QMSM_MAX_ENTRY_DEPTH_]; QMState const *s = hist; int_fast8_t i = 0; // tran. entry path index while ((s != me->state.obj) && (i < (QMSM_MAX_ENTRY_DEPTH_ - 1))) { if (s->entryAction != Q_ACTION_CAST(0)) { epath[i] = s; ++i; } s = s->superstate; } QF_CRIT_STAT QF_CRIT_ENTRY(); Q_ASSERT_INCRIT(910, s == me->state.obj); QF_CRIT_EXIT(); // retrace the entry path in reverse (desired) order... while (i > 0) { --i; (void)(*epath[i]->entryAction)(me); // run entry action in epath[i] QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_ENTRY, qsId) QS_OBJ_PRE(me); QS_FUN_PRE(epath[i]->stateHandler); // entered state handler QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } me->state.obj = hist; // set current state to the tran. target // initial tran. present? QState r; if (hist->initAction != Q_ACTION_CAST(0)) { r = (*hist->initAction)(me); // execute the tran. action QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QEP_STATE_INIT, qsId) QS_OBJ_PRE(me); // this state machine object QS_FUN_PRE(hist->stateHandler); // source QS_FUN_PRE(me->temp.tatbl->target->stateHandler); // target QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } else { r = Q_RET_NULL; } return r; { (QSignal)(sig_), 0x01U, 0x0EU } ((uint8_t)0) do { \ Q_ASSERT(((QAsm *)(me_))->vptr); \ (*((QAsm *)(me_))->vptr->init)((QAsm *)(me_), (par_), (qsId_)); \ } while (false) do { \ Q_ASSERT(((QAsm *)(me_))->vptr); \ (*((QAsm *)(me_))->vptr->init)((QAsm *)(me_), (par_), 0); \ } while (false) \ (*((QAsm *)(me_))->vptr->dispatch)((QAsm *)(me_), (e_), (qsId_)) \ (*((QAsm *)(me_))->vptr->dispatch)((QAsm *)(me_), (e_), 0U) \ (*((QAsm *)(me_))->vptr->isIn)((QAsm *)(me_), (state_)) ((QAsm *)(ptr_)) ((QHsm *)(ptr_)) ((QMsm *)(ptr_)) ((QSignal)0) ((QSignal)1) ((QSignal)2) ((QSignal)3) ((enum_t)4) \ ((Q_ASM_UPCAST(me))->temp.fun = Q_STATE_CAST(target_), \ (QState)Q_RET_TRAN) \ ((Q_ASM_UPCAST(me))->temp.fun = (hist_), \ (QState)Q_RET_TRAN_HIST) \ ((Q_ASM_UPCAST(me))->temp.fun = Q_STATE_CAST(super_), \ (QState)Q_RET_SUPER) ((QState)Q_RET_HANDLED) ((QState)Q_RET_UNHANDLED) / ((QActionHandler)0) ((class_ const *)(e)) ((QStateHandler)(handler_)) ((QActionHandler)(action_)) ((void)(par_)) (sizeof(array_) / sizeof((array_)[0U])) ((type_ *)(uint_)) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_ENTRY) ((QState)Q_RET_ENTRY) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_EXIT) ((QState)Q_RET_EXIT) \ ((Q_ASM_UPCAST(me))->temp.obj = (state_), \ (QState)Q_RET_EXIT) ((Q_ASM_UPCAST(me))->temp.tatbl \ = (struct QMTranActTable const *)(tatbl_), \ (QState)Q_RET_TRAN) ((Q_ASM_UPCAST(me))->temp.tatbl \ = (struct QMTranActTable const *)(tatbl_), \ (QState)Q_RET_TRAN_INIT) \ ((((Q_ASM_UPCAST(me))->state.obj = (history_)), \ ((Q_ASM_UPCAST(me))->temp.tatbl = \ (struct QMTranActTable const *)(tatbl_))), \ (QState)Q_RET_TRAN_HIST) ((QState)Q_RET_HANDLED) ((QState)Q_RET_UNHANDLED) ((QState)Q_RET_SUPER) ((QMState *)0) //! @class QF { //! @cond INTERNAL uint8_t dummy; //! @endcond } QV; const static uint8_t const log2LUT[16] = { 0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U }; uint_fast8_t n = 0U; QPSetBits tmp; QPSetBits x = bitmask; #if (QF_MAX_ACTIVE > 16U) tmp = (x >> 16U); if (tmp != 0U) { n += 16U; x = tmp; } #endif #if (QF_MAX_ACTIVE > 8U) tmp = (x >> 8U); if (tmp != 0U) { n += 8U; x = tmp; } #endif tmp = (x >> 4U); if (tmp != 0U) { n += 4U; x = tmp; } return n + log2LUT[x]; //! @class QPSet //! @private @memberof QPSet //! @public @memberof QPSet //! @public @memberof QPSet me->bits[0] = 0U; #if (QF_MAX_ACTIVE > 32) me->bits[1] = 0U; #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] == 0U); #else return (me->bits[0] == 0U) ? (me->bits[1] == 0U) : false; #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] != 0U); #else return (me->bits[0] != 0U) ? true : (me->bits[1] != 0U); #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return (me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U; #else return (n <= 32U) ? ((me->bits[0] & ((QPSetBits)1U << (n - 1U))) != 0U) : ((me->bits[1] & ((QPSetBits)1U << (n - 33U))) != 0U); #endif //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); #else if (n <= 32U) { me->bits[0] = (me->bits[0] | ((QPSetBits)1U << (n - 1U))); } else { me->bits[1] = (me->bits[1] | ((QPSetBits)1U << (n - 33U))); } #endif //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) me->bits[0] = (me->bits[0] & (QPSetBits)(~((QPSetBits)1U << (n - 1U)))); #else if (n <= 32U) { (me->bits[0] = (me->bits[0] & ~((QPSetBits)1U << (n - 1U)))); } else { (me->bits[1] = (me->bits[1] & ~((QPSetBits)1U << (n - 33U)))); } #endif const //! @public @memberof QPSet //! @public @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return QF_LOG2(me->bits[0]); #else return (me->bits[1] != 0U) ? (QF_LOG2(me->bits[1]) + 32U) : (QF_LOG2(me->bits[0])); #endif const //! @private @memberof QPSet //! @private @memberof QPSet dis->bits[0] = ~me->bits[0]; #if (QF_MAX_ACTIVE > 32U) dis->bits[1] = ~me->bits[1]; #endif const //! @private @memberof QPSet //! @private @memberof QPSet #if (QF_MAX_ACTIVE <= 32U) return me->bits[0] == (QPSetBits)(~dis->bits[0]); #else return (me->bits[0] == (QPSetBits)(~dis->bits[0])) && (me->bits[1] == (QPSetBits)(~dis->bits[1])); #endif //! @struct QSubscrList //! @private @memberof QSubscrList //! @private @memberof QSubscrList //! @class QActive //! @extends QAsm //! @protected @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @static @private @memberof QActive //! @static @private @memberof QActive //! @static @private @memberof QActive //! @static @private @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive // clear the whole QActive object, so that the framework can start // correctly even if the startup code fails to clear the uninitialized // data (as is required by the C Standard). QF_bzero_(me, sizeof(*me)); // NOTE: QActive inherits the abstract QAsm class, but it calls the // constructor of the QHsm subclass. This is because QActive inherits // the behavior from the QHsm subclass. QHsm_ctor((QHsm *)(me), initial); // NOTE: this vtable is identical as QHsm, but is provided // for the QActive subclass to provide a UNIQUE vptr to distinguish // subclasses of QActive (e.g., in the debugger). static struct QAsmVtable const vtable = { // QActive virtual table &QHsm_init_, &QHsm_dispatch_, &QHsm_isIn_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.vptr = &vtable; // hook vptr to QActive vtable //! @public @memberof QActive //! @public @memberof QActive //! @public @memberof QActive //! @public @memberof QActive //! @protected @memberof QActive //! @protected @memberof QActive //! @private @memberof QActive //! @private @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); if (me->pthre == 0U) { // preemption-threshold not defined? me->pthre = me->prio; // apply the default } #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(100, (0U < me->prio) && (me->prio <= QF_MAX_ACTIVE) && (QActive_registry_[me->prio] == (QActive *)0) && (me->prio <= me->pthre)); uint8_t prev_thre = me->pthre; uint8_t next_thre = me->pthre; uint_fast8_t p; for (p = (uint_fast8_t)me->prio - 1U; p > 0U; --p) { if (QActive_registry_[p] != (QActive *)0) { prev_thre = QActive_registry_[p]->pthre; break; } } for (p = (uint_fast8_t)me->prio + 1U; p <= QF_MAX_ACTIVE; ++p) { if (QActive_registry_[p] != (QActive *)0) { next_thre = QActive_registry_[p]->pthre; break; } } Q_ASSERT_INCRIT(190, (prev_thre <= me->pthre) && (me->pthre <= next_thre)); me->prio_dis = (uint8_t)(~me->prio); me->pthre_dis = (uint8_t)(~me->pthre); #endif // Q_UNSAFE // register the AO at the QF-prio. QActive_registry_[me->prio] = me; QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); QActive_registry_[p] = (QActive *)0; // free-up the prio. level me->super.state.fun = Q_STATE_CAST(0); // invalidate the state QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif #ifdef Q_UTEST // test? #if (Q_UTEST != 0) // testing QP-stub? if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? return QActiveDummy_fakePost_(me, e, margin, sender); } #endif // (Q_UTEST != 0) #endif // def Q_UTEST QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, e != (QEvt *)0); QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary #ifndef Q_UNSAFE QEQueueCtr dis = (QEQueueCtr)~me->eQueue.nFree_dis; Q_INVARIANT_INCRIT(201, (QEvt_verify_(e)) && (tmp == dis)); #endif // ndef Q_UNSAFE // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QActive_post_) QS_TEST_PROBE_ID(1, tmp = 0U; // fake no free events ) // required margin available? bool status; if (margin == QF_NO_MARGIN) { if (tmp > 0U) { // free entries available in the queue? status = true; // can post } else { // no free entries available status = false; // cannot post // The queue overflows, but QF_NO_MARGIN indicates that // the "event delivery guarantee" is required. Q_ERROR_INCRIT(210); // must be able to post the event } } else if (tmp > (QEQueueCtr)margin) { // enough free entries? status = true; // can post } else { // the # free entries below the requested margin status = false; // cannot post, but don't assert } // is it a mutable event? if (QEvt_getPoolNum_(e) != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } if (status) { // can post the event? --tmp; // one free entry just used up me->eQueue.nFree = tmp; // update the original #ifndef Q_UNSAFE me->eQueue.nFree_dis = (QEQueueCtr)~tmp; // update the DIS if (me->eQueue.nMin > tmp) { me->eQueue.nMin = tmp; // update minimum so far } #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_ACTIVE_POST, me->prio) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(sender); // the sender object QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this active object (recipient) QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries #ifndef Q_UNSAFE QS_EQC_PRE(me->eQueue.nMin); // min # free entries #else QS_EQC_PRE(0U); // min # free entries #endif QS_END_PRE() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QF_MEM_APP(); QF_CRIT_EXIT(); QS_onTestPost(sender, me, e, status); QF_CRIT_ENTRY(); QF_MEM_SYS(); } #endif // def Q_UTEST if (me->eQueue.frontEvt == (QEvt *)0) { // is the queue empty? me->eQueue.frontEvt = e; // deliver event directly #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(211, me->eQueue.frontEvt_dis == (uintptr_t)~Q_PTR2UINT_CAST_((QEvt *)0)); me->eQueue.frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(e); #endif // ndef Q_UNSAFE #ifdef QXK_H_ if (me->super.state.act == Q_ACTION_CAST(0)) { // eXtended? QXTHREAD_EQUEUE_SIGNAL_(me); // signal eXtended Thread } else { QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object } #else QACTIVE_EQUEUE_SIGNAL_(me); // signal the Active Object #endif // def QXK_H_ } else { // queue was not empty, insert event into the ring-buffer tmp = me->eQueue.head; // get volatile into temporary #ifndef Q_UNSAFE dis = (QEQueueCtr)~me->eQueue.head_dis; Q_INVARIANT_INCRIT(212, tmp == dis); #endif // ndef Q_UNSAFE me->eQueue.ring[tmp] = e; // insert e into buffer if (tmp == 0U) { // need to wrap the head? tmp = me->eQueue.end; } --tmp; // advance the head (counter-clockwise) me->eQueue.head = tmp; // update the original #ifndef Q_UNSAFE me->eQueue.head_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE } QF_MEM_APP(); QF_CRIT_EXIT(); } else { // event cannot be posted QS_BEGIN_PRE(QS_QF_ACTIVE_POST_ATTEMPT, me->prio) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(sender); // the sender object QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this active object (recipient) QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries QS_EQC_PRE(margin); // margin requested QS_END_PRE() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QF_MEM_APP(); QF_CRIT_EXIT(); QS_onTestPost(sender, me, e, status); QF_CRIT_ENTRY(); QF_MEM_SYS(); } #endif // def Q_USTEST QF_MEM_APP(); QF_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) QF_gc(e); // recycle the event to avoid a leak #endif // (QF_MAX_EPOOL > 0U) } return status; //! @private @memberof QActive //! @private @memberof QActive #ifdef Q_UTEST // test? #if (Q_UTEST != 0) // testing QP-stub? if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy? QActiveDummy_fakePostLIFO_(me, e); return; } #endif // (Q_UTEST != 0) #endif // def Q_UTEST QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // the posted event must be be valid (which includes not NULL) Q_REQUIRE_INCRIT(300, e != (QEvt *)0); QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary #ifndef Q_UNSAFE QEQueueCtr dis = (QEQueueCtr)~me->eQueue.nFree_dis; Q_INVARIANT_INCRIT(301, (QEvt_verify_(e)) && (tmp == dis)); #endif // ndef Q_UNSAFE // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QActive_postLIFO_) QS_TEST_PROBE_ID(1, tmp = 0U; // fake no free events ) // The queue must NOT overflow for the LIFO posting policy. Q_REQUIRE_INCRIT(310, tmp != 0U); if (QEvt_getPoolNum_(e) != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --tmp; // one free entry just used up me->eQueue.nFree = tmp; // update the original #ifndef Q_UNSAFE me->eQueue.nFree_dis = (QEQueueCtr)~tmp; if (me->eQueue.nMin > tmp) { me->eQueue.nMin = tmp; // update minimum so far } #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_ACTIVE_POST_LIFO, me->prio) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries #ifndef Q_UNSAFE QS_EQC_PRE(me->eQueue.nMin); // min # free entries #else QS_EQC_PRE(0U); // min # free entries #endif QS_END_PRE() #ifdef Q_UTEST // callback to examine the posted event under the same conditions // as producing the #QS_QF_ACTIVE_POST trace record, which are: // the local filter for this AO ('me->prio') is set if (QS_LOC_CHECK_(me->prio)) { QF_MEM_APP(); QF_CRIT_EXIT(); QS_onTestPost((QActive *)0, me, e, true); QF_CRIT_ENTRY(); QF_MEM_SYS(); } #endif // def Q_UTEST QEvt const * const frontEvt = me->eQueue.frontEvt; me->eQueue.frontEvt = e; // deliver the event directly to the front #ifndef Q_UNSAFE me->eQueue.frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(e); #endif // ndef Q_UNSAFE if (frontEvt != (QEvt *)0) { // was the queue NOT empty? tmp = me->eQueue.tail; // get volatile into temporary; #ifndef Q_UNSAFE dis = (QEQueueCtr)~me->eQueue.tail_dis; Q_INVARIANT_INCRIT(311, tmp == dis); #endif // ndef Q_UNSAFE ++tmp; if (tmp == me->eQueue.end) { // need to wrap the tail? tmp = 0U; // wrap around } me->eQueue.tail = tmp; #ifndef Q_UNSAFE me->eQueue.tail_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE me->eQueue.ring[tmp] = frontEvt; } else { // queue was empty QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue } QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QActive //! @private @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // wait for event to arrive directly (depends on QP port) // NOTE: might use assertion-IDs 400-409 QACTIVE_EQUEUE_WAIT_(me); // always remove event from the front QEvt const * const e = me->eQueue.frontEvt; QEQueueCtr tmp = me->eQueue.nFree; // get volatile into temporary #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(410, e != (QEvt *)0); // queue must NOT be empty Q_INVARIANT_INCRIT(411, Q_PTR2UINT_CAST_(e) == (uintptr_t)~me->eQueue.frontEvt_dis); QEQueueCtr dis = (QEQueueCtr)~me->eQueue.nFree_dis; Q_INVARIANT_INCRIT(412, tmp == dis); #endif // ndef Q_UNSAFE ++tmp; // one more free event in the queue me->eQueue.nFree = tmp; // update the # free #ifndef Q_UNSAFE me->eQueue.nFree_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE if (tmp <= me->eQueue.end) { // any events in the ring buffer? QS_BEGIN_PRE(QS_QF_ACTIVE_GET, me->prio) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries QS_END_PRE() // remove event from the tail tmp = me->eQueue.tail; // get volatile into temporary #ifndef Q_UNSAFE dis = (QEQueueCtr)~me->eQueue.tail_dis; Q_INVARIANT_INCRIT(420, tmp == dis); #endif // ndef Q_UNSAFE QEvt const * const frontEvt = me->eQueue.ring[tmp]; #ifndef Q_UNSAFE Q_ASSERT_INCRIT(421, frontEvt != (QEvt *)0); me->eQueue.frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(frontEvt); #endif // ndef Q_UNSAFE me->eQueue.frontEvt = frontEvt; // update the original if (tmp == 0U) { // need to wrap the tail? tmp = me->eQueue.end; } --tmp; // advance the tail (counter-clockwise) me->eQueue.tail = tmp; // update the original #ifndef Q_UNSAFE me->eQueue.tail_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE } else { me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty #ifndef Q_UNSAFE me->eQueue.frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_((QEvt *)0); #endif // ndef Q_UNSAFE // all entries in the queue must be free (+1 for fronEvt) Q_ASSERT_INCRIT(440, tmp == (me->eQueue.end + 1U)); QS_BEGIN_PRE(QS_QF_ACTIVE_GET_LAST, me->prio) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_END_PRE() } QF_MEM_APP(); QF_CRIT_EXIT(); return e; //! @static @public @memberof QActive //! @static @public @memberof QActive QActive_subscrList_ = subscrSto; QActive_maxPubSignal_ = maxSignal; // initialize the subscriber list for (enum_t sig = 0; sig < maxSignal; ++sig) { QPSet_setEmpty(&subscrSto[sig].set); #ifndef Q_UNSAFE QPSet_update_(&subscrSto[sig].set, &subscrSto[sig].set_dis); #endif } //! @static @private @memberof QActive //! @static @private @memberof QActive #ifndef Q_SPY Q_UNUSED_PAR(sender); Q_UNUSED_PAR(qsId); #endif QSignal const sig = e->sig; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, sig < (QSignal)QActive_maxPubSignal_); Q_INVARIANT_INCRIT(202, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE(QS_QF_PUBLISH, qsId) QS_TIME_PRE(); // the timestamp QS_OBJ_PRE(sender); // the sender object QS_SIG_PRE(sig); // the signal of the event QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_END_PRE() // is it a mutable event? if (QEvt_getPoolNum_(e) != 0U) { // NOTE: The reference counter of a mutable event is incremented to // prevent premature recycling of the event while the multicasting // is still in progress. At the end of the function, the garbage // collector step (QF_gc()) decrements the reference counter and // recycles the event if the counter drops to zero. This covers the // case when the event was published without any subscribers. QEvt_refCtr_inc_(e); } // make a local, modifiable copy of the subscriber set QPSet subscrSet = QActive_subscrList_[sig].set; QF_MEM_APP(); QF_CRIT_EXIT(); if (QPSet_notEmpty(&subscrSet)) { // any subscribers? // highest-prio subscriber uint_fast8_t p = QPSet_findMax(&subscrSet); QF_CRIT_ENTRY(); QF_MEM_SYS(); QActive *a = QActive_registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(210, a != (QActive *)0); QF_MEM_APP(); QF_CRIT_EXIT(); QF_SCHED_STAT_ QF_SCHED_LOCK_(p); // lock the scheduler up to AO's prio uint_fast8_t lbound = QF_MAX_ACTIVE + 1U; // fixed upper loop bound do { // loop over all subscribers --lbound; // QACTIVE_POST() asserts internally if the queue overflows QACTIVE_POST(a, e, sender); QPSet_remove(&subscrSet, p); // remove the handled subscriber if (QPSet_notEmpty(&subscrSet)) { // still more subscribers? p = QPSet_findMax(&subscrSet); // highest-prio subscriber QF_CRIT_ENTRY(); QF_MEM_SYS(); a = QActive_registry_[p]; // the AO must be registered with the framework Q_ASSERT_INCRIT(220, a != (QActive *)0); QF_MEM_APP(); QF_CRIT_EXIT(); } else { p = 0U; // no more subscribers } } while ((p != 0U) && (lbound > 0U)); QF_CRIT_ENTRY(); // NOTE: the following postcondition can only succeed when // (lbound > 0), so no extra check for lbound is necessary. Q_ENSURE_INCRIT(290, p == 0U); // all subscribers processed QF_CRIT_EXIT(); QF_SCHED_UNLOCK_(); // unlock the scheduler } // The following garbage collection step decrements the reference counter // and recycles the event if the counter drops to zero. This covers both // cases when the event was published with or without any subscribers. #if (QF_MAX_EPOOL > 0U) QF_gc(e); // recycle the event to avoid a leak #endif const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, (Q_USER_SIG <= sig) && (sig < QActive_maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); Q_INVARIANT_INCRIT(302, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE(QS_QF_ACTIVE_SUBSCRIBE, p) QS_TIME_PRE(); // timestamp QS_SIG_PRE(sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_END_PRE() // insert the prio. into the subscriber set QPSet_insert(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast8_t const p = (uint_fast8_t)me->prio; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (Q_USER_SIG <= sig) && (sig < QActive_maxPubSignal_) && (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); Q_INVARIANT_INCRIT(402, QPSet_verify_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis)); QS_BEGIN_PRE(QS_QF_ACTIVE_UNSUBSCRIBE, p) QS_TIME_PRE(); // timestamp QS_SIG_PRE(sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_END_PRE() // remove the prio. from the subscriber set QPSet_remove(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QF_MEM_APP(); QF_CRIT_EXIT(); const //! @protected @memberof QActive //! @protected @memberof QActive QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); uint_fast8_t const p = (uint_fast8_t)me->prio; Q_REQUIRE_INCRIT(500, (0U < p) && (p <= QF_MAX_ACTIVE) && (QActive_registry_[p] == me)); enum_t const maxPubSig = QActive_maxPubSignal_; QF_MEM_APP(); QF_CRIT_EXIT(); for (enum_t sig = Q_USER_SIG; sig < maxPubSig; ++sig) { QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QPSet_hasElement(&QActive_subscrList_[sig].set, p)) { QPSet_remove(&QActive_subscrList_[sig].set, p); #ifndef Q_UNSAFE QPSet_update_(&QActive_subscrList_[sig].set, &QActive_subscrList_[sig].set_dis); #endif QS_BEGIN_PRE(QS_QF_ACTIVE_UNSUBSCRIBE, p) QS_TIME_PRE(); // timestamp QS_SIG_PRE(sig); // the signal of this event QS_OBJ_PRE(me); // this active object QS_END_PRE() } QF_MEM_APP(); QF_CRIT_EXIT(); QF_CRIT_EXIT_NOP(); // prevent merging critical sections } const //! @protected @memberof QActive //! @protected @memberof QActive bool const status = QEQueue_post(eq, e, 0U, me->prio); QS_CRIT_STAT QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_ACTIVE_DEFER, me->prio) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this active object QS_OBJ_PRE(eq); // the deferred queue QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); return status; //! @protected @memberof QActive //! @protected @memberof QActive QEvt const * const e = QEQueue_get(eq, me->prio); QF_CRIT_STAT bool recalled; if (e != (QEvt *)0) { // event available? QACTIVE_POST_LIFO(me, e); // post it to the front of the AO's queue QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QEvt_getPoolNum_(e) != 0U) { // is it a mutable event? // after posting to the AO's queue the event must be referenced // at least twice: once in the deferred event queue (eq->get() // did NOT decrement the reference counter) and once in the // AO's event queue. Q_ASSERT_INCRIT(210, e->refCtr_ >= 2U); // we need to decrement the reference counter once, to account // for removing the event from the deferred event queue. QEvt_refCtr_dec_(e); // decrement the reference counter } QS_BEGIN_PRE(QS_QF_ACTIVE_RECALL, me->prio) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this active object QS_OBJ_PRE(eq); // the deferred queue QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_END_PRE() QF_MEM_APP(); QF_CRIT_EXIT(); recalled = true; } else { QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_ACTIVE_RECALL_ATTEMPT, me->prio) QS_TIME_PRE(); // time stamp QS_OBJ_PRE(me); // this active object QS_OBJ_PRE(eq); // the deferred queue QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); recalled = false; } return recalled; const //! @protected @memberof QActive //! @protected @memberof QActive uint_fast16_t n = 0U; while (n < num) { QEvt const * const e = QEQueue_get(eq, me->prio); if (e != (QEvt *)0) { ++n; // count one more flushed event #if (QF_MAX_EPOOL > 0U) QF_gc(e); // garbage collect #endif } else { break; } } return n; //! @private @memberof QActive //! @private @memberof QActive //! @class QMActive //! @extends QActive //! @protected @memberof QMActive //! @protected @memberof QMActive // clear the whole QMActive object, so that the framework can start // correctly even if the startup code fails to clear the uninitialized // data (as is required by the C Standard). QF_bzero_(me, sizeof(*me)); // NOTE: QActive inherits the QActvie class, but it calls the // constructor of the QMsm subclass. This is because QMActive inherits // the behavior from the QMsm subclass. QMsm_ctor((QMsm *)(me), initial); // NOTE: this vtable is identical as QMsm, but is provided // for the QMActive subclass to provide a UNIQUE vptr to distinguish // subclasses of QActive (e.g., in the debugger). static struct QAsmVtable const vtable = { // QMActive virtual table &QMsm_init_, &QMsm_dispatch_, &QMsm_isIn_ #ifdef Q_SPY ,&QMsm_getStateHandler_ #endif }; me->super.super.vptr = &vtable; // hook vptr to QMActive vtable //! @class QTimeEvt //! @extends QEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, (sig != 0) && (tickRate < QF_MAX_TICK_RATE)); QF_CRIT_EXIT(); QEvt_ctor(&me->super, sig); me->next = (QTimeEvt *)0; me->act = act; me->ctr = 0U; me->interval = 0U; me->tickRate = (uint8_t)tickRate; me->flags = 0U; #ifndef Q_UNSAFE me->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(me->next); me->ctr_dis = (QTimeEvtCtr)~me->ctr; #endif // ndef Q_UNSAFE //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // dynamic range checks #if (QF_TIMEEVT_CTR_SIZE == 1U) Q_REQUIRE_INCRIT(400, (nTicks < 0xFFU) && (interval < 0xFFU)); #elif (QF_TIMEEVT_CTR_SIZE == 2U) Q_REQUIRE_INCRIT(400, (nTicks < 0xFFFFU) && (interval < 0xFFFFU)); #endif #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(401, QEvt_verify_(&me->super)); #endif QTimeEvtCtr const ctr = me->ctr; uint8_t const tickRate = me->tickRate; #ifdef Q_SPY uint_fast8_t const qsId = ((QActive *)(me->act))->prio; #endif Q_REQUIRE_INCRIT(410, (nTicks != 0U) && (ctr == 0U) && (me->act != (void *)0) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE)); #ifndef Q_UNSAFE QTimeEvtCtr const dis = (QTimeEvtCtr)~me->ctr_dis; Q_INVARIANT_INCRIT(411, ctr == dis); #else Q_UNUSED_PAR(ctr); #endif // ndef Q_UNSAFE me->ctr = (QTimeEvtCtr)nTicks; me->interval = (QTimeEvtCtr)interval; #ifndef Q_UNSAFE me->ctr_dis = (QTimeEvtCtr)~nTicks; #endif // ndef Q_UNSAFE // is the time event unlinked? // NOTE: For the duration of a single clock tick of the specified tick // rate a time event can be disarmed and yet still linked into the list // because un-linking is performed exclusively in QTimeEvt_tick_(). if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) { me->flags |= QTE_FLAG_IS_LINKED; // mark as linked // The time event is initially inserted into the separate "freshly // armed" link list based on QTimeEvt_timeEvtHead_[tickRate].act. // Only later, inside the QTimeEvt_tick_() function, the "freshly // armed" list is appended to the main list of armed time events // based on QTimeEvt_timeEvtHead_[tickRate].next. Again, this is // to keep any changes to the main list exclusively inside the // QTimeEvt_tick_(). #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(420, Q_PTR2UINT_CAST_(me->next) == (uintptr_t)~me->next_dis); Q_INVARIANT_INCRIT(421, Q_PTR2UINT_CAST_(QTimeEvt_timeEvtHead_[tickRate].act) == (uintptr_t)(~QTimeEvt_timeEvtHead_dis_[tickRate])); #endif me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; #ifndef Q_UNSAFE me->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(me->next); QTimeEvt_timeEvtHead_dis_[tickRate] = (uintptr_t)~Q_PTR2UINT_CAST_(me); #endif // ndef Q_UNSAFE } QS_BEGIN_PRE(QS_QF_TIMEEVT_ARM, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this time event object QS_OBJ_PRE(me->act); // the active object QS_TEC_PRE(nTicks); // the # ticks QS_TEC_PRE(interval); // the interval QS_U8_PRE(tickRate); // tick rate QS_END_PRE() QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QTimeEvtCtr const ctr = me->ctr; #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(500, QEvt_verify_(&me->super)); QTimeEvtCtr const dis = (QTimeEvtCtr)~me->ctr_dis; Q_INVARIANT_INCRIT(501, ctr == dis); #endif // ndef Q_UNSAFE #ifdef Q_SPY uint_fast8_t const qsId = QACTIVE_CAST_(me->act)->prio; #endif // was the time event actually armed? bool wasArmed; if (ctr != 0U) { wasArmed = true; me->flags |= QTE_FLAG_WAS_DISARMED; me->ctr = 0U; // schedule removal from the list #ifndef Q_UNSAFE me->ctr_dis = (QTimeEvtCtr)~0U; #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this time event object QS_OBJ_PRE(me->act); // the target AO QS_TEC_PRE(ctr); // the # ticks QS_TEC_PRE(me->interval); // the interval QS_U8_PRE(me->tickRate); // tick rate QS_END_PRE() } else { // the time event was already disarmed automatically wasArmed = false; me->flags &= (uint8_t)(~QTE_FLAG_WAS_DISARMED & 0xFFU); QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM_ATTEMPT, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this time event object QS_OBJ_PRE(me->act); // the target AO QS_U8_PRE(me->tickRate); // tick rate QS_END_PRE() } QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // dynamic range checks #if (QF_TIMEEVT_CTR_SIZE == 1U) Q_REQUIRE_INCRIT(600, nTicks < 0xFFU); #elif (QF_TIMEEVT_CTR_SIZE == 2U) Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU); #endif uint8_t const tickRate = me->tickRate; QTimeEvtCtr const ctr = me->ctr; Q_REQUIRE_INCRIT(600, (nTicks != 0U) && (me->act != (void *)0) && (tickRate < QF_MAX_TICK_RATE)); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(601, QEvt_verify_(&me->super)); QTimeEvtCtr const dis = (QTimeEvtCtr)~me->ctr_dis; Q_INVARIANT_INCRIT(602, ctr == dis); #endif // ndef Q_UNSAFE #ifdef Q_SPY uint_fast8_t const qsId = ((QActive *)(me->act))->prio; #endif me->ctr = (QTimeEvtCtr)nTicks; #ifndef Q_UNSAFE me->ctr_dis = (QTimeEvtCtr)~nTicks; #endif // ndef Q_UNSAFE // was the time evt not running? bool wasArmed; if (ctr == 0U) { wasArmed = false; // NOTE: For the duration of a single clock tick of the specified // tick rate a time event can be disarmed and yet still linked into // the list, because unlinking is performed exclusively in the // QTimeEvt_tick_() function. // is the time event unlinked? if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) { me->flags |= QTE_FLAG_IS_LINKED; // mark as linked // The time event is initially inserted into the separate "freshly // armed" link list based on QTimeEvt_timeEvtHead_[tickRate].act. // Only later, inside the QTimeEvt_tick_() function, the "freshly // armed" list is appended to the main list of armed time events // based on QTimeEvt_timeEvtHead_[tickRate].next. Again, this is // to keep any changes to the main list exclusively inside // QTimeEvt_tick_(). #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(620, Q_PTR2UINT_CAST_(me->next) == (uintptr_t)~me->next_dis); Q_INVARIANT_INCRIT(621, Q_PTR2UINT_CAST_(QTimeEvt_timeEvtHead_[tickRate].act) == (uintptr_t)(~QTimeEvt_timeEvtHead_dis_[tickRate])); #endif me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = me; #ifndef Q_UNSAFE me->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(me->next); QTimeEvt_timeEvtHead_dis_[tickRate] = (uintptr_t)~Q_PTR2UINT_CAST_(me); #endif // ndef Q_UNSAFE } } else { // the time event was armed wasArmed = true; } QS_BEGIN_PRE(QS_QF_TIMEEVT_REARM, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this time event object QS_OBJ_PRE(me->act); // the target AO QS_TEC_PRE(nTicks); // the # ticks QS_TEC_PRE(me->interval); // the interval QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U)); QS_END_PRE() QF_MEM_APP(); QF_CRIT_EXIT(); return wasArmed; //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); bool const wasDisarmed = (me->flags & QTE_FLAG_WAS_DISARMED) != 0U; me->flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed QF_MEM_APP(); QF_CRIT_EXIT(); return wasDisarmed; const //! @public @memberof QTimeEvt //! @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); QTimeEvtCtr const ctr = me->ctr; QF_CRIT_EXIT(); return ctr; //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt for (uint_fast8_t tickRate = 0U; tickRate < Q_DIM(QTimeEvt_timeEvtHead_); ++tickRate) { QTimeEvt_ctorX(&QTimeEvt_timeEvtHead_[tickRate], (QActive *)0, Q_USER_SIG, tickRate); #ifndef Q_UNSAFE QTimeEvt_timeEvtHead_dis_[tickRate] = (uintptr_t)~Q_PTR2UINT_CAST_(QTimeEvt_timeEvtHead_[tickRate].act); #endif } //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(800, tickRate < Q_DIM(QTimeEvt_timeEvtHead_)); QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate]; QS_BEGIN_PRE(QS_QF_TICK, 0U) ++prev->ctr; QS_TEC_PRE(prev->ctr); // tick ctr QS_U8_PRE(tickRate); // tick rate QS_END_PRE() // scan the linked-list of time events at this rate... uint_fast8_t lbound = 2U*QF_MAX_ACTIVE; // fixed upper loop bound for (; lbound > 0U; --lbound) { Q_ASSERT_INCRIT(810, prev != (QTimeEvt *)0); // sanity check QTimeEvt *te = prev->next; // advance down the time evt. list #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(811, Q_PTR2UINT_CAST_(te) == (uintptr_t)~prev->next_dis); #endif // ndef Q_UNSAFE if (te == (QTimeEvt *)0) { // end of the list? // any new time events armed since the last QTimeEvt_tick_()? if (QTimeEvt_timeEvtHead_[tickRate].act != (void *)0) { #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(812, Q_PTR2UINT_CAST_(QTimeEvt_timeEvtHead_[tickRate].act) == (uintptr_t)~QTimeEvt_timeEvtHead_dis_[tickRate]); #endif // ndef Q_UNSAFE prev->next = (QTimeEvt*)QTimeEvt_timeEvtHead_[tickRate].act; QTimeEvt_timeEvtHead_[tickRate].act = (void *)0; #ifndef Q_UNSAFE prev->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(prev->next); QTimeEvt_timeEvtHead_dis_[tickRate] = (uintptr_t)~Q_PTR2UINT_CAST_((void *)0); #endif // ndef Q_UNSAFE te = prev->next; // switch to the new list } else { // all currently armed time events are processed break; // terminate the for-loop } } // the time event 'te' must be valid Q_ASSERT_INCRIT(820, te != (QTimeEvt *)0); QTimeEvtCtr ctr = te->ctr; #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(821, QEvt_verify_(&te->super)); QTimeEvtCtr const dis = (QTimeEvtCtr)~te->ctr_dis; Q_INVARIANT_INCRIT(822, ctr == dis); #endif // ndef Q_UNSAFE if (ctr == 0U) { // time event scheduled for removal? prev->next = te->next; #ifndef Q_UNSAFE prev->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(te->next); #endif // ndef Q_UNSAFE // mark time event 'te' as NOT linked te->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU); // do NOT advance the prev pointer QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section to reduce latency // NOTE: prevent merging critical sections // In some QF ports the critical section exit takes effect only // on the next machine instruction. If the next instruction is // another entry to a critical section, the critical section // might not be really exited, but rather the two adjacent // critical sections would be MERGED. The QF_CRIT_EXIT_NOP() // macro contains minimal code required to prevent such merging // of critical sections in QF ports, in which it can occur. QF_CRIT_EXIT_NOP(); } else if (ctr == 1U) { // is time event about to expire? QActive * const act = (QActive *)te->act; if (te->interval != 0U) { // periodic time evt? te->ctr = te->interval; // rearm the time event #ifndef Q_UNSAFE te->ctr_dis = (QTimeEvtCtr)~te->interval; #endif // ndef Q_UNSAFE prev = te; // advance to this time event } else { // one-shot time event: automatically disarm te->ctr = 0U; prev->next = te->next; #ifndef Q_UNSAFE te->ctr_dis = (QTimeEvtCtr)~0U; prev->next_dis = (uintptr_t)~Q_PTR2UINT_CAST_(te->next); #endif // ndef Q_UNSAFE // mark time event 'te' as NOT linked te->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU); // do NOT advance the prev pointer QS_BEGIN_PRE(QS_QF_TIMEEVT_AUTO_DISARM, act->prio) QS_OBJ_PRE(te); // this time event object QS_OBJ_PRE(act); // the target AO QS_U8_PRE(tickRate); // tick rate QS_END_PRE() } QS_BEGIN_PRE(QS_QF_TIMEEVT_POST, act->prio) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(te); // the time event object QS_SIG_PRE(te->super.sig);// signal of this time event QS_OBJ_PRE(act); // the target AO QS_U8_PRE(tickRate); // tick rate QS_END_PRE() #ifdef QXK_H_ if ((enum_t)te->super.sig < Q_USER_SIG) { QXThread_timeout_(act); QF_MEM_APP(); QF_CRIT_EXIT(); } else { QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // QACTIVE_POST() asserts if the queue overflows QACTIVE_POST(act, &te->super, sender); } #else QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section before posting // QACTIVE_POST() asserts if the queue overflows QACTIVE_POST(act, &te->super, sender); #endif } else { // time event keeps timing out --ctr; // decrement the tick counter te->ctr = ctr; // update the original #ifndef Q_UNSAFE te->ctr_dis = (QTimeEvtCtr)~ctr; #endif // ndef Q_UNSAFE prev = te; // advance to this time event QF_MEM_APP(); QF_CRIT_EXIT(); // exit crit. section to reduce latency // prevent merging critical sections, see NOTE above QF_CRIT_EXIT_NOP(); } QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop QF_MEM_SYS(); } Q_ENSURE_INCRIT(890, lbound > 0U); QF_MEM_APP(); QF_CRIT_EXIT(); //! @static @private @memberof QTimeEvt //! @static @private @memberof QTimeEvt //! @static @public @memberof QTimeEvt //! @static @public @memberof QTimeEvt QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(900, tickRate < QF_MAX_TICK_RATE); QF_CRIT_EXIT(); bool inactive; if (QTimeEvt_timeEvtHead_[tickRate].next != (QTimeEvt *)0) { inactive = false; } else if ((QTimeEvt_timeEvtHead_[tickRate].act != (void *)0)) { inactive = false; } else { inactive = true; } return inactive; //! @class QTicker //! @extends QActive //! @public @memberof QTicker //! @public @memberof QTicker QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor static struct QAsmVtable const vtable = { // QTicker virtual table &QTicker_init_, &QTicker_dispatch_, &QHsm_isIn_ #ifdef Q_SPY ,&QHsm_getStateHandler_ #endif }; me->super.super.vptr = &vtable; // hook the vptr // reuse eQueue.head for tick-rate me->super.eQueue.head = (QEQueueCtr)tickRate; #ifndef Q_UNSAFE me->super.eQueue.head_dis = (QEQueueCtr)~tickRate; #endif // ndef Q_UNSAFE //! @private @memberof QTicker //! @private @memberof QTicker Q_UNUSED_PAR(me); Q_UNUSED_PAR(par); Q_UNUSED_PAR(qsId); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QACTIVE_CAST_(me)->eQueue.tail = 0U; #ifndef Q_UNSAFE QACTIVE_CAST_(me)->eQueue.tail_dis = (QEQueueCtr)~0U; #endif // ndef Q_UNSAFE QF_MEM_APP(); QF_CRIT_EXIT(); //! @private @memberof QTicker //! @private @memberof QTicker Q_UNUSED_PAR(e); Q_UNUSED_PAR(qsId); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // get volatile into temporaries QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail; QEQueueCtr const tickRate = QACTIVE_CAST_(me)->eQueue.head; #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(700, nTicks > 0U); QEQueueCtr dis = (QEQueueCtr)~QACTIVE_CAST_(me)->eQueue.tail_dis; Q_INVARIANT_INCRIT(701, nTicks == dis); dis = (QEQueueCtr)~QACTIVE_CAST_(me)->eQueue.head_dis; Q_INVARIANT_INCRIT(702, tickRate == dis); #endif // ndef Q_UNSAFE QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks #ifndef Q_UNSAFE QACTIVE_CAST_(me)->eQueue.tail_dis = (QEQueueCtr)~0U; #endif // ndef Q_UNSAFE QF_MEM_APP(); QF_CRIT_EXIT(); for (; nTicks > 0U; --nTicks) { QTimeEvt_tick_((uint_fast8_t)tickRate, me); } //! @private @memberof QTicker //! @private @memberof QTicker #ifndef Q_SPY Q_UNUSED_PAR(sender); #endif static QEvt const tickEvt = QEVT_INITIALIZER(0); QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEQueueCtr nTicks = me->eQueue.tail; // get volatile into temporary if (me->eQueue.frontEvt == (QEvt *)0) { // no tick events? #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(800, nTicks == 0U); Q_REQUIRE_INCRIT(801, me->eQueue.nFree == 1U); Q_INVARIANT_INCRIT(802, me->eQueue.frontEvt_dis == (uintptr_t)~Q_PTR2UINT_CAST_((QEvt *)0)); QEQueueCtr dis = (QEQueueCtr)~me->eQueue.nFree_dis; Q_INVARIANT_INCRIT(803, 1U == dis); dis = (QEQueueCtr)~me->eQueue.tail_dis; Q_INVARIANT_INCRIT(804, 0U == dis); #endif // ndef Q_UNSAFE me->eQueue.frontEvt = &tickEvt; // deliver event directly me->eQueue.nFree = 0U; #ifndef Q_UNSAFE me->eQueue.frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(&tickEvt); me->eQueue.nFree_dis = (QEQueueCtr)~0U; #endif // ndef Q_UNSAFE QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue } else { #ifndef Q_UNSAFE Q_REQUIRE_INCRIT(810, (nTicks > 0U) && (nTicks < 0xFFU)); Q_REQUIRE_INCRIT(811, me->eQueue.nFree == 0U); Q_INVARIANT_INCRIT(812, me->eQueue.frontEvt_dis == (uintptr_t)~Q_PTR2UINT_CAST_(&tickEvt)); QEQueueCtr dis = (QEQueueCtr)~me->eQueue.nFree_dis; Q_INVARIANT_INCRIT(813, 0U == dis); dis = (QEQueueCtr)~me->eQueue.tail_dis; Q_INVARIANT_INCRIT(814, nTicks == dis); #endif // ndef Q_UNSAFE } ++nTicks; // account for one more tick event me->eQueue.tail = nTicks; // update the original #ifndef Q_UNSAFE me->eQueue.tail_dis = (QEQueueCtr)~nTicks; #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_ACTIVE_POST, me->prio) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(sender); // the sender object QS_SIG_PRE(0U); // the signal of the event QS_OBJ_PRE(me); // this active object QS_2U8_PRE(0U, 0U); // poolNum & refCtr QS_EQC_PRE(0U); // # free entries QS_EQC_PRE(0U); // min # free entries QS_END_PRE() QF_MEM_APP(); QF_CRIT_EXIT(); //! @class QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue //! @private @memberof QEQueue // dummy static member to force generating 'struct QEQueue {...}' //! @public @memberof QEQueue //! @public @memberof QEQueue QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); #if (QF_EQUEUE_CTR_SIZE == 1U) Q_REQUIRE_INCRIT(100, qLen < 0xFFU); #endif me->frontEvt = (QEvt *)0; // no events in the queue me->ring = qSto; // the beginning of the ring buffer me->end = (QEQueueCtr)qLen; if (qLen > 0U) { me->head = 0U; me->tail = 0U; } me->nFree = (QEQueueCtr)(qLen + 1U); // +1 for frontEvt #ifndef Q_UNSAFE me->frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(me->frontEvt); me->head_dis = (QEQueueCtr)~me->head; me->tail_dis = (QEQueueCtr)~me->tail; me->nFree_dis = (QEQueueCtr)~me->nFree; me->nMin = me->nFree; #endif QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, e != (QEvt *)0); QEQueueCtr tmp = me->nFree; // get volatile into temporary #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(201, QEvt_verify_(e)); QEQueueCtr dis = (QEQueueCtr)~me->nFree_dis; Q_INVARIANT_INCRIT(202, tmp == dis); #endif // ndef Q_UNSAFE // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QEQueue_post) QS_TEST_PROBE_ID(1, tmp = 0U; // fake no free events ) // required margin available? bool status; if (((margin == QF_NO_MARGIN) && (tmp > 0U)) || (tmp > (QEQueueCtr)margin)) { // is it a mutable event? if (QEvt_getPoolNum_(e) != 0U) { QEvt_refCtr_inc_(e); // increment the reference counter } --tmp; // one free entry just used up me->nFree = tmp; // update the original #ifndef Q_UNSAFE me->nFree_dis = (QEQueueCtr)~tmp; if (me->nMin > tmp) { me->nMin = tmp; // update minimum so far } #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_EQUEUE_POST, qsId) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_OBJ_PRE(me); // this queue object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries #ifndef Q_UNSAFE QS_EQC_PRE(me->nMin); // min # free entries #else QS_EQC_PRE(0U); // min # free entries #endif QS_END_PRE() if (me->frontEvt == (QEvt *)0) { // is the queue empty? me->frontEvt = e; // deliver event directly #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(211, me->frontEvt_dis == (uintptr_t)~Q_PTR2UINT_CAST_((QEvt *)0)); me->frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(e); #endif // ndef Q_UNSAFE } else { // queue was not empty, insert event into the ring-buffer tmp = me->head; // get volatile into temporary #ifndef Q_UNSAFE dis = (QEQueueCtr)~me->head_dis; Q_INVARIANT_INCRIT(212, tmp == dis); #endif // ndef Q_UNSAFE me->ring[tmp] = e; // insert e into buffer if (tmp == 0U) { // need to wrap the head? tmp = me->end; } --tmp; // advance head (counter-clockwise) me->head = tmp; // update the original #ifndef Q_UNSAFE me->head_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE } status = true; // event posted successfully } else { // event cannot be posted // dropping events must be acceptable Q_ASSERT_INCRIT(210, margin != QF_NO_MARGIN); QS_BEGIN_PRE(QS_QF_EQUEUE_POST_ATTEMPT, qsId) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this queue object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries QS_EQC_PRE(margin); // margin requested QS_END_PRE() status = false; // event not posted } QF_MEM_APP(); QF_CRIT_EXIT(); return status; //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, e != (QEvt *)0); QEQueueCtr tmp = me->nFree; // get volatile into temporary #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(301, QEvt_verify_(e)); QEQueueCtr dis = (QEQueueCtr)~me->nFree_dis; Q_INVARIANT_INCRIT(302, tmp == dis); #endif // ndef Q_UNSAFE // test-probe#1 for faking queue overflow QS_TEST_PROBE_DEF(&QEQueue_postLIFO) QS_TEST_PROBE_ID(1, tmp = 0U; // fake no free events ) // must be able to LIFO-post the event Q_REQUIRE_INCRIT(310, tmp != 0U); if (QEvt_getPoolNum_(e) != 0U) { // is it a mutable event? QEvt_refCtr_inc_(e); // increment the reference counter } --tmp; // one free entry just used up me->nFree = tmp; // update the original #ifndef Q_UNSAFE me->nFree_dis = (QEQueueCtr)~tmp; if (me->nMin > tmp) { me->nMin = tmp; // update minimum so far } #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_EQUEUE_POST_LIFO, qsId) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this queue object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries #ifndef Q_UNSAFE QS_EQC_PRE(me->nMin); // min # free entries #else QS_EQC_PRE(0U); // min # free entries #endif QS_END_PRE() QEvt const * const frontEvt = me->frontEvt; me->frontEvt = e; // deliver the event directly to the front #ifndef Q_UNSAFE me->frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(e); #endif // ndef Q_UNSAFE if (frontEvt != (QEvt *)0) { // was the queue NOT empty? tmp = me->tail; // get volatile into temporary; #ifndef Q_UNSAFE dis = (QEQueueCtr)~me->tail_dis; Q_INVARIANT_INCRIT(311, tmp == dis); #endif // ndef Q_UNSAFE ++tmp; if (tmp == me->end) { // need to wrap the tail? tmp = 0U; // wrap around } me->tail = tmp; #ifndef Q_UNSAFE me->tail_dis = (QEQueueCtr)~tmp; #endif me->ring[tmp] = frontEvt; } QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); QEvt const * const e = me->frontEvt; // always remove evt from the front #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(411, Q_PTR2UINT_CAST_(e) == (uintptr_t)~me->frontEvt_dis); #endif // ndef Q_UNSAFE if (e != (QEvt *)0) { // was the queue not empty? QEQueueCtr tmp = me->nFree; // get volatile into temporary #ifndef Q_UNSAFE QEQueueCtr const dis = (QEQueueCtr)~me->nFree_dis; Q_INVARIANT_INCRIT(412, tmp == dis); #endif // ndef Q_UNSAFE ++tmp; // one more free event in the queue me->nFree = tmp; // update the # free #ifndef Q_UNSAFE me->nFree_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE // any events in the ring buffer? if (tmp <= me->end) { QS_BEGIN_PRE(QS_QF_EQUEUE_GET, qsId) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this queue object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_EQC_PRE(tmp); // # free entries QS_END_PRE() tmp = me->tail; // get volatile into temporary QEvt const * const frontEvt = me->ring[tmp]; #ifndef Q_UNSAFE Q_ASSERT_INCRIT(421, frontEvt != (QEvt *)0); me->frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_(frontEvt); #endif // ndef Q_UNSAFE me->frontEvt = frontEvt; // update the original if (tmp == 0U) { // need to wrap the tail? tmp = me->end; } --tmp; // advance the tail (counter-clockwise) me->tail = tmp; // update the original #ifndef Q_UNSAFE me->tail_dis = (QEQueueCtr)~tmp; #endif // ndef Q_UNSAFE } else { me->frontEvt = (QEvt *)0; // queue becomes empty #ifndef Q_UNSAFE me->frontEvt_dis = (uintptr_t)~Q_PTR2UINT_CAST_((QEvt *)0); #endif // ndef Q_UNSAFE // all entries in the queue must be free (+1 for frontEvt) Q_INVARIANT_INCRIT(440, tmp == (me->end + 1U)); QS_BEGIN_PRE(QS_QF_EQUEUE_GET_LAST, qsId) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of this event QS_OBJ_PRE(me); // this queue object QS_2U8_PRE(QEvt_getPoolNum_(e), e->refCtr_); QS_END_PRE() } } QF_MEM_APP(); QF_CRIT_EXIT(); return e; const //! @public @memberof QEQueue //! @public @memberof QEQueue return me->nFree; const //! @public @memberof QEQueue //! @public @memberof QEQueue #ifndef Q_UNSAFE return me->nMin; #else return 0U; #endif const //! @public @memberof QEQueue //! @public @memberof QEQueue return me->frontEvt == (struct QEvt *)0; //! @struct QFreeBlock //! @private @memberof QFreeBlock //! @private @memberof QFreeBlock //! @class QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @private @memberof QMPool //! @public @memberof QMPool //! @public @memberof QMPool QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, poolSto != (void *)0); Q_REQUIRE_INCRIT(101, poolSize >= (uint_fast32_t)sizeof(QFreeBlock)); Q_REQUIRE_INCRIT(102, (uint_fast16_t)(blockSize + sizeof(QFreeBlock)) > blockSize); me->free_head = (QFreeBlock *)poolSto; // find # free blocks in a memory block, NO DIVISION me->blockSize = (QMPoolSize)(2U * sizeof(void *)); uint_fast16_t nblocks = 1U; while (me->blockSize < (QMPoolSize)blockSize) { me->blockSize += (QMPoolSize)sizeof(QFreeBlock); ++nblocks; } // the pool buffer must fit at least one rounded-up block Q_ASSERT_INCRIT(110, poolSize >= me->blockSize); // start at the head of the free list QFreeBlock *fb = me->free_head; uint32_t nTot = 1U; // the last block already in the list // chain all blocks together in a free-list... for (uint_fast32_t size = poolSize - me->blockSize; size >= (uint_fast32_t)me->blockSize; size -= (uint_fast32_t)me->blockSize) { fb->next = &fb[nblocks]; // point next link to next block #ifndef Q_UNSAFE fb->next_dis = (uintptr_t)(~Q_PTR2UINT_CAST_(fb->next)); #endif fb = fb->next; // advance to the next block ++nTot; // one more free block in the pool } // dynamic range check #if (QF_MPOOL_CTR_SIZE == 1U) Q_ENSURE_INCRIT(190, nTot < 0xFFU); #elif (QF_MPOOL_CTR_SIZE == 2U) Q_ENSURE_INCRIT(190, nTot < 0xFFFFU); #endif fb->next = (QFreeBlock *)0; // the last link points to NULL me->nTot = (QMPoolCtr)nTot; me->nFree = me->nTot; // all blocks are free me->start = (QFreeBlock *)poolSto; // the original start this pool buffer me->end = fb; // the last block in this pool #ifndef Q_UNSAFE me->free_head_dis = (uintptr_t)~Q_PTR2UINT_CAST_(me->free_head); me->nFree_dis = (QMPoolCtr)~me->nFree; me->nMin = me->nTot; // the minimum # free blocks fb->next_dis = (uintptr_t)(~Q_PTR2UINT_CAST_(fb->next)); #endif QF_MEM_APP(); QF_CRIT_EXIT(); //! @public @memberof QMPool //! @public @memberof QMPool #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // get volatile into temporaries QFreeBlock *fb = me->free_head; QMPoolCtr nFree = me->nFree; #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(301, Q_PTR2UINT_CAST_(fb) == (uintptr_t)~me->free_head_dis); QMPoolCtr const dis = (QMPoolCtr)~me->nFree_dis; Q_INVARIANT_INCRIT(302, nFree == dis); #endif // ndef Q_UNSAFE // have more free blocks than the requested margin? if (nFree > (QMPoolCtr)margin) { Q_ASSERT_INCRIT(310, fb != (QFreeBlock *)0); QFreeBlock * const fb_next = fb->next; // fast temporary #ifndef Q_UNSAFE // the free block must have integrity (duplicate inverse storage) Q_INVARIANT_INCRIT(311, Q_PTR2UINT_CAST_(fb_next) == (uintptr_t)~fb->next_dis); #endif // ndef Q_UNSAFE --nFree; // one less free block if (nFree == 0U) { // is the pool becoming empty? // pool is becoming empty, so the next free block must be NULL Q_ASSERT_INCRIT(320, fb_next == (QFreeBlock *)0); me->nFree = 0U; #ifndef Q_UNSAFE me->nFree_dis = (QMPoolCtr)~0U; me->nMin = 0U; // remember that the pool got empty #endif // ndef Q_UNSAFE } else { me->nFree = nFree; // update the original #ifndef Q_UNSAFE me->nFree_dis = (QMPoolCtr)~nFree; // The pool is not empty, so the next free-block pointer // must be in range. Q_INVARIANT_INCRIT(330, (me->start <= fb_next) && (fb_next <= me->end)); // is the # free blocks the new minimum so far? if (me->nMin > nFree) { me->nMin = nFree; // remember the minimum so far } #endif // ndef Q_UNSAFE } me->free_head = fb_next; // set the head to the next free block #ifndef Q_UNSAFE me->free_head_dis = (uintptr_t)(~Q_PTR2UINT_CAST_(fb_next)); #endif // ndef Q_UNSAFE QS_BEGIN_PRE(QS_QF_MPOOL_GET, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this memory pool QS_MPC_PRE(nFree); // # of free blocks in the pool #ifndef Q_UNSAFE QS_MPC_PRE(me->nMin); // min # free blocks ever in the pool #else QS_MPC_PRE(0U); // min # free blocks (not available) #endif // ndef Q_UNSAFE QS_END_PRE() } else { // don't have enough free blocks at this point fb = (QFreeBlock *)0; QS_BEGIN_PRE(QS_QF_MPOOL_GET_ATTEMPT, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this memory pool QS_MPC_PRE(nFree); // # of free blocks in the pool QS_MPC_PRE(margin); // the requested margin QS_END_PRE() } QF_MEM_APP(); QF_CRIT_EXIT(); return fb; // return the block or NULL pointer to the caller //! @public @memberof QMPool //! @public @memberof QMPool #ifndef Q_SPY Q_UNUSED_PAR(qsId); #endif QFreeBlock * const fb = (QFreeBlock *)block; QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // get volatile into temporaries QFreeBlock * const free_head = me->free_head; QMPoolCtr nFree = me->nFree; #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(401, Q_PTR2UINT_CAST_(free_head) == (uintptr_t)~me->free_head_dis); QMPoolCtr const dis = (QMPoolCtr)~me->nFree_dis; Q_INVARIANT_INCRIT(402, nFree == dis); Q_REQUIRE_INCRIT(410, nFree < me->nTot); Q_REQUIRE_INCRIT(411, (me->start <= fb) && (fb <= me->end)); // the block must not be in the pool already Q_REQUIRE_INCRIT(412, Q_PTR2UINT_CAST_(fb->next) != (uintptr_t)~fb->next_dis); #endif // ndef Q_UNSAFE ++nFree; // one more free block in this pool me->free_head = fb; // set as new head of the free list me->nFree = nFree; fb->next = free_head; // link into the list #ifndef Q_UNSAFE me->free_head_dis = (uintptr_t)(~Q_PTR2UINT_CAST_(fb)); me->nFree_dis = (QMPoolCtr)~nFree; fb->next_dis = (uintptr_t)(~Q_PTR2UINT_CAST_(free_head)); #endif QS_BEGIN_PRE(QS_QF_MPOOL_PUT, qsId) QS_TIME_PRE(); // timestamp QS_OBJ_PRE(me); // this memory pool QS_MPC_PRE(nFree); // the # free blocks in the pool QS_END_PRE() QF_MEM_APP(); QF_CRIT_EXIT(); //! @class QF_Attr //! @private @memberof QF_Attr //! @private @memberof QF_Attr //! @private @memberof QF_Attr //! @static @private @memberof QF //! @static @private @memberof QF //! @static @private @memberof QF uint8_t *ptr = (uint8_t *)start; for (uint_fast16_t n = len; n > 0U; --n) { *ptr = 0U; ++ptr; } //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, (prio <= QF_MAX_ACTIVE) && (QActive_registry_[prio] != (QActive *)0)); #ifndef Q_UNSAFE uint_fast16_t const min = (uint_fast16_t)QActive_registry_[prio]->eQueue.nMin; #else uint_fast16_t const min = 0U; #endif QF_CRIT_EXIT(); return min; //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF //! @static @public @memberof QF uint_fast8_t const poolNum = QF_priv_.maxPool_; // see precondition{qf_dyn,200} and precondition{qf_dyn,201} QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(200, poolNum < QF_MAX_EPOOL); if (poolNum > 0U) { Q_REQUIRE_INCRIT(201, QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolNum - 1U]) < evtSize); } QF_priv_.maxPool_ = poolNum + 1U; // one more pool QF_MEM_APP(); QF_CRIT_EXIT(); // perform the port-dependent initialization of the event-pool QF_EPOOL_INIT_(QF_priv_.ePool_[poolNum], poolSto, poolSize, evtSize); #ifdef Q_SPY // generate the object-dictionary entry for the initialized pool { uint8_t obj_name[9] = "EvtPool?"; obj_name[7] = (uint8_t)((uint8_t)'0' + poolNum + 1U); QS_obj_dict_pre_(&QF_priv_.ePool_[poolNum], (char const *)obj_name); } #endif // Q_SPY //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); uint_fast16_t const max_size = QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[QF_priv_.maxPool_ - 1U]); QF_MEM_APP(); QF_CRIT_EXIT(); return max_size; //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(400, (poolNum <= QF_MAX_EPOOL) && (0U < poolNum) && (poolNum <= QF_priv_.maxPool_)); #ifndef Q_UNSAFE uint_fast16_t const min = (uint_fast16_t)QF_priv_.ePool_[poolNum - 1U].nMin; #else uint_fast16_t const min = 0U; #endif QF_MEM_APP(); QF_CRIT_EXIT(); return min; //! @static @private @memberof QF //! @static @private @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); // find the pool id that fits the requested event size... uint_fast8_t poolNum = 0U; // zero-based poolNum initially for (; poolNum < QF_priv_.maxPool_; ++poolNum) { if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_priv_.ePool_[poolNum])) { break; } } // precondition: // - cannot run out of registered pools Q_REQUIRE_INCRIT(300, poolNum < QF_priv_.maxPool_); ++poolNum; // convert to 1-based poolNum QF_MEM_APP(); QF_CRIT_EXIT(); // get event e (port-dependent)... QEvt *e; #ifdef Q_SPY QF_EPOOL_GET_(QF_priv_.ePool_[poolNum - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), (uint_fast8_t)QS_EP_ID + poolNum); #else QF_EPOOL_GET_(QF_priv_.ePool_[poolNum - 1U], e, ((margin != QF_NO_MARGIN) ? margin : 0U), 0U); #endif if (e != (QEvt *)0) { // was e allocated correctly? e->sig = (QSignal)sig; // set the signal e->refCtr_ = 0U; // initialize the reference counter to 0 e->evtTag_ = (uint8_t)((poolNum << 4U) | 0x0FU); QS_CRIT_ENTRY(); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_NEW, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event QS_END_PRE() QS_MEM_APP(); QS_CRIT_EXIT(); } else { // event was not allocated QF_CRIT_ENTRY(); // This assertion means that the event allocation failed, // and this failure cannot be tolerated. The most frequent // reason is an event leak in the application. Q_ASSERT_INCRIT(320, margin != QF_NO_MARGIN); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_NEW_ATTEMPT, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_EVS_PRE(evtSize); // the size of the event QS_SIG_PRE(sig); // the signal of the event QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); } // the returned event e is guaranteed to be valid (not NULL) // if we can't tolerate failed allocation return e; //! @static @public @memberof QF //! @static @public @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(400, e != (QEvt *)0); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(401, QEvt_verify_(e)); #endif uint_fast8_t const poolNum = QEvt_getPoolNum_(e); if (poolNum != 0U) { // is it a pool event (mutable)? QF_MEM_SYS(); if (e->refCtr_ > 1U) { // isn't this the last reference? QS_BEGIN_PRE(QS_QF_GC_ATTEMPT, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); QS_END_PRE() QEvt_refCtr_dec_(e); // decrement the ref counter QF_MEM_APP(); QF_CRIT_EXIT(); } else { // this is the last reference to this event, recycle it QS_BEGIN_PRE(QS_QF_GC, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); QS_END_PRE() // pool number must be in range Q_ASSERT_INCRIT(410, (poolNum <= QF_priv_.maxPool_) && (poolNum <= QF_MAX_EPOOL)); QF_MEM_APP(); QF_CRIT_EXIT(); // NOTE: casting 'const' away is legit because it's a pool event #ifdef Q_SPY QF_EPOOL_PUT_(QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, (uint_fast8_t)QS_EP_ID + poolNum); #else QF_EPOOL_PUT_(QF_priv_.ePool_[poolNum - 1U], (QEvt *)e, 0U); #endif } } else { QF_CRIT_EXIT(); } //! @static @private @memberof QF //! @static @private @memberof QF #ifdef Q_UNSAFE Q_UNUSED_PAR(evtRef); #endif QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(500, e != (QEvt *)0); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(501, QEvt_verify_(e)); #endif uint_fast8_t const poolNum = QEvt_getPoolNum_(e); Q_UNUSED_PAR(poolNum); // might be unused Q_REQUIRE_INCRIT(510, (poolNum != 0U) && (evtRef == (void *)0)); QEvt_refCtr_inc_(e); // increments the ref counter QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_NEW_REF, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); QS_END_PRE() QS_MEM_APP(); QF_CRIT_EXIT(); return e; //! @static @private @memberof QF //! @static @private @memberof QF QF_CRIT_STAT QF_CRIT_ENTRY(); QEvt const * const e = (QEvt const *)evtRef; Q_REQUIRE_INCRIT(600, e != (QEvt *)0); #ifndef Q_UNSAFE Q_INVARIANT_INCRIT(601, QEvt_verify_(e)); #endif // ndef Q_UNSAFE #ifdef Q_SPY uint_fast8_t const poolNum = QEvt_getPoolNum_(e); QS_MEM_SYS(); QS_BEGIN_PRE(QS_QF_DELETE_REF, (uint_fast8_t)QS_EP_ID + poolNum) QS_TIME_PRE(); // timestamp QS_SIG_PRE(e->sig); // the signal of the event QS_2U8_PRE(poolNum, e->refCtr_); QS_END_PRE() QS_MEM_APP(); #endif // def Q_SPY QF_CRIT_EXIT(); #if (QF_MAX_EPOOL > 0U) QF_gc(e); // recycle the referenced event #endif //! @static @public @memberof QF //! @static @public @memberof QF ((uint_fast16_t)0xFFFFU) ((QPrioSpec)((prio_) | ((pthre_) << 8U))) ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (enum_t)(sig_))) \ (evtT_##_init((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ QF_NO_MARGIN, (sig_)), __VA_ARGS__)) \ ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (enum_t)(sig_))) \ (evtT_##_init((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \ (margin_), (sig_)), __VA_ARGS__)) \ ((evtRef_) = (evtT_ const *)QF_newRef_(e, (evtRef_))) do { \ QF_deleteRef_((evtRef_)); \ (evtRef_) = (void *)0; \ } while (false) \ ((void)QActive_post_((me_), (e_), QF_NO_MARGIN, (sender_))) \ ((void)QActive_post_((me_), (e_), QF_NO_MARGIN, (void *)0)) \ (QActive_post_((me_), (e_), (margin_), (sender_))) \ (QActive_post_((me_), (e_), (margin_), (void *)0)) \ (QActive_postLIFO_((me_), (e_))) \ (QActive_publish_((e_), (void const *)(sender_), (sender_)->prio)) (QActive_publish_((e_), (void *)0, 0U)) (QTimeEvt_tick_((tickRate_), (sender_))) (QTimeEvt_tick_((tickRate_), (void *)0)) QTIMEEVT_TICK_X(0U, (sender_)) (QTicker_trig_((ticker_), (sender_))) (QTicker_trig_((ticker_), (void *)0)) ((void)0) QTIMEEVT_TICK_X((tickRate_), (sender_)) QTIMEEVT_TICK(sender_) QACTIVE_PUBLISH((e_), (sender_)) ((void)0) ((void)0) QMPool \ (QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_))) ((uint_fast16_t)(p_).blockSize) \ ((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qsId_))) \ (QMPool_put(&(p_), (e_), (qsId_))) //! @class QV { //! @cond INTERNAL uint8_t dummy; //! @endcond } QV; //! @class QV_Attr //! @memberof QV_Attr //! @memberof QV_Attr //! @memberof QV_Attr //! @memberof QV_Attr //! @static @private @memberof QV //! @static @public @memberof QV //! @static @public @memberof QV QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_INVARIANT_INCRIT(102, QV_priv_.schedCeil == (uint_fast8_t)(~QV_priv_.schedCeil_dis)); if (ceiling > QV_priv_.schedCeil) { // raising the scheduler ceiling? QS_BEGIN_PRE(QS_SCHED_LOCK, 0U) QS_TIME_PRE(); // timestamp // the previous sched ceiling & new sched ceiling QS_2U8_PRE((uint8_t)QV_priv_.schedCeil, (uint8_t)ceiling); QS_END_PRE() QV_priv_.schedCeil = ceiling; #ifndef Q_UNSAFE QV_priv_.schedCeil_dis = (uint_fast8_t)(~ceiling); #endif } QF_MEM_APP(); QF_CRIT_EXIT(); //! @static @public @memberof QV //! @static @public @memberof QV QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_INVARIANT_INCRIT(202, QV_priv_.schedCeil == (uint_fast8_t)(~QV_priv_.schedCeil_dis)); if (QV_priv_.schedCeil != 0U) { // actually enabling the scheduler? QS_BEGIN_PRE(QS_SCHED_UNLOCK, 0U) QS_TIME_PRE(); // timestamp // current sched ceiling (old), previous sched ceiling (new) QS_2U8_PRE((uint8_t)QV_priv_.schedCeil, 0U); QS_END_PRE() QV_priv_.schedCeil = 0U; #ifndef Q_UNSAFE QV_priv_.schedCeil_dis = (uint_fast8_t)(~0U); #endif } QF_MEM_APP(); QF_CRIT_EXIT(); //! @static @public @memberof QV //! @static @public @memberof QV //! @static @public @memberof QF //! @static @public @memberof QF QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QV_priv_, sizeof(QV_priv_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); #ifndef Q_UNSAFE QPSet_update_(&QV_priv_.readySet, &QV_priv_.readySet_dis); QV_priv_.schedCeil_dis = (uint_fast8_t)(~0U); #endif QTimeEvt_init(); // initialize QTimeEvts #ifdef QV_INIT QV_INIT(); // port-specific initialization of the QV kernel #endif //! @static @public @memberof QF //! @static @public @memberof QF QF_onCleanup(); // application-specific cleanup callback // nothing else to do for the QV kernel //! @static @public @memberof QF //! @static @public @memberof QF #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_QF_RUN); QS_endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY QF_onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); #ifdef QV_START QV_START(); // port-specific startup of the QV kernel #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) uint_fast8_t pprev = 0U; // previously used prio. #ifdef QF_ON_CONTEXT_SW // officially switch to the idle cotext QF_onContextSw((QActive *)0, (QActive *)0); #endif // def QF_ON_CONTEXT_SW #endif // def (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) for (;;) { // QV event loop... // check internal integrity (duplicate inverse storage) Q_INVARIANT_INCRIT(302, QPSet_verify_(&QV_priv_.readySet, &QV_priv_.readySet_dis)); // check internal integrity (duplicate inverse storage) Q_INVARIANT_INCRIT(303, QV_priv_.schedCeil == (uint_fast8_t)(~QV_priv_.schedCeil_dis)); // find the maximum prio. AO ready to run uint_fast8_t const p = (QPSet_notEmpty(&QV_priv_.readySet) ? QPSet_findMax(&QV_priv_.readySet) : 0U); if (p > QV_priv_.schedCeil) { // is it above the sched ceiling? QActive * const a = QActive_registry_[p]; #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QS_BEGIN_PRE(QS_SCHED_NEXT, p) QS_TIME_PRE(); // timestamp QS_2U8_PRE((uint8_t)p, (uint8_t)pprev); QS_END_PRE() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(((pprev != 0U) ? QActive_registry_[pprev] : (QActive *)0), a); #endif // QF_ON_CONTEXT_SW pprev = p; // update previous prio. #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QF_MEM_APP(); QF_INT_ENABLE(); QEvt const * const e = QActive_get_(a); // NOTE QActive_get_() performs QS_MEM_APP() before return // dispatch event (virtual call) (*a->super.vptr->dispatch)(&a->super, e, p); #if (QF_MAX_EPOOL > 0U) QF_gc(e); #endif QF_INT_DISABLE(); QF_MEM_SYS(); if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QV_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QV_priv_.readySet, &QV_priv_.readySet_dis); #endif } } else { // no AO ready to run --> idle #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (pprev != 0U) { QS_BEGIN_PRE(QS_SCHED_IDLE, pprev) QS_TIME_PRE(); // timestamp QS_U8_PRE((uint8_t)pprev); // previous prio QS_END_PRE() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive_registry_[pprev], (QActive *)0); #endif // QF_ON_CONTEXT_SW pprev = 0U; // update previous prio. } #endif // (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) QF_MEM_APP(); // QV_onIdle() must be called with interrupts DISABLED because // the determination of the idle condition can change at any time // by an interrupt posting events to a queue. // // NOTE: QV_onIdle() MUST enable interrupts internally, ideally // atomically with putting the CPU into a power-saving mode. QV_onIdle(); QF_INT_DISABLE(); // disable interrupts before looping back QF_MEM_SYS(); } } #ifdef __GNUC__ // GNU compiler? return 0; #endif //! QActive active object class customization for QV //! @public @memberof QActive //! @public @memberof QActive Q_UNUSED_PAR(stkSto); // not needed in QV Q_UNUSED_PAR(stkSize); // not needed in QV QF_CRIT_STAT QF_CRIT_ENTRY(); Q_REQUIRE_INCRIT(300, stkSto == (void *)0); QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = 0U; // not used QActive_register_(me); // make QF aware of this active object QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->prio); QS_FLUSH(); // flush the trace buffer to the host ((void)0) ((void)0) ((void)0) \ QPSet_insert(&QV_priv_.readySet, (uint_fast8_t)(me_)->prio); \ QPSet_update_(&QV_priv_.readySet, &QV_priv_.readySet_dis) \ QPSet_insert(&QV_priv_.readySet, (uint_fast8_t)(me_)->prio) //! @class QK { //! @cond INTERNAL uint8_t dummy; //! @endcond } QK; //! @class QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @memberof QK_Attr //! @static @private @memberof QK //! @static @public @memberof QK //! @static @public @memberof QK QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(100, !QK_ISR_CONTEXT_()); Q_INVARIANT_INCRIT(102, QK_priv_.lockCeil == (uint_fast8_t)(~QK_priv_.lockCeil_dis)); // first store the previous lock prio QSchedStatus stat; if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling? QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio) QS_TIME_PRE(); // timestamp // the previous lock ceiling & new lock ceiling QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)ceiling); QS_END_PRE() // previous status of the lock stat = (QSchedStatus)QK_priv_.lockCeil; // new status of the lock QK_priv_.lockCeil = ceiling; #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = (uint_fast8_t)(~ceiling); #endif } else { stat = 0xFFU; // scheduler not locked } QF_MEM_APP(); QF_CRIT_EXIT(); return stat; // return the status to be saved in a stack variable //! @static @public @memberof QK //! @static @public @memberof QK // has the scheduler been actually locked by the last QK_schedLock()? if (prevCeil != 0xFFU) { QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_INVARIANT_INCRIT(202, QK_priv_.lockCeil == (uint_fast8_t)(~QK_priv_.lockCeil_dis)); Q_REQUIRE_INCRIT(210, (!QK_ISR_CONTEXT_()) && (QK_priv_.lockCeil > prevCeil)); QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio) QS_TIME_PRE(); // timestamp // current lock ceiling (old), previous lock ceiling (new) QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)prevCeil); QS_END_PRE() // restore the previous lock ceiling QK_priv_.lockCeil = prevCeil; #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = (uint_fast8_t)(~prevCeil); #endif // find if any AOs should be run after unlocking the scheduler if (QK_sched_() != 0U) { // preemption needed? QK_activate_(); // activate any unlocked AOs } QF_MEM_APP(); QF_CRIT_EXIT(); } //! @static @public @memberof QK //! @static @public @memberof QK //! @static @private @memberof QK //! @static @private @memberof QK // NOTE: this function is entered with interrupts DISABLED Q_INVARIANT_INCRIT(402, QPSet_verify_(&QK_priv_.readySet, &QK_priv_.readySet_dis)); uint_fast8_t p; if (QPSet_isEmpty(&QK_priv_.readySet)) { p = 0U; // no activation needed } else { // find the highest-prio AO with non-empty event queue p = QPSet_findMax(&QK_priv_.readySet); Q_INVARIANT_INCRIT(412, QK_priv_.actThre == (uint_fast8_t)(~QK_priv_.actThre_dis)); // is the AO's prio. below the active preemption-threshold? if (p <= QK_priv_.actThre) { p = 0U; // no activation needed } else { Q_INVARIANT_INCRIT(422, QK_priv_.lockCeil == (uint_fast8_t)(~QK_priv_.lockCeil_dis)); // is the AO's prio. below the lock-ceiling? if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { Q_INVARIANT_INCRIT(432, QK_priv_.nextPrio == (uint_fast8_t)(~QK_priv_.nextPrio_dis)); QK_priv_.nextPrio = p; // next AO to run #ifndef Q_UNSAFE QK_priv_.nextPrio_dis = (uint_fast8_t)(~QK_priv_.nextPrio); #endif } } } return p; //! @static @private @memberof QK //! @static @private @memberof QK // NOTE: this function is entered with interrupts DISABLED uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio. uint_fast8_t p = QK_priv_.nextPrio; // next prio to run Q_INVARIANT_INCRIT(502, (prio_in == (uint_fast8_t)(~QK_priv_.actPrio_dis)) && (p == (uint_fast8_t)(~QK_priv_.nextPrio_dis))); Q_REQUIRE_INCRIT(510, (prio_in <= QF_MAX_ACTIVE) && (0U < p) && (p <= QF_MAX_ACTIVE)); #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) uint_fast8_t pprev = prio_in; #endif // QF_ON_CONTEXT_SW || Q_SPY QK_priv_.nextPrio = 0U; // clear for the next time #ifndef Q_UNSAFE QK_priv_.nextPrio_dis = (uint_fast8_t)(~0U); #endif uint_fast8_t pthre_in; QActive *a; if (prio_in == 0U) { // preempting the idle thread? pthre_in = 0U; } else { a = QActive_registry_[prio_in]; Q_ASSERT_INCRIT(510, a != (QActive *)0); pthre_in = (uint_fast8_t)a->pthre; Q_INVARIANT_INCRIT(511, pthre_in == (uint_fast8_t)(~(uint_fast8_t)a->pthre_dis & 0xFFU)); } // loop until no more ready-to-run AOs of higher pthre than the initial do { a = QActive_registry_[p]; // obtain the pointer to the AO Q_ASSERT_INCRIT(520, a != (QActive *)0); // the AO must be registered uint_fast8_t const pthre = (uint_fast8_t)a->pthre; Q_INVARIANT_INCRIT(522, pthre == (uint_fast8_t)(~(uint_fast8_t)a->pthre_dis & 0xFFU)); // set new active prio. and preemption-threshold QK_priv_.actPrio = p; QK_priv_.actThre = pthre; #ifndef Q_UNSAFE QK_priv_.actPrio_dis = (uint_fast8_t)(~p); QK_priv_.actThre_dis = (uint_fast8_t)(~pthre); #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (p != pprev) { // changing threads? QS_BEGIN_PRE(QS_SCHED_NEXT, p) QS_TIME_PRE(); // timestamp QS_2U8_PRE(p, // prio. of the scheduled AO pprev); // previous prio. QS_END_PRE() #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive_registry_[pprev], a); #endif // QF_ON_CONTEXT_SW pprev = p; // update previous prio. } #endif // QF_ON_CONTEXT_SW || Q_SPY QF_MEM_APP(); QF_INT_ENABLE(); // unconditionally enable interrupts QEvt const * const e = QActive_get_(a); // NOTE QActive_get_() performs QF_MEM_APP() before return // dispatch event (virtual call) (*a->super.vptr->dispatch)(&a->super, e, p); #if (QF_MAX_EPOOL > 0U) QF_gc(e); #endif // determine the next highest-prio. AO ready to run... QF_INT_DISABLE(); // unconditionally disable interrupts QF_MEM_SYS(); // internal integrity check (duplicate inverse storage) Q_INVARIANT_INCRIT(532, QPSet_verify_(&QK_priv_.readySet, &QK_priv_.readySet_dis)); if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue? QPSet_remove(&QK_priv_.readySet, p); #ifndef Q_UNSAFE QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); #endif } if (QPSet_isEmpty(&QK_priv_.readySet)) { p = 0U; // no activation needed } else { // find new highest-prio AO ready to run... p = QPSet_findMax(&QK_priv_.readySet); // is the new prio. below the initial preemption-threshold? if (p <= pthre_in) { p = 0U; // no activation needed } else { Q_INVARIANT_INCRIT(542, QK_priv_.lockCeil == (uint_fast8_t)(~QK_priv_.lockCeil_dis)); // is the AO's prio. below the lock preemption-threshold? if (p <= QK_priv_.lockCeil) { p = 0U; // no activation needed } else { Q_ASSERT_INCRIT(550, p <= QF_MAX_ACTIVE); } } } } while (p != 0U); // restore the active prio. and preemption-threshold QK_priv_.actPrio = prio_in; QK_priv_.actThre = pthre_in; #ifndef Q_UNSAFE QK_priv_.actPrio_dis = (uint_fast8_t)(~QK_priv_.actPrio); QK_priv_.actThre_dis = (uint_fast8_t)(~QK_priv_.actThre); #endif #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY) if (prio_in != 0U) { // resuming an active object? a = QActive_registry_[prio_in]; // pointer to preempted AO QS_BEGIN_PRE(QS_SCHED_NEXT, prio_in) QS_TIME_PRE(); // timestamp // prio. of the resumed AO, previous prio. QS_2U8_PRE(prio_in, pprev); QS_END_PRE() } else { // resuming prio.==0 --> idle a = (QActive *)0; // QK idle loop QS_BEGIN_PRE(QS_SCHED_IDLE, pprev) QS_TIME_PRE(); // timestamp QS_U8_PRE(pprev); // previous prio. QS_END_PRE() } #ifdef QF_ON_CONTEXT_SW QF_onContextSw(QActive_registry_[pprev], a); #endif // QF_ON_CONTEXT_SW #endif // QF_ON_CONTEXT_SW || Q_SPY //! @static @public @memberof QF //! @static @public @memberof QF QF_bzero_(&QF_priv_, sizeof(QF_priv_)); QF_bzero_(&QK_priv_, sizeof(QK_priv_)); QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_)); // setup the QK scheduler as initially locked and not running QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked #ifndef Q_UNSAFE QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); QK_priv_.actPrio_dis = (uint_fast8_t)(~0U); QK_priv_.nextPrio_dis = (uint_fast8_t)(~0U); QK_priv_.actThre_dis = (uint_fast8_t)(~0U); QK_priv_.lockCeil_dis = (uint_fast8_t)(~QK_priv_.lockCeil); #endif QTimeEvt_init(); // initialize QTimeEvts #ifdef QK_INIT QK_INIT(); // port-specific initialization of the QK kernel #endif //! @static @public @memberof QF //! @static @public @memberof QF QF_onCleanup(); // application-specific cleanup callback // nothing else to do for the preemptive QK kernel //! @static @public @memberof QF //! @static @public @memberof QF #ifdef Q_SPY // produce the QS_QF_RUN trace record QF_INT_DISABLE(); QF_MEM_SYS(); QS_beginRec_((uint_fast8_t)QS_QF_RUN); QS_endRec_(); QF_MEM_APP(); QF_INT_ENABLE(); #endif // Q_SPY QF_onStartup(); // application-specific startup callback QF_INT_DISABLE(); QF_MEM_SYS(); #ifdef QK_START QK_START(); // port-specific startup of the QK kernel #endif QK_priv_.lockCeil = 0U; // unlock the QK scheduler #ifndef Q_UNSAFE QK_priv_.lockCeil_dis = (uint_fast8_t)(~0U); #endif #ifdef QF_ON_CONTEXT_SW // officially switch to the idle context QF_onContextSw((QActive *)0, QActive_registry_[QK_priv_.nextPrio]); #endif // activate AOs to process events posted so far if (QK_sched_() != 0U) { QK_activate_(); } QF_MEM_APP(); QF_INT_ENABLE(); for (;;) { // QK idle loop... QK_onIdle(); // application-specific QK on-idle callback } #ifdef __GNUC__ return 0; #endif // QActive class customization for QK //! @public @memberof QActive //! @public @memberof QActive Q_UNUSED_PAR(stkSto); // not needed in QK Q_UNUSED_PAR(stkSize); // not needed in QK QF_CRIT_STAT QF_CRIT_ENTRY(); QF_MEM_SYS(); Q_REQUIRE_INCRIT(300, (!QK_ISR_CONTEXT_()) && (stkSto == (void *)0)); QF_MEM_APP(); QF_CRIT_EXIT(); me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold QActive_register_(me); // make QF aware of this active object QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue // top-most initial tran. (virtual call) (*me->super.vptr->init)(&me->super, par, me->prio); QS_FLUSH(); // flush the trace buffer to the host // See if this AO needs to be scheduled if QK is already running QF_CRIT_ENTRY(); QF_MEM_SYS(); if (QK_sched_() != 0U) { // activation needed? QK_activate_(); } QF_MEM_APP(); QF_CRIT_EXIT(); QSchedStatus lockStat_; do { \ if (QK_ISR_CONTEXT_()) { \ lockStat_ = 0xFFU; \ } else { \ lockStat_ = QK_schedLock((ceil_)); \ } \ } while (false) do { \ if (lockStat_ != 0xFFU) { \ QK_schedUnlock(lockStat_); \ } \ } while (false) ((void)0) do { \ QPSet_insert(&QK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) do { \ QPSet_insert(&QK_priv_.readySet, (uint_fast8_t)(me_)->prio); \ if (!QK_ISR_CONTEXT_()) { \ if (QK_sched_() != 0U) { \ QK_activate_(); \ } \ } \ } while (false) #ifndef QSAFE_H_ #define QSAFE_H_ #ifdef __cplusplus extern "C" { #endif // QF-FuSa enabled =========================================================== #ifndef Q_UNSAFE #ifndef QF_CRIT_STAT #define QF_CRIT_STAT #endif #ifndef QF_CRIT_ENTRY #define QF_CRIT_ENTRY() ((void)0) #endif #ifndef QF_CRIT_EXIT #define QF_CRIT_EXIT() ((void)0) #endif $declare ${QP-FuSa::enabled} // QF-FuSa disabled ========================================================== #else $declare ${QP-FuSa::disabled} #endif //============================================================================ $declare1 ${QP-FuSa} #ifdef __cplusplus } #endif #endif // QSAFE_H_ #ifndef QP_H_ #define QP_H_ //============================================================================ #define QP_VERSION_STR "8.0.0" #define QP_VERSION 800U #define QP_RELEASE 0x7055936FU //============================================================================ //! @cond INTERNAL #ifndef Q_SIGNAL_SIZE #define Q_SIGNAL_SIZE 2U #endif #ifndef QF_MAX_ACTIVE #define QF_MAX_ACTIVE 32U #endif #if (QF_MAX_ACTIVE > 64U) #error QF_MAX_ACTIVE exceeds the maximum of 64U; #endif #ifndef QF_MAX_TICK_RATE #define QF_MAX_TICK_RATE 1U #endif #if (QF_MAX_TICK_RATE > 15U) #error QF_MAX_TICK_RATE exceeds the maximum of 15U; #endif #ifndef QF_MAX_EPOOL #define QF_MAX_EPOOL 3U #endif #if (QF_MAX_EPOOL > 15U) #error QF_MAX_EPOOL exceeds the maximum of 15U; #endif #ifndef QF_TIMEEVT_CTR_SIZE #define QF_TIMEEVT_CTR_SIZE 4U #endif #if (QF_TIMEEVT_CTR_SIZE > 4U) #error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif #ifndef QF_EVENT_SIZ_SIZE #define QF_EVENT_SIZ_SIZE 2U #endif #if (QF_EVENT_SIZ_SIZE > 4U) #error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U; #endif //! @endcond //============================================================================ $declare ${glob-types} $declare ${QEP} $declare ${QEP-macros} $declare ${QF::types} $declare ${QF::QActive} $declare ${QF::QMActive} $declare ${QF::QTimeEvt} $declare ${QF::QTicker} $declare ${QF::QF-base} $declare ${QF::QF-dyn} $declare ${QF-macros} #endif // QP_H_ #ifndef QP_PKG_H_ #define QP_PKG_H_ $declare ${QF::QF-pkg} // Bitmasks are for the QTimeEvt::flags attribute #define QTE_FLAG_IS_LINKED (1U << 7U) #define QTE_FLAG_WAS_DISARMED (1U << 6U) //! @private @memberof QEvt static inline void QEvt_refCtr_inc_(QEvt const *me) { uint8_t rc = me->refCtr_ + 1U; ((QEvt *)me)->refCtr_ = rc; // cast away 'const' #ifndef Q_UNSAFE ((QEvt *)me)->evtTag_ = (me->evtTag_ & 0xF0U) | ((~rc) & 0x0FU); #endif // ndef Q_UNSAFE } //! @private @memberof QEvt static inline void QEvt_refCtr_dec_(QEvt const *me) { uint8_t rc = me->refCtr_ - 1U; ((QEvt *)me)->refCtr_ = rc; // cast away 'const' #ifndef Q_UNSAFE ((QEvt *)me)->evtTag_ = (me->evtTag_ & 0xF0U) | ((~rc) & 0x0FU); #endif // ndef Q_UNSAFE } #define QACTIVE_CAST_(ptr_) ((QActive *)(ptr_)) #define Q_PTR2UINT_CAST_(ptr_) ((uintptr_t)(ptr_)) #endif // QP_PKG_H_ #ifndef QEQUEUE_H_ #define QEQUEUE_H_ #ifndef QF_EQUEUE_CTR_SIZE #define QF_EQUEUE_CTR_SIZE 1U #endif #if (QF_EQUEUE_CTR_SIZE == 1U) typedef uint8_t QEQueueCtr; #elif (QF_EQUEUE_CTR_SIZE == 2U) typedef uint16_t QEQueueCtr; #else #error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1U or 2U" #endif struct QEvt; // forward declartion $declare ${QF::QEQueue} #endif // QEQUEUE_H_ #ifndef QMPOOL_H_ #define QMPOOL_H_ #ifndef QF_MPOOL_SIZ_SIZE #define QF_MPOOL_SIZ_SIZE 2U #endif #ifndef QF_MPOOL_CTR_SIZE #define QF_MPOOL_CTR_SIZE 2U #endif #if (QF_MPOOL_SIZ_SIZE == 1U) typedef uint8_t QMPoolSize; #elif (QF_MPOOL_SIZ_SIZE == 2U) typedef uint16_t QMPoolSize; #elif (QF_MPOOL_SIZ_SIZE == 4U) typedef uint32_t QMPoolSize; #else #error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #if (QF_MPOOL_CTR_SIZE == 1U) typedef uint8_t QMPoolCtr; #elif (QF_MPOOL_CTR_SIZE == 2U) typedef uint16_t QMPoolCtr; #elif (QF_MPOOL_CTR_SIZE == 4U) typedef uint32_t QMPoolCtr; #else #error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #define QF_MPOOL_EL(evType_) struct { \ QFreeBlock sto_[((sizeof(evType_) - 1U) / (2U * sizeof(void *))) + 1U]; \ } $declare ${QF::QFreeBlock} $declare ${QF::QMPool} #endif // QMPOOL_H_ #ifndef QV_H_ #define QV_H_ $declare ${QV::QV} $declare ${QV::QV-base} //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QV-impl} $declare ${QF_EPOOL-impl} #endif // QP_IMPL #endif // QV_H_ #ifndef QK_H_ #define QK_H_ $declare ${QK::QK} $declare ${QK::QSchedStatus} $declare ${QK::QK-base} //============================================================================ // interface used only for internal implementation, but not in applications #ifdef QP_IMPL $declare ${QK-impl} $declare ${QF_EPOOL-impl} #endif // QP_IMPL #endif // QK_H_ #ifndef QSTAMP_H_ #define QSTAMP_H_ extern char const Q_BUILD_DATE[12]; extern char const Q_BUILD_TIME[9]; #endif // QSTAMP_H_ #ifndef QPC_H_ #define QPC_H_ #include "qp_port.h" // QP port from the port directory #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // software tracing enabled? #include "qs_port.h" // QS/C port from the port directory #else #include "qs_dummy.h" // QS/C dummy interface (inactive) #endif #ifndef QP_API_VERSION #define QP_API_VERSION 0 #endif // #ifndef QP_API_VERSION // QP API compatibility layer... //============================================================================ #if (QP_API_VERSION < 800) #define QM_SUPER_SUB(host_) error "submachines no longer supported" #define QM_TRAN_EP(tatbl_) error "submachines no longer supported" #define QM_TRAN_XP(xp_, tatbl_) error "submachines no longer supported" #ifdef QEVT_DYN_CTOR //! @deprecated #QEVT_DYN_CTOR, please use #QEVT_PAR_INIT #define QEVT_PAR_INIT #endif //! @deprecated plain 'char' is no longer forbidden in MISRA-C:2023 typedef char char_t; //! @deprecated Macro for starting an Active Object. //! Use QActive::QActive_start() instead. #define QACTIVE_START(me_, prioSpec_, qSto_, qLen_, stkSto_, stkSize_, par_) \ (QActive_start((QActive *)(me_), (prioSpec_), \ (qSto_), (qLen_), (stkSto_), (stkSize_), (par_))) //! @deprecated Macro for starting an eXtended Thread. //! Use QXThread::QXThread_start() instead. #define QXTHREAD_START(me_, prioSpec_, qSto_, qLen_, stkSto_, stkSize_, par_) \ (QXThread_start((QXThread *)(me_), (prioSpec_), \ (qSto_), (qLen_), (stkSto_), (stkSize_), (par_))) //! @deprecated Assertion failure handler. //! Use Q_onError() instead. #define Q_onAssert(module_, id_) Q_onError(module_, id_) //! @deprecated #Q_NASSERT preprocessor switch to disable QP assertions #ifdef Q_NASSERT // #Q_UNSAFE now replaces the functionality of Q_NASSERT #define Q_UNSAFE //! @deprecated general purpose assertion with user-specified ID //! number that **always** evaluates the `expr_` expression. #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) #elif defined Q_UNSAFE //! @deprecated general purpose assertion with user-specified ID //! number that **always** evaluates the `expr_` expression. #define Q_ALLEGE_ID(id_, expr_) ((void)(expr_)) #else // QP FuSa Subsystem enabled //! @deprecated general purpose assertion with user-specified ID //! number that **always** evaluates the `expr_` expression. //! @note //! The use of this macro is no longer recommended. #define Q_ALLEGE_ID(id_, expr_) if (!(expr_)) { \ QF_CRIT_STAT \ QF_CRIT_ENTRY(); \ Q_onError(&Q_this_module_[0], (id_)); \ QF_CRIT_EXIT(); \ } else ((void)0) #endif //! @deprecated general purpose assertion without ID number //! that **always** evaluates the `expr_` expression. //! Instead of ID number, this macro is based on the standard //! `__LINE__` macro. //! //! @note The use of this macro is no longer recommended. #define Q_ALLEGE(expr_) Q_ALLEGE_ID(__LINE__, (expr_)) //! Static (compile-time) assertion. //! @deprecated //! Use Q_ASSERT_STATIC() or better yet `_Static_assert()` instead. #define Q_ASSERT_COMPILE(expr_) Q_ASSERT_STATIC(expr_) //! @static @public @memberof QF //! @deprecated static inline void QF_psInit( QSubscrList * const subscrSto, enum_t const maxSignal) { QActive_psInit(subscrSto, maxSignal); } //! @deprecated instead use: QASM_INIT() #define QHSM_INIT(me_, par_, qsId_) QASM_INIT((me_), (par_), (qsId_)) //! @deprecated instead use: QASM_DISPATCH() #define QHSM_DISPATCH(me_, e_, qsId_) QASM_DISPATCH((me_), (e_), (qsId_)) //! @deprecated instead use: QASM_IS_IN() #define QHsm_isIn(me_, state_) QASM_IS_IN((QAsm *)(me_), (state_)) #endif // QP_API_VERSION < 800 #endif // QPC_H_ #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qep_hsm") $define ${QEP::QP_versionStr[16]} //============================================================================ //! @cond INTERNAL $define ${QEP::QEvt::reserved_[4]} // helper macro to handle reserved event in an QHsm #define QHSM_RESERVED_EVT_(state_, sig_) \ ((*(state_))(me, &QEvt_reserved_[(sig_)])) // helper macro to trace state entry #define QS_STATE_ENTRY_(state_, qsId_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE(QS_QEP_STATE_ENTRY, (qsId_)) \ QS_OBJ_PRE(me); \ QS_FUN_PRE(state_); \ QS_END_PRE() \ QS_MEM_APP(); \ QS_CRIT_EXIT() // helper macro to trace state exit #define QS_STATE_EXIT_(state_, qsId_) \ QS_CRIT_ENTRY(); \ QS_MEM_SYS(); \ QS_BEGIN_PRE(QS_QEP_STATE_EXIT, (qsId_)) \ QS_OBJ_PRE(me); \ QS_FUN_PRE(state_); \ QS_END_PRE() \ QS_MEM_APP(); \ QS_CRIT_EXIT() //! @endcond enum { // maximum depth of state nesting in a QHsm (including the top level), // must be >= 3 QHSM_MAX_NEST_DEPTH_ = 6 }; $define ${QEP::QHsm} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //============================================================================ //! @cond INTERNAL Q_DEFINE_THIS_MODULE("qep_msm") // top-state object for QMsm-style state machines static struct QMState const l_msm_top_s = { (struct QMState *)0, Q_STATE_CAST(0), Q_ACTION_CAST(0), Q_ACTION_CAST(0), Q_ACTION_CAST(0) }; //! @endcond enum { // maximum depth of state nesting in a QMsm (including the top level) QMSM_MAX_NEST_DEPTH_ = 8, // maximum length of transition-action array QMSM_MAX_TRAN_LENGTH_ = 2*QMSM_MAX_NEST_DEPTH_, // maximum depth of entry levels in a MSM for tran. to history QMSM_MAX_ENTRY_DEPTH_ = 4 }; //============================================================================ $define ${QEP::QMsm} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //Q_DEFINE_THIS_MODULE("qf_act") $define ${QF::QActive::registry_[QF_MAX_ACTIVE + 1U]} $define ${QF::QF-pkg} $define ${QF::types::QF_LOG2} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_actq") //============================================================================ $define ${QF::QActive::post_} $define ${QF::QActive::postLIFO_} $define ${QF::QActive::get_} $define ${QF::QF-base::getQueueMin} $define ${QF::QTicker} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_defer") $define ${QF::QActive::defer} $define ${QF::QActive::recall} $define ${QF::QActive::flushDeferred} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY #if (QF_MAX_EPOOL > 0U) // mutable events configured? Q_DEFINE_THIS_MODULE("qf_dyn") $define ${QF::QF-dyn} #endif // (QF_MAX_EPOOL > 0U) mutable events configured #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_mem") $define ${QF::QMPool} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_qact") $define ${QF::QActive::ctor} $define ${QF::QActive::register_} $define ${QF::QActive::unregister_} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY //Q_DEFINE_THIS_MODULE("qf_qmact") $define ${QF::QMActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_qeq") $define ${QF::QEQueue} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_ps") $define ${QF::QActive::subscrList_} $define ${QF::QActive::maxPubSignal_} $define ${QF::QActive::psInit} $define ${QF::QActive::publish_} $define ${QF::QActive::subscribe} $define ${QF::QActive::unsubscribe} $define ${QF::QActive::unsubscribeAll} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY Q_DEFINE_THIS_MODULE("qf_time") $define ${QF::QTimeEvt} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope internal interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QV_H_ #error "Source file included in a project NOT based on the QV kernel" #endif // QV_H_ Q_DEFINE_THIS_MODULE("qv") $define ${QV::QV-base} $define ${QV::QF-cust} $define ${QV::QActive} #define QP_IMPL // this is QP implementation #include "qp_port.h" // QP port #include "qp_pkg.h" // QP package-scope internal interface #include "qsafe.h" // QP Functional Safety (FuSa) Subsystem #ifdef Q_SPY // QS software tracing enabled? #include "qs_port.h" // QS port #include "qs_pkg.h" // QS facilities for pre-defined trace records #else #include "qs_dummy.h" // disable the QS software tracing #endif // Q_SPY // protection against including this source file in a wrong project #ifndef QK_H_ #error "Source file included in a project NOT based on the QK kernel" #endif // QK_H_ Q_DEFINE_THIS_MODULE("qk") $define ${QK::QK-base} $define ${QK::QF-cust} $define ${QK::QActive} #include "qstamp.h" char const Q_BUILD_DATE[12] = __DATE__; char const Q_BUILD_TIME[9] = __TIME__;