/** * \description * The "extended" QK scheduler performs all the steps of the regular scheduler * QK_sched_() and additionally switches the Thread-Local Storage (TLS) and * handles the extended context-switch. * * \arguments * \arg[in] \c p priority of the next AO to schedule * * \note The "extended" QK scheduler needs to be called only to handle * "asynchronous" preemption, under the assumption that neither the ISRs * nor the QK idle loop use TLS or the co-processors requiring * extended context switch (see [PSiCC2] Section 10.4.3). * * \note QK_schedExt_() must be always called with interrupts DISABLED. * * \note The extended scheduler might enable interrupts internally, * but always returns with interrupts DISABLED. */ void QK_schedExt_(uint_fast8_t p) { uint_fast8_t pin = QK_currPrio_; /* save the initial priority */ /* thread-local storage used? */ #ifdef QK_TLS uint_fast8_t pprev = pin; #endif QActive *a; /* extended context-switch used? */ #ifdef QK_EXT_SAVE /* aren't we preempting the idle loop? (idle loop has prio==0) */ if (pin != (uint_fast8_t)0) { a = QF_active_[pin]; /* the pointer to the preempted AO */ QK_EXT_SAVE(a); /* save the extended context */ } #endif /* loop until have ready-to-run AOs of higher priority than the initial */ do { QEvt const *e; a = QF_active_[p]; /* obtain the pointer to the AO */ QK_currPrio_ = p; /* this becomes the current task priority */ /* thread-local storage used? */ #ifdef QK_TLS /* are we changing threads? */ if (p != pprev) { QK_TLS(a); /* switch new thread-local storage */ pprev = p; } #endif QS_BEGIN_NOCRIT_(QS_QK_SCHEDULE, QS_priv_.aoObjFilter, a) QS_TIME_(); /* timestamp */ QS_U8_((uint8_t)p); /* the priority of the AO */ QS_U8_((uint8_t)pin); /* the preempted priority */ QS_END_NOCRIT_() QF_INT_ENABLE(); /* unconditionally enable interrupts */ /* perform the run-to-completion (RTS) step... * 1. retrieve the event from the AO's event queue, which by this * time must be non-empty and QActive_get_() asserts it. * 2. dispatch the event to the AO's state machine. * 3. determine if event is garbage and collect it if so */ e = QActive_get_(a); QMSM_DISPATCH(&a->super, e); QF_gc(e); QF_INT_DISABLE(); /* disable interrupts */ /* find new highest-priority AO ready to run... */ #if (QF_MAX_ACTIVE <= 8) QPSet8_findMax(&QK_readySet_, p); #else QPSet64_findMax(&QK_readySet_, p); #endif /* is the new priority below the current preemption threshold? */ if (p <= pin) { p = (uint_fast8_t)0; } #ifndef QK_NO_MUTEX /* is the new priority below the mutex ceiling? */ else if (p <= QK_ceilingPrio_) { p = (uint_fast8_t)0; } else { /* empty */ } #endif } while (p != (uint_fast8_t)0); QK_currPrio_ = pin; /* restore the initial priority */ #if defined(QK_TLS) || defined(QK_EXT_RESTORE) /* aren't we preempting the idle loop? (idle loop has prio==0) */ if (pin != (uint_fast8_t)0) { a = QF_active_[pin]; /* the pointer to the preempted AO */ /* thread-local storage used? */ #ifdef QK_TLS QK_TLS(a); /* restore the original TLS */ #endif /* extended context-switch used? */ #ifdef QK_EXT_RESTORE QK_EXT_RESTORE(a); /* restore the extended context */ #endif } #endif }
void QK_scheduleExt_(void) { #else void QK_scheduleExt_(QF_INT_KEY_TYPE intLockKey_) { #endif // the QK scheduler must be called at task level only Q_REQUIRE(QK_intNest_ == (uint8_t)0); // determine the priority of the highest-priority task ready to run uint8_t p = QK_readySet_.findMax(); #ifdef QK_NO_MUTEX if (p > QK_currPrio_) { // do we have a preemption? #else // QK priority-ceiling mutexes allowed if ((p > QK_currPrio_) && (p > QK_ceilingPrio_)) { #endif uint8_t pin = QK_currPrio_; // save the initial priority QActive *a; #ifdef QK_TLS // thread-local storage used? uint8_t pprev = pin; #endif #ifdef QK_EXT_SAVE // extended context-switch used? if (pin != (uint8_t)0) { // no extended context for the idle loop a = QF::active_[pin]; // the pointer to the preempted AO QK_EXT_SAVE(a); // save the extended context } #endif do { QEvent const *e; a = QF::active_[p]; // obtain the pointer to the AO QK_currPrio_ = p; // this becomes the current task priority #ifdef QK_TLS // thread-local storage used? if (p != pprev) { // are we changing threads? QK_TLS(a); // switch new thread-local storage pprev = p; } #endif QS_BEGIN_NOLOCK_(QS_QK_SCHEDULE, QS::aoObj_, a) QS_TIME_(); // timestamp QS_U8_(p); // the priority of the active object QS_U8_(pin); // the preempted priority QS_END_NOLOCK_() QK_INT_UNLOCK_(); // unlock the interrupts e = a->get_(); // get the next event for this active object a->dispatch(e); // dispatch e to the active object QF::gc(e); // garbage collect the event, if necessary QK_INT_LOCK_(); // determine the highest-priority AO ready to run p = QK_readySet_.findMax(); #ifdef QK_NO_MUTEX } while (p > pin); // is the new priority higher than initial? #else // QK priority-ceiling mutexes allowed } while ((p > pin) && (p > QK_ceilingPrio_)); #endif QK_currPrio_ = pin; // restore the initial priority #if defined(QK_TLS) || defined(QK_EXT_RESTORE) if (pin != (uint8_t)0) { // no extended context for the idle loop a = QF::active_[pin]; // the pointer to the preempted AO #ifdef QK_TLS // thread-local storage used? QK_TLS(a); // restore the original TLS #endif #ifdef QK_EXT_RESTORE // extended context-switch used? QK_EXT_RESTORE(a); // restore the extended context #endif } #endif } }