diff --git a/os/rt/include/chvt.h b/os/rt/include/chvt.h index b2118df07..01dc38ac6 100644 --- a/os/rt/include/chvt.h +++ b/os/rt/include/chvt.h @@ -339,6 +339,26 @@ static inline bool chVTIsArmedI(virtual_timer_t *vtp) { return (bool)(vtp->vt_func != NULL); } +/** + * @brief Returns @p true if the specified timer is armed. + * @pre The timer must have been initialized using @p chVTObjectInit() + * or @p chVTDoSetI(). + * + * @param[in] vtp the @p virtual_timer_t structure pointer + * @return true if the timer is armed. + * + * @api + */ +static inline bool chVTIsArmed(virtual_timer_t *vtp) { + bool b; + + chSysLock(); + b = chVTIsArmedI(vtp); + chSysUnlock(); + + return b; +} + /** * @brief Disables a Virtual Timer. * @note The timer is first checked and disabled only if armed. @@ -466,35 +486,43 @@ static inline void chVTDoTickI(void) { } #else /* CH_CFG_ST_TIMEDELTA > 0 */ virtual_timer_t *vtp; + systime_t now, delta; + + /* First timer to be processed.*/ + vtp = ch.vtlist.vt_next; + now = chVTGetSystemTimeX(); /* The list is assumed to be non-empty because an tick interrupt just occurred.*/ - chDbgAssert(&ch.vtlist != (virtual_timers_list_t *)ch.vtlist.vt_next, + chDbgAssert(&ch.vtlist != (virtual_timers_list_t *)vtp, "timers list empty"); - /* First timer to be processed, its time is assumed to be between - "vt_lasttime" and "now".*/ - vtp = ch.vtlist.vt_next; - + /* The timer time is assumed to be between "vt_lasttime" and "now".*/ chDbgAssert(chVTIsTimeWithinX(ch.vtlist.vt_lasttime + vtp->vt_delta, ch.vtlist.vt_lasttime, - chVTGetSystemTimeX() + 1), + now + 1), "out of time window"); - /* Timers processing loop.*/ - while (true) { - systime_t now; + /* All timers within the time window are triggered and removed, + note that the loop is stopped by the timers header having + "ch.vtlist.vt_delta == (systime_t)-1" which is greater than + all deltas.*/ + while (vtp->vt_delta <= now - ch.vtlist.vt_lasttime) { vtfunc_t fn; /* The "last time" becomes this timer's expiration time.*/ ch.vtlist.vt_lasttime += vtp->vt_delta; - /* The timer is removed from the list and marked as non-armed.*/ vtp->vt_next->vt_prev = (virtual_timer_t *)&ch.vtlist; ch.vtlist.vt_next = vtp->vt_next; fn = vtp->vt_func; vtp->vt_func = NULL; + /* if the list becomes empty then the timer is stopped.*/ + if (ch.vtlist.vt_next == (virtual_timer_t *)&ch.vtlist) { + port_timer_stop_alarm(); + } + /* Leaving the system critical zone in order to execute the callback and in order to give a preemption chance to higher priority interrupts.*/ @@ -507,37 +535,27 @@ static inline void chVTDoTickI(void) { of the list.*/ chSysLockFromISR(); - /* If the list is empty then ending the loop, the list has to be - re-checked because new timers could have been added/removed from - within the callback or other ISRs.*/ + /* Next element in the list, the current time could have advanced so + recalculating the time window.*/ vtp = ch.vtlist.vt_next; - if (&ch.vtlist == (virtual_timers_list_t *)vtp) { - /* Timers list empty, stopping alarms.*/ - port_timer_stop_alarm(); - return; - } - - /* Getting the current system time and calculating the time window since - the last time has expired.*/ now = chVTGetSystemTimeX(); - - /* The next element is outside the current time window, the loop - is stopped here.*/ - if (vtp->vt_delta > now - ch.vtlist.vt_lasttime) { - /* Updating the alarm to the next deadline, deadline that must not be - closer in time than the minimum time delta.*/ - systime_t delta = ch.vtlist.vt_lasttime + vtp->vt_delta - now; - if (delta < (systime_t)CH_CFG_ST_TIMEDELTA) { - delta = (systime_t)CH_CFG_ST_TIMEDELTA; - } - port_timer_set_alarm(now + delta); - - chDbgAssert((chVTGetSystemTimeX() - ch.vtlist.vt_lasttime) < delta, - "exceeding delta"); - - return; - } } + + /* if the list is empty, nothing else to do.*/ + if (ch.vtlist.vt_next == (virtual_timer_t *)&ch.vtlist) { + return; + } + + /* Recalculating the next alarm time.*/ + delta = ch.vtlist.vt_lasttime + vtp->vt_delta - now; + if (delta < (systime_t)CH_CFG_ST_TIMEDELTA) { + delta = (systime_t)CH_CFG_ST_TIMEDELTA; + } + port_timer_set_alarm(now + delta); + + chDbgAssert((chVTGetSystemTimeX() - ch.vtlist.vt_lasttime) <= + (now + delta - ch.vtlist.vt_lasttime), + "exceeding delta"); #endif /* CH_CFG_ST_TIMEDELTA > 0 */ } diff --git a/os/rt/src/chvt.c b/os/rt/src/chvt.c index 1d7661cb9..58fce1f6d 100644 --- a/os/rt/src/chvt.c +++ b/os/rt/src/chvt.c @@ -101,40 +101,56 @@ void chVTDoSetI(virtual_timer_t *vtp, systime_t delay, vtp->vt_par = par; vtp->vt_func = vtfunc; - p = ch.vtlist.vt_next; #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.vt_next) { + /* 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; + } + + /* The delta list is empty, the current time becomes the new + delta list base time, the timer is inserted.*/ + ch.vtlist.vt_lasttime = now; + ch.vtlist.vt_next = vtp; + ch.vtlist.vt_prev = vtp; + vtp->vt_next = (virtual_timer_t *)&ch.vtlist; + vtp->vt_prev = (virtual_timer_t *)&ch.vtlist; + vtp->vt_delta = delay; + + /* Being the first element in the list the alarm timer is started.*/ + port_timer_start_alarm(ch.vtlist.vt_lasttime + delay); + + return; } - 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 { + /* Special case where the timer will be placed as first element in a + non-empty list, the alarm needs to be recalculated.*/ + if ((now + delay) < (ch.vtlist.vt_lasttime + ch.vtlist.vt_next->vt_delta)) { + /* 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; + } + /* 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); - } + /* New alarm deadline.*/ + 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. */ + p = ch.vtlist.vt_next; while (p->vt_delta < delay) { delay -= p->vt_delta; p = p->vt_next;