/** * @brief Disables a Virtual Timer. * @pre The timer must be in armed state before calling this function. * * @param[in] vtp the @p virtual_timer_t structure pointer * * @iclass */ void chVTDoResetI(virtual_timer_t *vtp) { chDbgCheckClassI(); chDbgCheck(vtp != NULL); chDbgAssert(vtp->vt_func != NULL, "timer not set or already triggered"); /* Removing the element from the delta list.*/ vtp->vt_next->vt_delta += vtp->vt_delta; vtp->vt_prev->vt_next = vtp->vt_next; vtp->vt_next->vt_prev = vtp->vt_prev; vtp->vt_func = (vtfunc_t)NULL; /* The above code changes the value in the header when the removed element is the last of the list, restoring it.*/ ch.vtlist.vt_delta = (systime_t)-1; #if CH_CFG_ST_TIMEDELTA > 0 || defined(__DOXYGEN__) { if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.vt_next) { /* Just removed the last element in the list, alarm timer stopped.*/ port_timer_stop_alarm(); } else { /* The alarm is set to the next element in the delta list.*/ port_timer_set_alarm(ch.vtlist.vt_lasttime + ch.vtlist.vt_next->vt_delta); } } #endif /* CH_CFG_ST_TIMEDELTA > 0 */ }
/** * @brief Enables a virtual timer. * @details The timer is enabled and programmed to trigger after the delay * specified as parameter. * @pre The timer must not be already armed before calling this function. * @note The callback function is invoked from interrupt context. * * @param[out] vtp the @p virtual_timer_t structure pointer * @param[in] delay the number of ticks before the operation timeouts, the * special values are handled as follow: * - @a TIME_INFINITE is allowed but interpreted as a * normal time specification. * - @a TIME_IMMEDIATE this value is not allowed. * . * @param[in] vtfunc the timer callback function. After invoking the * callback the timer is disabled and the structure can * be disposed or reused. * @param[in] par a parameter that will be passed to the callback * function * * @iclass */ void chVTDoSetI(virtual_timer_t *vtp, systime_t delay, vtfunc_t vtfunc, void *par) { virtual_timer_t *p; chDbgCheckClassI(); chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE)); vtp->vt_par = par; vtp->vt_func = vtfunc; p = ch.vtlist.vt_next; #if CH_CFG_ST_TIMEDELTA > 0 || defined(__DOXYGEN__) { systime_t now = port_timer_get_time(); /* If the requested delay is lower than the minimum safe delta then it is raised to the minimum safe value.*/ if (delay < CH_CFG_ST_TIMEDELTA) delay = CH_CFG_ST_TIMEDELTA; if (&ch.vtlist == (virtual_timers_list_t *)p) { /* The delta list is empty, the current time becomes the new delta list base time.*/ ch.vtlist.vt_lasttime = now; port_timer_start_alarm(ch.vtlist.vt_lasttime + delay); } else { /* Now the delay is calculated as delta from the last tick interrupt time.*/ delay += now - ch.vtlist.vt_lasttime; /* If the specified delay is closer in time than the first element in the delta list then it becomes the next alarm event in time.*/ if (delay < p->vt_delta) port_timer_set_alarm(ch.vtlist.vt_lasttime + delay); } } #endif /* CH_CFG_ST_TIMEDELTA > 0 */ /* The delta list is scanned in order to find the correct position for this timer. */ while (p->vt_delta < delay) { delay -= p->vt_delta; p = p->vt_next; } /* The timer is inserted in the delta list.*/ vtp->vt_prev = (vtp->vt_next = p)->vt_prev; vtp->vt_prev->vt_next = p->vt_prev = vtp; vtp->vt_delta = delay /* Special case when the timer is in last position in the list, the value in the header must be restored.*/; p->vt_delta -= delay; ch.vtlist.vt_delta = (systime_t)-1; }
/** * @brief Enables a virtual timer. * @details The timer is enabled and programmed to trigger after the delay * specified as parameter. * @pre The timer must not be already armed before calling this function. * @note The callback function is invoked from interrupt context. * * @param[out] vtp the @p virtual_timer_t structure pointer * @param[in] delay the number of ticks before the operation timeouts, the * special values are handled as follow: * - @a TIME_INFINITE is allowed but interpreted as a * normal time specification. * - @a TIME_IMMEDIATE this value is not allowed. * . * @param[in] vtfunc the timer callback function. After invoking the * callback the timer is disabled and the structure can * be disposed or reused. * @param[in] par a parameter that will be passed to the callback * function * * @iclass */ void chVTDoSetI(virtual_timer_t *vtp, sysinterval_t delay, vtfunc_t vtfunc, void *par) { virtual_timer_t *p; sysinterval_t delta; chDbgCheckClassI(); chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE)); vtp->par = par; vtp->func = vtfunc; #if CH_CFG_ST_TIMEDELTA > 0 { systime_t now = chVTGetSystemTimeX(); /* If the requested delay is lower than the minimum safe delta then it is raised to the minimum safe value.*/ if (delay < (sysinterval_t)CH_CFG_ST_TIMEDELTA) { delay = (sysinterval_t)CH_CFG_ST_TIMEDELTA; } /* Special case where the timers list is empty.*/ if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.next) { /* The delta list is empty, the current time becomes the new delta list base time, the timer is inserted.*/ ch.vtlist.lasttime = now; ch.vtlist.next = vtp; ch.vtlist.prev = vtp; vtp->next = (virtual_timer_t *)&ch.vtlist; vtp->prev = (virtual_timer_t *)&ch.vtlist; vtp->delta = delay; #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION /* The delta could be too large for the physical timer to handle.*/ if (delay > (sysinterval_t)TIME_MAX_SYSTIME) { delay = (sysinterval_t)TIME_MAX_SYSTIME; } #endif /* Being the first element in the list the alarm timer is started.*/ port_timer_start_alarm(chTimeAddX(ch.vtlist.lasttime, delay)); return; } /* Pointer to the first element in the delta list, which is non-empty.*/ p = ch.vtlist.next; /* Delay as delta from 'lasttime'. Note, it can overflow and the value becomes lower than 'now'.*/ delta = chTimeDiffX(ch.vtlist.lasttime, now) + delay; if (delta < chTimeDiffX(ch.vtlist.lasttime, now)) { /* Scenario where a very large delay excedeed the numeric range, it requires a special handling. We need to skip the first element and adjust the delta to wrap back in the previous numeric range.*/ delta -= p->delta; p = p->next; } else if (delta < p->delta) { sysinterval_t deadline_delta; /* A small delay that will become the first element in the delta list and next deadline.*/ deadline_delta = delta; #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION /* The delta could be too large for the physical timer to handle.*/ if (deadline_delta > (sysinterval_t)TIME_MAX_SYSTIME) { deadline_delta = (sysinterval_t)TIME_MAX_SYSTIME; } #endif port_timer_set_alarm(chTimeAddX(ch.vtlist.lasttime, deadline_delta)); } } #else /* CH_CFG_ST_TIMEDELTA == 0 */ /* Delta is initially equal to the specified delay.*/ delta = delay; /* Pointer to the first element in the delta list.*/ p = ch.vtlist.next; #endif /* CH_CFG_ST_TIMEDELTA == 0 */ /* The delta list is scanned in order to find the correct position for this timer. */ while (p->delta < delta) { /* Debug assert if the timer is already in the list.*/ chDbgAssert(p != vtp, "timer already armed"); delta -= p->delta; p = p->next; } /* The timer is inserted in the delta list.*/ vtp->next = p; vtp->prev = vtp->next->prev; vtp->prev->next = vtp; p->prev = vtp; vtp->delta = delta; /* Calculate new delta for the following entry.*/ p->delta -= delta; /* Special case when the timer is in last position in the list, the value in the header must be restored.*/ ch.vtlist.delta = (sysinterval_t)-1; }
/** * @brief Disables a Virtual Timer. * @pre The timer must be in armed state before calling this function. * * @param[in] vtp the @p virtual_timer_t structure pointer * * @iclass */ void chVTDoResetI(virtual_timer_t *vtp) { chDbgCheckClassI(); chDbgCheck(vtp != NULL); chDbgAssert(vtp->func != NULL, "timer not set or already triggered"); #if CH_CFG_ST_TIMEDELTA == 0 /* The delta of the timer is added to the next timer.*/ vtp->next->delta += vtp->delta; /* Removing the element from the delta list.*/ vtp->prev->next = vtp->next; vtp->next->prev = vtp->prev; vtp->func = NULL; /* The above code changes the value in the header when the removed element is the last of the list, restoring it.*/ ch.vtlist.delta = (sysinterval_t)-1; #else /* CH_CFG_ST_TIMEDELTA > 0 */ sysinterval_t nowdelta, delta; /* If the timer is not the first of the list then it is simply unlinked else the operation is more complex.*/ if (ch.vtlist.next != vtp) { /* Removing the element from the delta list.*/ vtp->prev->next = vtp->next; vtp->next->prev = vtp->prev; vtp->func = NULL; /* Adding delta to the next element, if it is not the last one.*/ if (&ch.vtlist != (virtual_timers_list_t *)vtp->next) vtp->next->delta += vtp->delta; return; } /* Removing the first timer from the list.*/ ch.vtlist.next = vtp->next; ch.vtlist.next->prev = (virtual_timer_t *)&ch.vtlist; vtp->func = NULL; /* If the list become empty then the alarm timer is stopped and done.*/ if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.next) { port_timer_stop_alarm(); return; } /* The delta of the removed timer is added to the new first timer.*/ ch.vtlist.next->delta += vtp->delta; /* If the new first timer has a delta of zero then the alarm is not modified, the already programmed alarm will serve it.*/ /* if (ch.vtlist.next->delta == 0) { return; }*/ /* Distance in ticks between the last alarm event and current time.*/ nowdelta = chTimeDiffX(ch.vtlist.lasttime, chVTGetSystemTimeX()); /* If the current time surpassed the time of the next element in list then the event interrupt is already pending, just return.*/ if (nowdelta >= ch.vtlist.next->delta) { return; } /* Distance from the next scheduled event and now.*/ delta = ch.vtlist.next->delta - nowdelta; /* Making sure to not schedule an event closer than CH_CFG_ST_TIMEDELTA ticks from now.*/ if (delta < (sysinterval_t)CH_CFG_ST_TIMEDELTA) { delta = nowdelta + (sysinterval_t)CH_CFG_ST_TIMEDELTA; } else { delta = nowdelta + delta; #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION /* The delta could be too large for the physical timer to handle.*/ if (delta > (sysinterval_t)TIME_MAX_SYSTIME) { delta = (sysinterval_t)TIME_MAX_SYSTIME; } #endif } port_timer_set_alarm(chTimeAddX(ch.vtlist.lasttime, delta)); #endif /* CH_CFG_ST_TIMEDELTA > 0 */ }
/** * @brief Puts the current thread to sleep into the specified state with * timeout specification. * @details The thread goes into a sleeping state, if it is not awakened * explicitly within the specified system time then it is forcibly * awakened with a @p NIL_MSG_TMO low level message. * * @param[in] newstate the new thread state or a semaphore pointer * @param[in] timeout the number of ticks before the operation timeouts. * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The wakeup message. * @retval NIL_MSG_TMO if a timeout occurred. * * @sclass */ msg_t chSchGoSleepTimeoutS(tstate_t newstate, systime_t timeout) { thread_t *ntp, *otp = nil.current; chDbgAssert(otp != &nil.threads[NIL_CFG_NUM_THREADS], "idle cannot sleep"); /* Storing the wait object for the current thread.*/ otp->state = newstate; #if NIL_CFG_ST_TIMEDELTA > 0 if (timeout != TIME_INFINITE) { systime_t abstime; /* TIMEDELTA makes sure to have enough time to reprogram the timer before the free-running timer counter reaches the selected timeout.*/ if (timeout < (systime_t)NIL_CFG_ST_TIMEDELTA) { timeout = (systime_t)NIL_CFG_ST_TIMEDELTA; } /* Absolute time of the timeout event.*/ abstime = chVTGetSystemTimeX() + timeout; if (nil.lasttime == nil.nexttime) { /* Special case, first thread asking for a timeout.*/ port_timer_start_alarm(abstime); nil.nexttime = abstime; } else { /* Special case, there are already other threads with a timeout activated, evaluating the order.*/ if (chVTIsTimeWithinX(abstime, nil.lasttime, nil.nexttime)) { port_timer_set_alarm(abstime); nil.nexttime = abstime; } } /* Timeout settings.*/ otp->timeout = abstime - nil.lasttime; } #else /* Timeout settings.*/ otp->timeout = timeout; #endif /* Scanning the whole threads array.*/ ntp = nil.threads; while (true) { /* Is this thread ready to execute?*/ if (NIL_THD_IS_READY(ntp)) { nil.current = nil.next = ntp; if (ntp == &nil.threads[NIL_CFG_NUM_THREADS]) { NIL_CFG_IDLE_ENTER_HOOK(); } port_switch(ntp, otp); return nil.current->u1.msg; } /* Points to the next thread in lowering priority order.*/ ntp++; chDbgAssert(ntp <= &nil.threads[NIL_CFG_NUM_THREADS], "pointer out of range"); } }
/** * @brief Time management handler. * @note This handler has to be invoked by a periodic ISR in order to * reschedule the waiting threads. * * @iclass */ void chSysTimerHandlerI(void) { #if NIL_CFG_ST_TIMEDELTA == 0 thread_t *tp = &nil.threads[0]; nil.systime++; do { /* Is the thread in a wait state with timeout?.*/ if (tp->timeout > (systime_t)0) { chDbgAssert(!NIL_THD_IS_READY(tp), "is ready"); /* Did the timer reach zero?*/ if (--tp->timeout == (systime_t)0) { /* Timeout on semaphores requires a special handling because the semaphore counter must be incremented.*/ /*lint -save -e9013 [15.7] There is no else because it is not needed.*/ if (NIL_THD_IS_WTSEM(tp)) { tp->u1.semp->cnt++; } else if (NIL_THD_IS_SUSP(tp)) { *tp->u1.trp = NULL; } /*lint -restore*/ (void) chSchReadyI(tp, MSG_TIMEOUT); } } /* Lock released in order to give a preemption chance on those architectures supporting IRQ preemption.*/ chSysUnlockFromISR(); tp++; chSysLockFromISR(); } while (tp < &nil.threads[NIL_CFG_NUM_THREADS]); #else thread_t *tp = &nil.threads[0]; systime_t next = (systime_t)0; chDbgAssert(nil.nexttime == port_timer_get_alarm(), "time mismatch"); do { /* Is the thread in a wait state with timeout?.*/ if (tp->timeout > (systime_t)0) { chDbgAssert(!NIL_THD_IS_READY(tp), "is ready"); chDbgAssert(tp->timeout >= (nil.nexttime - nil.lasttime), "skipped one"); tp->timeout -= nil.nexttime - nil.lasttime; if (tp->timeout == (systime_t)0) { /* Timeout on semaphores requires a special handling because the semaphore counter must be incremented.*/ /*lint -save -e9013 [15.7] There is no else because it is not needed.*/ if (NIL_THD_IS_WTSEM(tp)) { tp->u1.semp->cnt++; } else if (NIL_THD_IS_SUSP(tp)) { *tp->u1.trp = NULL; } /*lint -restore*/ (void) chSchReadyI(tp, MSG_TIMEOUT); } else { if (tp->timeout <= (systime_t)(next - (systime_t)1)) { next = tp->timeout; } } } /* Lock released in order to give a preemption chance on those architectures supporting IRQ preemption.*/ chSysUnlockFromISR(); tp++; chSysLockFromISR(); } while (tp < &nil.threads[NIL_CFG_NUM_THREADS]); nil.lasttime = nil.nexttime; if (next > (systime_t)0) { nil.nexttime += next; port_timer_set_alarm(nil.nexttime); } else { /* No tick event needed.*/ port_timer_stop_alarm(); } #endif }
/** * @brief Enables a virtual timer. * @details The timer is enabled and programmed to trigger after the delay * specified as parameter. * @pre The timer must not be already armed before calling this function. * @note The callback function is invoked from interrupt context. * * @param[out] vtp the @p virtual_timer_t structure pointer * @param[in] delay the number of ticks before the operation timeouts, the * special values are handled as follow: * - @a TIME_INFINITE is allowed but interpreted as a * normal time specification. * - @a TIME_IMMEDIATE this value is not allowed. * . * @param[in] vtfunc the timer callback function. After invoking the * callback the timer is disabled and the structure can * be disposed or reused. * @param[in] par a parameter that will be passed to the callback * function * * @iclass */ void chVTDoSetI(virtual_timer_t *vtp, systime_t delay, vtfunc_t vtfunc, void *par) { virtual_timer_t *p; systime_t delta; chDbgCheckClassI(); chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE)); vtp->par = par; vtp->func = vtfunc; #if CH_CFG_ST_TIMEDELTA > 0 { systime_t now = chVTGetSystemTimeX(); /* If the requested delay is lower than the minimum safe delta then it is raised to the minimum safe value.*/ if (delay < (systime_t)CH_CFG_ST_TIMEDELTA) { delay = (systime_t)CH_CFG_ST_TIMEDELTA; } /* Special case where the timers list is empty.*/ if (&ch.vtlist == (virtual_timers_list_t *)ch.vtlist.next) { /* The delta list is empty, the current time becomes the new delta list base time, the timer is inserted.*/ ch.vtlist.lasttime = now; ch.vtlist.next = vtp; ch.vtlist.prev = vtp; vtp->next = (virtual_timer_t *)&ch.vtlist; vtp->prev = (virtual_timer_t *)&ch.vtlist; vtp->delta = delay; /* Being the first element in the list the alarm timer is started.*/ port_timer_start_alarm(ch.vtlist.lasttime + delay); return; } /* Special case where the timer will be placed as first element in a non-empty list, the alarm needs to be recalculated.*/ delta = now + delay - ch.vtlist.lasttime; if (delta < ch.vtlist.next->delta) { /* New alarm deadline.*/ port_timer_set_alarm(ch.vtlist.lasttime + delta); } } #else /* CH_CFG_ST_TIMEDELTA == 0 */ /* Delta is initially equal to the specified delay.*/ delta = delay; #endif /* CH_CFG_ST_TIMEDELTA == 0 */ /* The delta list is scanned in order to find the correct position for this timer. */ p = ch.vtlist.next; while (p->delta < delta) { delta -= p->delta; p = p->next; } /* The timer is inserted in the delta list.*/ vtp->next = p; vtp->prev = vtp->next->prev; vtp->prev->next = vtp; p->prev = vtp; vtp->delta = delta /* Special case when the timer is in last position in the list, the value in the header must be restored.*/; p->delta -= delta; ch.vtlist.delta = (systime_t)-1; }