/** * \description * This function initializes one event pool at a time and must be called * exactly once for each event pool before the pool can be used. * * \arguments * \arg[in] \c poolSto pointer to the storage for the event pool * \arg[in] \c poolSize size of the storage for the pool in bytes * \arg[in] \c evtSize the block-size of the pool in bytes, which determines * the maximum size of events that can be allocated from the pool. * * \note * You might initialize many event pools by making many consecutive calls * to the QF_poolInit() function. However, for the simplicity of the internal * implementation, you must initialize event pools in the ascending order of * the event size. * * Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that can * be adapted for QF event pools. In case such support is missing, QF provides * a native QF event pool implementation. The macro #QF_EPOOL_TYPE_ determines * the type of event pool used by a particular QF port. See structure ::QMPool * for more information. * * \note The actual number of events available in the pool might be actually * less than (\a poolSize / \a evtSize) due to the internal alignment * of the blocks that the pool might perform. You can always check the * capacity of the pool by calling QF_getPoolMin(). * * \note The dynamic allocation of events is optional, meaning that you * might choose not to use dynamic events. In that case calling QF_poolInit() * and using up memory for the memory blocks is unnecessary. * * \sa QF initialization example for QF_init() */ void QF_poolInit(void * const poolSto, uint_fast16_t const poolSize, uint_fast16_t const evtSize) { /** \pre cannot exceed the number of available memory pools */ Q_REQUIRE_ID(100, QF_maxPool_ < (uint_fast8_t)Q_DIM(QF_pool_)); /** \pre please initialize event pools in ascending order of evtSize: */ Q_REQUIRE_ID(101, (QF_maxPool_ == (uint_fast8_t)0) || (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - (uint_fast8_t)1]) < evtSize)); /* perform the platform-dependent initialization of the pool */ QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize); ++QF_maxPool_; /* one more pool */ }
//**************************************************************************** /// @description /// This function is part of the Publish-Subscribe event delivery mechanism /// available in QF. Un-subscribing from all events means that the framework /// will stop posting any published events to the event queue of the active /// object. /// /// @note Due to the latency of event queues, an active object should NOT /// assume that no events will ever be dispatched to the state machine of /// the active object after un-subscribing from all events. /// The events might be already in the queue, or just about to be posted /// and the un-subscribe operation will not flush such events. Also, the /// alternative event-delivery mechanisms, such as direct event posting or /// time events, can be still delivered to the event queue of the active /// object. /// /// @sa QP::QF::publish_(), QP::QMActive::subscribe(), and /// QP::QMActive::unsubscribe() /// void QMActive::unsubscribeAll(void) const { uint_fast8_t const p = m_prio; Q_REQUIRE_ID(500, (static_cast<uint_fast8_t>(0) < p) && (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE)) && (QF::active_[p] == this)); uint_fast8_t const i = static_cast<uint_fast8_t>(QF_div8Lkup[p]); enum_t sig; for (sig = Q_USER_SIG; sig < QF_maxSignal_; ++sig) { QF_CRIT_STAT_ QF_CRIT_ENTRY_(); if ((QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] & QF_pwr2Lkup[p]) != static_cast<uint8_t>(0)) { QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_UNSUBSCRIBE, QS::priv_.aoObjFilter, this) QS_TIME_(); // timestamp QS_SIG_(sig); // the signal of this event QS_OBJ_(this); // this active object QS_END_NOCRIT_() // clear the priority bit QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] &= QF_invPwr2Lkup[p]; } QF_CRIT_EXIT_(); } }
//**************************************************************************** // @description // Starts execution of the AO and registers the AO with the framework. // // @param[in] prio priority at which to start the active object // @param[in] qSto pointer to the storage for the ring buffer of the // event queue (used only with the built-in QP::QEQueue) // @param[in] qLen length of the event queue (in events) // @param[in] stkSto pointer to the stack storage (used only when // per-AO stack is needed) // @param[in] stkSize stack size (in bytes) // @param[in] ie pointer to the optional initialization event // (might be NULL). // void QMActive::start(uint_fast8_t const prio, QEvt const *qSto[], uint_fast16_t const qLen, void * const stkSto, uint_fast16_t const stkSize, QEvt const * const ie) { Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_()) /* don't start AO's in an ISR! */ && (prio <= (uint_fast8_t)QF_MAX_ACTIVE) && (qSto != static_cast<QEvt const **>(0)) && (qLen != static_cast<uint_fast16_t>(0)) && (stkSto != static_cast<void *>(0)) && (stkSize != static_cast<uint_fast16_t>(0))); m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO // initialize the stack of the private thread QXK_stackInit_(this, static_cast<QXThreadHandler>(&thread_ao), stkSto, stkSize); m_prio = prio; // set the QF priority of this AO QF::add_(this); // make QF aware of this AO this->init(ie); // take the top-most initial tran. (virtual) QS_FLUSH(); // flush the trace buffer to the host QF_CRIT_STAT_ QF_CRIT_ENTRY_(); QXK_attr_.readySet.insert(m_prio); if (QXK_attr_.curr != static_cast<QMActive *>(0)) { // is QXK running? QXK_sched_(); } QF_CRIT_EXIT_(); }
//**************************************************************************** void QXThread::start(uint_fast8_t const prio, QEvt const *qSto[], uint_fast16_t const qLen, void * const stkSto, uint_fast16_t const stkSize, QEvt const * const /*ie*/) { QF_CRIT_STAT_ Q_REQUIRE_ID(300, (!QXK_ISR_CONTEXT_()) /* don't start AO's in an ISR! */ && (prio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE)) && (stkSto != static_cast<void *>(0)) && (stkSize != static_cast<uint_fast16_t>(0)) && (m_state.act == static_cast<QActionHandler>(0))); // is storage for the queue buffer provided? if (qSto != static_cast<QEvt const **>(0)) { m_eQueue.init(qSto, qLen); } // "naked" threads provide their thread function in place of // the top-most initial transition 'me->super.temp.act' QXK_stackInit_(this, reinterpret_cast<QXThreadHandler>(m_temp.act), stkSto, stkSize); m_prio = prio; QF::add_(this); // make QF aware of this naked thread QF_CRIT_ENTRY_(); QXK_attr_.readySet.insert(m_prio); // is QXK running? if (QXK_attr_.curr != static_cast<QMActive *>(0)) { QXK_sched_(); } QF_CRIT_EXIT_(); }
/* QActive functions =======================================================*/ void QActive_start_(QActive * const me, uint_fast8_t prio, QEvt const *qSto[], uint_fast16_t qLen, void *stkSto, uint_fast16_t stkSize, QEvt const *ie) { DWORD threadId; int win32Prio; void *fudgedQSto; uint_fast16_t fudgedQLen; Q_REQUIRE_ID(700, ((uint_fast8_t)0 < prio) /* priority must be in range */ && (prio <= (uint_fast8_t)QF_MAX_ACTIVE) && (qSto != (QEvt const **)0) /* queue storage must be... */ && (qLen > (uint_fast16_t)0) /* ...provided */ && (stkSto == (void *)0)); /* statck storage must NOT... * ... be provided */ me->prio = prio; /* set QF priority of this AO before adding it to QF */ QF_add_(me); /* make QF aware of this active object */ /* ignore the original storage for the event queue 'qSto' and * instead allocate an oversized "fudged" storage for the queue. * See also NOTE2 in qf_port.h. */ Q_ASSERT_ID(710, (uint32_t)qLen * QF_WIN32_FUDGE_FACTOR < USHRT_MAX); fudgedQLen = qLen * QF_WIN32_FUDGE_FACTOR; /* fudge the queue length */ fudgedQSto = calloc(fudgedQLen, sizeof(QEvt *)); /* new queue storage */ Q_ASSERT_ID(720, fudgedQSto != (void *)0); /* allocation must succeed */ QEQueue_init(&me->eQueue, (QEvt const **)fudgedQSto, fudgedQLen); /* save osObject as integer, in case it contains the Win32 priority */ win32Prio = (me->osObject != (void *)0) ? (int)me->osObject : THREAD_PRIORITY_NORMAL; /* create the Win32 "event" to throttle the AO's event queue */ me->osObject = CreateEvent(NULL, FALSE, FALSE, NULL); QMSM_INIT(&me->super, ie); /* take the top-most initial tran. */ QS_FLUSH(); /* flush the QS trace buffer to the host */ /* stack size not provided? */ if (stkSize == 0U) { stkSize = 1024U; /* NOTE: will be rounded up to the nearest page */ } /* create a Win32 thread for the AO; * The thread is created with THREAD_PRIORITY_NORMAL */ me->thread = CreateThread(NULL, stkSize, &ao_thread, me, 0, &threadId); Q_ASSERT_ID(730, me->thread != (HANDLE)0); /* thread must be created */ /* was the thread priority provided? */ if (win32Prio != 0) { SetThreadPriority(me->thread, win32Prio); } }
//**************************************************************************** //! unblock (resume) a given "naked" thread void QXThread::unblock(void) const { QF_CRIT_STAT_ // the unblocked thread must be a "naked" thread (no state) Q_REQUIRE_ID(800, m_state.act == (QActionHandler)0); QF_CRIT_ENTRY_(); unblock_(); QF_CRIT_EXIT_(); }
/*..........................................................................*/ void QActive_start_(QActive * const me, uint_fast8_t prio, QEvt const *qSto[], uint_fast16_t qLen, void *stkSto, uint_fast16_t stkSize, QEvt const *ie) { pthread_t thread; pthread_attr_t attr; struct sched_param param; /* p-threads allocate stack internally */ Q_REQUIRE_ID(600, stkSto == (void *)0); QEQueue_init(&me->eQueue, qSto, qLen); pthread_cond_init(&me->osObject, 0); me->prio = (uint8_t)prio; QF_add_(me); /* make QF aware of this active object */ QMSM_INIT(&me->super, ie); /* take the top-most initial tran. */ QS_FLUSH(); /* flush the QS trace buffer to the host */ pthread_attr_init(&attr); /* SCHED_FIFO corresponds to real-time preemptive priority-based scheduler * NOTE: This scheduling policy requires the superuser privileges */ pthread_attr_setschedpolicy(&attr, SCHED_FIFO); /* see NOTE04 */ param.sched_priority = prio + (sched_get_priority_max(SCHED_FIFO) - QF_MAX_ACTIVE - 3); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (stkSize == 0U) { /* set the allowed minimum */ stkSize = (uint_fast16_t)PTHREAD_STACK_MIN; } pthread_attr_setstacksize(&attr, (size_t)stkSize); if (pthread_create(&thread, &attr, &thread_routine, me) != 0) { /* Creating the p-thread with the SCHED_FIFO policy failed. Most * probably this application has no superuser privileges, so we just * fall back to the default SCHED_OTHER policy and priority 0. */ pthread_attr_setschedpolicy(&attr, SCHED_OTHER); param.sched_priority = 0; pthread_attr_setschedparam(&attr, ¶m); Q_ALLEGE(pthread_create(&thread, &attr, &thread_routine, me)== 0); } pthread_attr_destroy(&attr); me->thread = (uint8_t)1; }
//**************************************************************************** //! block (suspend) the current "naked" thread void QXThread::block(void) { QF_CRIT_STAT_ QF_CRIT_ENTRY_(); QXThread *thr = static_cast<QXThread *>(QXK_attr_.curr); Q_REQUIRE_ID(700, (!QXK_ISR_CONTEXT_()) /* can't block inside an ISR */ /* this must be a "naked" thread (no state) */ && (thr->m_state.act == static_cast<QActionHandler>(0))); thr->block_(); QF_CRIT_EXIT_(); }
/** * \description * Arms a time event to fire in a specified number of clock ticks and with * a specified interval. If the interval is zero, the time event is armed for * one shot ('one-shot' time event). The time event gets directly posted * (using the FIFO policy) into the event queue of the host active object. * * \arguments * \arg[in,out] \c me pointer (see \ref derivation) * \arg[in] \c nTicks number of clock ticks (at the associated rate) * to rearm the time event with. * \arg[in] \c interval interval (in clock ticks) for periodic time event. * * \note After posting, a one-shot time event gets automatically disarmed * while a periodic time event (interval != 0) is automatically re-armed. * * \note A time event can be disarmed at any time by calling the * QTimeEvt_disarm() function. Also, a time event can be re-armed to fire * in a different number of clock ticks by calling the QTimeEvt_rearm() * function. * * \usage * The following example shows how to arm a one-shot time event from a state * machine of an active object: * \include qf_state.c */ void QTimeEvt_armX(QTimeEvt * const me, QTimeEvtCtr const nTicks, QTimeEvtCtr const interval) { uint_fast8_t tickRate = (uint_fast8_t)me->super.refCtr_ & (uint_fast8_t)0x7F; QTimeEvtCtr ctr = me->ctr; QF_CRIT_STAT_ /** \pre the host AO must be valid, time evnet must be disarmed, * number of clock ticks cannot be zero, and the signal must be valid. */ Q_REQUIRE_ID(100, (me->act != (void *)0) && (ctr == (QTimeEvtCtr)0) && (nTicks != (QTimeEvtCtr)0) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE) && (me->super.sig >= (QSignal)Q_USER_SIG)); QF_CRIT_ENTRY_(); me->ctr = nTicks; me->interval = interval; /* 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 the QF_tickX() function. */ if ((me->super.refCtr_ & (uint8_t)0x80) == (uint8_t)0) { me->super.refCtr_ |= (uint8_t)0x80; /* mark as linked */ /* The time event is initially inserted into the separate * "freshly armed" link list based on QF_timeEvtHead_[tickRate].act. * Only later, inside the QF_tickX() function, the "freshly armed" * list is appended to the main list of armed time events based on * QF_timeEvtHead_[tickRate].next. Again, this is to keep any * changes to the main list exclusively inside the QF_tickX() * function. */ me->next = (QTimeEvt *)QF_timeEvtHead_[tickRate].act; QF_timeEvtHead_[tickRate].act = me; } QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_ARM, QS_priv_.teObjFilter, me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the active object */ QS_TEC_(nTicks); /* the number of ticks */ QS_TEC_(interval); /* the interval */ QS_U8_((uint8_t)tickRate); /* tick rate */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); }
//**************************************************************************** //! obtain a message from the private message queue (block if no messages) void const *QXThread::queueGet(uint_fast16_t const nTicks, uint_fast8_t const tickRate) { QEQueueCtr nFree; QEvt const *e; QF_CRIT_STAT_ QF_CRIT_ENTRY_(); QXThread *thr = static_cast<QXThread *>(QXK_attr_.curr); Q_REQUIRE_ID(900, (!QXK_ISR_CONTEXT_()) /* can't block inside an ISR */ /* this must be a "naked" thread (no state) */ && (thr->m_state.act == (QActionHandler)0)); // is the queue empty? -- block and wait for event(s) if (thr->m_eQueue.m_frontEvt == static_cast<QEvt *>(0)) { thr->m_temp.obj = reinterpret_cast<QMState const *>(&thr->m_eQueue); thr->teArm_(static_cast<enum_t>(QXK_QUEUE_SIG), nTicks, tickRate); QXK_attr_.readySet.remove(thr->m_prio); QXK_sched_(); QF_CRIT_EXIT_(); QF_CRIT_EXIT_NOP(); QF_CRIT_ENTRY_(); } // is the queue not empty? if (thr->m_eQueue.m_frontEvt != static_cast<QEvt *>(0)) { e = thr->m_eQueue.m_frontEvt; // always remove from the front // volatile into tmp nFree= thr->m_eQueue.m_nFree + static_cast<QEQueueCtr>(1); thr->m_eQueue.m_nFree = nFree; // update the number of free // any events in the ring buffer? if (nFree <= thr->m_eQueue.m_end) { // remove event from the tail thr->m_eQueue.m_frontEvt = QF_PTR_AT_(thr->m_eQueue.m_ring, thr->m_eQueue.m_tail); if (thr->m_eQueue.m_tail == static_cast<QEQueueCtr>(0)) { thr->m_eQueue.m_tail = thr->m_eQueue.m_end; // wrap } --thr->m_eQueue.m_tail; QS_BEGIN_NOCRIT_(QP::QS_QF_ACTIVE_GET, QP::QS::priv_.aoObjFilter, thr) QS_TIME_(); // timestamp QS_SIG_(e->sig); // the signal of this event QS_OBJ_(&thr); // this active object QS_2U8_(e->poolId_, e->refCtr_); // pool Id & ref Count QS_EQC_(nFree); // number of free entries QS_END_NOCRIT_() }
//**************************************************************************** // @description // The preferred way of calling this function is from within the active // object that needs to stop. In other words, an active object should stop // itself rather than being stopped by someone else. This policy works // best, because only the active object itself "knows" when it has reached // the appropriate state for the shutdown. // // @note // By the time the AO calls QP::QActive::stop(), it should have unsubscribed // from all events and no more events should be directly-posted to it. // void QMActive::stop(void) { QF_CRIT_STAT_ /// @pre QActive_stop() must be called from the AO that wants to stop. Q_REQUIRE_ID(300, (!QXK_ISR_CONTEXT_()) /* don't stop AO's from an ISR! */ && (this == QXK_attr_.curr)); QF::remove_(this); // remove this active object from the QF QF_CRIT_ENTRY_(); QXK_attr_.readySet.remove(m_prio); QXK_sched_(); QF_CRIT_EXIT_(); }
//**************************************************************************** /// @description /// QP::QF::run() is typically called from your startup code after you /// initialize the QF and start at least one active object or "naked" thread /// (with QP::QMActive::start() or QP::QXThread::start(), respectively). /// /// @returns QP::QF::run() typically does not return in embedded applications. /// However, when QP runs on top of an operating system, QP::QF::run() might /// return and in this case the return represents the error code (0 for /// success). Typically the value returned from QP::QF::run() is subsequently /// passed on as return from main(). /// int_t QF::run(void) { /// @pre QXK_init() must be called __before__ QP::QF::run() to initialize /// the QXK idle thread. Q_REQUIRE_ID(100, active_[0] == &l_idleThread); // switch to the highest-priority task QF_INT_DISABLE(); QXK_attr_.curr = &l_idleThread; // mark QXK as running uint_fast8_t p = QXK_attr_.readySet.findMax(); // next priority to run QXK_attr_.next = active_[p]; QXK_start_(); // start QXK multitasking (NOTE: enables interrupts) /* the QXK start should not return, but just in case... */ Q_ERROR_ID(110); return static_cast<int_t>(0); }
//**************************************************************************** /// @description /// This function removes a given active object from the active objects /// managed by the QF framework. It should not be called by the application /// directly, only through the function QP::QMActive::stop(). /// /// @param[in] a pointer to the active object to remove from the framework. /// /// @note The active object that is removed from the framework can no longer /// participate in the publish-subscribe event exchange. /// /// @sa QP::QF::add_() /// void QF::remove_(QMActive const * const a) { uint_fast8_t p = a->m_prio; Q_REQUIRE_ID(200, (static_cast<uint_fast8_t>(0) < p) && (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE)) && (active_[p] == a)); QF_CRIT_STAT_ QF_CRIT_ENTRY_(); active_[p] = static_cast<QMActive *>(0); // free-up the priority level QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_REMOVE, QS::priv_.aoObjFilter, a) QS_TIME_(); // timestamp QS_OBJ_(a); // the active object QS_U8_(p); // the priority of the active object QS_END_NOCRIT_() QF_CRIT_EXIT_(); }
/*..........................................................................*/ int_t QF_run(void) { /* CMSIS-RTX must be initialized and started from the starupt code */ Q_REQUIRE_ID(200, osKernelRunning()); /* call-back to the application to configure and start interrupts. * In the RTX port, QF_onStartup() typically also calls QF_setRtxTicker(). */ QF_onStartup(); /* CMSIS-RTOS starts thread execution with the function main(). * This main thread will continue executing after osKernelStart(). */ for (;;) { /* loop of the ticker thread */ QF_onRtxTicker(); /* call-back to the app to handle the tick */ osDelay(l_tickerPeriod); /* delay for the configurable period */ } #if defined (__GNUC__) return (int_t)0; /* dummy return to make the compiler happy */ #endif }
/*..........................................................................*/ void QActive_start_(QActive * const me, uint_fast8_t prio, QEvt const *qSto[], uint_fast16_t qLen, void *stkSto, uint_fast16_t stkSize, QEvt const *ie) { /* * The following os_thread_def_AO object specifies the attributes of the * RTX thread for Active Objects. This object is allocated on the stack, * because it only serves to create the specific task for this AO. */ osThreadDef_t os_thread_def_AO; /* no stack storage because RTX pre-allocates stacks internally, * but the queue storage and size must be provided */ Q_REQUIRE_ID(510, (stkSto == (void *)0) && (qSto != (QEvt const **)0) && (qLen > (uint_fast16_t)0)); /* create the QP event queue for the AO */ QEQueue_init(&me->eQueue, qSto, qLen); me->prio = prio; /* save the QF priority */ QF_add_(me); /* make QF aware of this active object */ QMSM_INIT(&me->super, ie); /* thake the top-most initial tran. */ QS_FLUSH(); /* flush the trace buffer to the host */ /* create the RTX thread for the AO... */ os_thread_def_AO.pthread = &ao_thread; /* RTX thread routine for AOs */ os_thread_def_AO.instances = (uint32_t)1; /* # instances of this thread */ os_thread_def_AO.stacksize = (uint32_t)stkSize; /* stack size [BYTES!] */ os_thread_def_AO.tpriority = (me->osObject != (uint32_t)0) /* RTX priority provided? */ ? (osPriority)(me->osObject) /* use the provided value */ : osPriorityNormal; /* use the default */ me->thread = osThreadCreate(&os_thread_def_AO, (void *)me); /* ensure that the thread was created correctly */ Q_ENSURE_ID(520, me->thread != (osThreadId)0); }
/*..........................................................................*/ void QF_init(void) { /* CMSIS-RTX must be initialized and started from the starupt code. */ Q_REQUIRE_ID(100, osKernelRunning()); /* Special consideration for Cortex-M systems with the hardware FPU... */ #ifdef __TARGET_FPU_VFP /* Enable access to Floating-point coprocessor in NVIC_CPACR */ (*((uint32_t volatile *)0xE000ED88U)) |= ((3U << 10*2) | (3U << 11*2)); /* Explictily Disable the automatic FPU state preservation and * the FPU lazy stacking in SCB_FPCCR */ (*((uint32_t volatile *)0xE000EF34U)) &= ~((1U << 31) | (1U << 30)); #endif /* __TARGET_FPU_VFP */ /* The main thread will be used as the ticker thread; save its ID */ l_tickerThreadId = osThreadGetId(); /* set the default period of the ticker thread... * NOTE: can be changed later in QF_setRtxTicker() */ l_tickerPeriod = 100000U; /* [microseconds] */ }
/** * @description * Executes the top-most initial transition in a MSM. * * @param[in,out] me pointer (see @ref oop) * @param[in] e pointer to the initialization event (might be NULL) * * @note Must be called only ONCE after the QMsm_ctor(). */ void QMsm_init_(QMsm * const me, QEvt const * const e) { QState r; QS_CRIT_STAT_ /** @pre the virtual pointer must be initialized, the top-most initial * transition must be initialized, and the initial transition must not * be taken yet. */ Q_REQUIRE_ID(200, (me->vptr != (QMsmVtbl const *)0) && (me->temp.fun != Q_STATE_CAST(0)) && (me->state.obj == &l_msm_top_s)); r = (*me->temp.fun)(me, e); /* the action of the top-most initial tran. */ /* the top-most initial transition must be taken */ Q_ASSERT_ID(210, r == (QState)Q_RET_TRAN_INIT); QS_BEGIN_(QS_QEP_STATE_INIT, QS_priv_.smObjFilter, me) QS_OBJ_(me); /* this state machine object */ QS_FUN_(me->state.obj->stateHandler); /* source state handler*/ QS_FUN_(me->temp.tatbl->target->stateHandler);/*target state handler*/ QS_END_() /* set state to the last tran. target */ me->state.obj = me->temp.tatbl->target; /* drill down into the state hierarchy with initial transitions... */ do { r = QMsm_execTatbl_(me, me->temp.tatbl); /* execute the tran. table */ } while (r >= (QState)Q_RET_TRAN_INIT); QS_BEGIN_(QS_QEP_INIT_TRAN, QS_priv_.smObjFilter, me) QS_TIME_(); /* time stamp */ QS_OBJ_(me); /* this state machine object */ QS_FUN_(me->state.obj->stateHandler); /* the new current state */ QS_END_() }
//****************************************************************************/ /// @description /// This function is part of the Publish-Subscribe event delivery mechanism /// available in QF. Subscribing to an event means that the framework will /// start posting all published events with a given signal @p sig to the /// event queue of the active object. /// /// @param[in] sig event signal to subscribe /// /// The following example shows how the Table active object subscribes /// to three signals in the initial transition: /// @include qf_subscribe.c /// /// @sa QP::QF::publish_(), QP::QMActive::unsubscribe(), and /// QP::QMActive::unsubscribeAll() /// void QMActive::subscribe(enum_t const sig) const { uint_fast8_t p = m_prio; Q_REQUIRE_ID(300, (Q_USER_SIG <= sig) && (sig < QF_maxSignal_) && (static_cast<uint_fast8_t>(0) < p) && (p <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE)) && (QF::active_[p] == this)); uint_fast8_t const i = static_cast<uint_fast8_t>(QF_div8Lkup[p]); QF_CRIT_STAT_ QF_CRIT_ENTRY_(); QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_SUBSCRIBE, QS::priv_.aoObjFilter, this) QS_TIME_(); // timestamp QS_SIG_(sig); // the signal of this event QS_OBJ_(this); // this active object QS_END_NOCRIT_() // set the priority bit QF_PTR_AT_(QF_subscrList_, sig).m_bits[i] |= QF_pwr2Lkup[p]; QF_CRIT_EXIT_(); }
//**************************************************************************** // must be called from within a critical section void QXThread::block_(void) const { /// @pre the thread holding the lock cannot block! Q_REQUIRE_ID(100, m_prio != QXK_attr_.lockPrio); QXK_attr_.readySet.remove(m_prio); QXK_sched_(); }
/** * \description * Constructor for the ::QEvt class provided when the switch #Q_EVT_CTOR * is defined. * * \arguments * \arg[in,out] \c me pointer (see \ref derivation) * \arg[in] \c sig signal to be assigned to the event */ QEvt *QEvt_ctor(QEvt * const me, enum_t const sig) { /** \pre the me pointer must be valid */ Q_REQUIRE_ID(200, me != (QEvt *)0); me->sig = (QSignal)sig; return me; }
void QF::publish_(QEvt const * const e) { #else void QF::publish_(QEvt const * const e, void const * const sender) { #endif /// @pre the published signal must be within the configured range Q_REQUIRE_ID(100, static_cast<enum_t>(e->sig) < QF_maxSignal_); QF_CRIT_STAT_ QF_CRIT_ENTRY_(); QS_BEGIN_NOCRIT_(QS_QF_PUBLISH, static_cast<void *>(0), static_cast<void *>(0)) QS_TIME_(); // the timestamp QS_OBJ_(sender); // the sender object QS_SIG_(e->sig); // the signal of the event QS_2U8_(e->poolId_, e->refCtr_); // pool Id & refCtr of the evt QS_END_NOCRIT_() // is it a dynamic event? if (e->poolId_ != static_cast<uint8_t>(0)) { QF_EVT_REF_CTR_INC_(e); // increment the reference counter, NOTE01 } QF_CRIT_EXIT_(); QF_SCHED_STAT_TYPE_ lockStat; lockStat.m_lockPrio = static_cast<uint_fast8_t>(0xFF); // uninitialized #if (QF_MAX_ACTIVE <= 8) uint_fast8_t tmp = static_cast<uint_fast8_t>( QF_PTR_AT_(QF_subscrList_, e->sig).m_bits[0]); while (tmp != static_cast<uint8_t>(0)) { uint_fast8_t p = static_cast<uint_fast8_t>(QF_LOG2(tmp)); // clear the subscriber bit tmp &= static_cast<uint_fast8_t>(QF_invPwr2Lkup[p]); // has the scheduler been locked yet? if (lockStat.m_lockPrio == static_cast<uint_fast8_t>(0xFF)) { QF_SCHED_LOCK_(&lockStat, p); } // the priority of the AO must be registered with the framework Q_ASSERT_ID(110, active_[p] != static_cast<QMActive *>(0)); // POST() asserts internally if the queue overflows (void)active_[p]->POST(e, sender); } #else uint_fast8_t i = static_cast<uint_fast8_t>(QF_SUBSCR_LIST_SIZE); // go through all bytes in the subscription list do { --i; uint_fast8_t tmp = static_cast<uint_fast8_t>( QF_PTR_AT_(QF_subscrList_, e->sig).m_bits[i]); while (tmp != static_cast<uint_fast8_t>(0)) { uint_fast8_t p = static_cast<uint_fast8_t>(QF_LOG2(tmp)); // clear the subscriber bit tmp &= static_cast<uint_fast8_t>(QF_invPwr2Lkup[p]); // adjust the priority p += static_cast<uint_fast8_t>(i << 3); // has the scheduler been locked yet? if (lockStat.m_lockPrio == static_cast<uint_fast8_t>(0xFF)) { QF_SCHED_LOCK_(&lockStat, p); } // the priority level be registered with the framework Q_ASSERT(active_[p] != static_cast<QMActive *>(0)); // POST() asserts internally if the queue overflows (void)active_[p]->POST(e, sender); } } while (i != static_cast<uint_fast8_t>(0)); #endif // was the scheduler locked? if (lockStat.m_lockPrio <= static_cast<uint_fast8_t>(QF_MAX_ACTIVE)) { QF_SCHED_UNLOCK_(&lockStat); // unlock the scheduler } // run the garbage collector gc(e); // NOTE: QP::QF::publish_() increments the reference counter to prevent // premature recycling of the event while the multicasting is still // in progress. At the end of the function, the garbage collector step // 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. }
/** * \description * Dispatches an event for processing to a hierarchical state machine (HSM). * The processing of an event represents one run-to-completion (RTC) step. * * \arguments * \arg[in,out] \c me pointer (see \ref derivation) * \arg[in] \c e pointer to the event to be dispatched to the HSM * * \note * This function should be called only via the virtual table (see * QMSM_DISPATCH()) and should NOT be called directly in the applications. */ void QHsm_dispatch_(QHsm * const me, QEvt const * const e) { QStateHandler t = me->state.fun; QStateHandler s; QState r; QS_CRIT_STAT_ /** \pre the state configuration must be stable */ Q_REQUIRE_ID(100, t == me->temp.fun); QS_BEGIN_(QS_QEP_DISPATCH, QS_priv_.smObjFilter, me) QS_TIME_(); /* time stamp */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this state machine object */ QS_FUN_(t); /* the current state */ QS_END_() /* process the event hierarchically... */ do { s = me->temp.fun; r = (*s)(me, e); /* invoke state handler s */ if (r == (QState)Q_RET_UNHANDLED) { /* unhandled due to a guard? */ QS_BEGIN_(QS_QEP_UNHANDLED, QS_priv_.smObjFilter, me) QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this state machine object */ QS_FUN_(s); /* the current state */ QS_END_() r = QEP_TRIG_(s, QEP_EMPTY_SIG_); /* find superstate of s */ } } while (r == (QState)Q_RET_SUPER); /* transition taken? */ if (r >= (QState)Q_RET_TRAN) { QStateHandler path[QHSM_MAX_NEST_DEPTH_]; int_fast8_t ip; path[0] = me->temp.fun; /* save the target of the transition */ path[1] = t; path[2] = s; /* exit current state to transition source s... */ for (; t != s; t = me->temp.fun) { if (QEP_TRIG_(t, Q_EXIT_SIG) == (QState)Q_RET_HANDLED) { QS_BEGIN_(QS_QEP_STATE_EXIT, QS_priv_.smObjFilter, me) QS_OBJ_(me); /* this state machine object */ QS_FUN_(t); /* the exited state */ QS_END_() (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of t */ } } ip = QHsm_tran_(me, path); #ifdef Q_SPY if (r == (QState)Q_RET_TRAN_HIST) { QS_BEGIN_(QS_QEP_TRAN_HIST, QS_priv_.smObjFilter, me) QS_OBJ_(me); /* this state machine object */ QS_FUN_(t); /* the source of the transition */ QS_FUN_(path[0]);/* the target of the tran. to history */ QS_END_() }
/** * @description * Dispatches an event for processing to a meta state machine (MSM). * The processing of an event represents one run-to-completion (RTC) step. * * @param[in,out] me pointer (see @ref oop) * @param[in] e pointer to the event to be dispatched to the MSM * * @note * This function should be called only via the virtual table (see * QMSM_DISPATCH()) and should NOT be called directly in the applications. */ void QMsm_dispatch_(QMsm * const me, QEvt const * const e) { QMState const *s = me->state.obj; /* store the current state */ QMState const *t = s; QState r = (QState)Q_RET_SUPER; QS_CRIT_STAT_ /** @pre current state must be initialized */ Q_REQUIRE_ID(300, s != (QMState const *)0); QS_BEGIN_(QS_QEP_DISPATCH, QS_priv_.smObjFilter, me) QS_TIME_(); /* time stamp */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this state machine object */ QS_FUN_(s->stateHandler); /* the current state handler */ QS_END_() /* scan the state hierarchy up to the top state... */ do { r = (*t->stateHandler)(me, e); /* call state handler function */ /* event handled? (the most frequent case) */ if (r >= (QState)Q_RET_HANDLED) { break; /* done scanning the state hierarchy */ } /* event unhandled and passed to the superstate? */ else if (r == (QState)Q_RET_SUPER) { t = t->superstate; /* advance to the superstate */ } /* event unhandled and passed to a submachine superstate? */ else if (r == (QState)Q_RET_SUPER_SUB) { t = me->temp.obj; /* current host state of the submachie */ } /* event unhandled due to a guard? */ else if (r == (QState)Q_RET_UNHANDLED) { QS_BEGIN_(QS_QEP_UNHANDLED, QS_priv_.smObjFilter, me) QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this state machine object */ QS_FUN_(t->stateHandler); /* the current state */ QS_END_() t = t->superstate; /* advance to the superstate */ } else { /* no other return value should be produced */ Q_ERROR_ID(310); } } while (t != (QMState const *)0); /* any kind of transition taken? */ if (r >= (QState)Q_RET_TRAN) { #ifdef Q_SPY QMState const *ts = t; /* transition source for QS tracing */ /* the transition source state must not be NULL */ Q_ASSERT_ID(320, ts != (QMState const *)0); #endif /* Q_SPY*/ do { /* save the transition-action table before it gets clobbered */ QMTranActTable const *tatbl = me->temp.tatbl; /* was a regular state transition segment taken? */ if (r == (QState)Q_RET_TRAN) { QMsm_exitToTranSource_(me, s, t); r = QMsm_execTatbl_(me, tatbl); } /* was an initial transition segment taken? */ else if (r == (QState)Q_RET_TRAN_INIT) { r = QMsm_execTatbl_(me, tatbl); } /* was a transition segment to history taken? */ else if (r == (QState)Q_RET_TRAN_HIST) { QMState const *hist = me->state.obj; /* save history */ me->state.obj = s; /* restore the original state */ QMsm_exitToTranSource_(me, s, t); (void)QMsm_execTatbl_(me, tatbl); r = QMsm_enterHistory_(me, hist); } /* was a transition segment to an entry point taken? */ else if (r == (QState)Q_RET_TRAN_EP) { r = QMsm_execTatbl_(me, tatbl); } /* was a transition segment to an exit point taken? */ else if (r == (QState)Q_RET_TRAN_XP) { QActionHandler const act = me->state.act; /* save XP action */ me->state.obj = s; /* restore the original state */ r = (*act)(me); /* execute the XP action */ if (r == (QState)Q_RET_TRAN) { QMsm_exitToTranSource_(me, s, t); /* take the tran-to-XP segment inside submachine */ (void)QMsm_execTatbl_(me, tatbl); me->state.obj = s; /* restore original state (history) */ #ifdef Q_SPY t = me->temp.tatbl->target; /* store for tracing */ #endif /* Q_SPY */ /* take the XP-Segment from submachine-state */ r = QMsm_execTatbl_(me, me->temp.tatbl); QS_BEGIN_(QS_QEP_TRAN_XP, QS_priv_.smObjFilter, me) QS_OBJ_(me); /* this state machine object */ QS_FUN_(s); /* source handler */ QS_FUN_(t); /* target handler */ QS_END_() } }