{
atomic
{
-
m_upper++;
if ((m_upper & OVERFLOW_MASK) == 0)
signal Counter.overflow();
void set_alarm()
{
- to_size_type now = call Counter.get(), expires, remaining;
-
- /* m_t0 is assumed to be in the past. If it's > now, we assume
- that time has wrapped around */
-
- expires = m_t0 + m_dt;
-
- /* The cast is necessary to get correct wrap-around arithmetic */
- remaining = (to_size_type)(expires - now);
+ to_size_type now = call Counter.get(), elapsed = now - m_t0, remaining;
- /* if (expires <= now) remaining = 0; in wrap-around arithmetic */
- if (m_t0 <= now)
- {
- if (expires >= m_t0 && // if it wraps, it's > now
- expires <= now)
- remaining = 0;
- }
+ m_skip_overflows = 0;
+ if (elapsed >= m_dt)
+ {
+ remaining = 0;
+ m_t0 += m_dt;
+ m_dt = 0;
+ }
else
- {
- if (expires >= m_t0 || // didn't wrap so < now
- expires <= now)
- remaining = 0;
- }
+ {
+ remaining = m_dt - elapsed;
- if (remaining > MAX_DELAY * 2)
+ /* MAX_DELAY is 1/2 an underlying counter overflow time. Just count
+ overflows if the timer is far in the future, and we'll set an
+ alarm once we're close to the deadline. */
+ if (remaining > MAX_DELAY * 2)
{
if (remaining >= MAX_DELAY * 2 * (to_size_type)256)
m_skip_overflows = 255;
m_skip_overflows = remaining / (MAX_DELAY * 2);
return;
}
- else
- m_skip_overflows = 0;
- if (remaining > MAX_DELAY)
+ if (remaining > MAX_DELAY)
{
m_t0 = now + MAX_DELAY;
m_dt = remaining - MAX_DELAY;
remaining = MAX_DELAY;
}
- else
+ else
{
m_t0 += m_dt;
m_dt = 0;
}
+ }
call AlarmFrom.startAt((from_size_type)now << bit_shift_right,
(from_size_type)remaining << bit_shift_right);
}
} Timer_t;
Timer_t m_timers[NUM_TIMERS];
-
- enum {
- /* bitmask */
- S_TIMER_RUNNING = 1,
- S_TIMER_CHANGED = 2
- };
- bool state;
+ bool m_timers_changed;
task void executeTimersNow();
- void executeTimers(uint32_t then)
+ void executeTimers()
{
- int32_t min_remaining = (1UL<<31)-1; //max signed int32_t
+ /* This code supports a maximum dt of MAXINT. If min_remaining and
+ remaining were switched to uint32_t, and the logic changed a
+ little, dt's up to 2^32-1 should work (but at a slightly higher
+ runtime cost). */
+ uint32_t now = call TimerFrom.getNow();
+ int32_t min_remaining = (1UL << 31) - 1; /* max int32_t */
bool min_remaining_isset = FALSE;
- int num;
+ uint8_t num;
- /* We always come here with the timer stopped */
- state = 0;
+ call TimerFrom.stop();
+ m_timers_changed = FALSE;
- for(num=0; num<NUM_TIMERS; num++)
+ for (num=0; num<NUM_TIMERS; num++)
{
Timer_t* timer = &m_timers[num];
if (timer->isrunning)
{
- // Calculate "remaining" before the timer is fired. If a timer
- // restarts itself in a fired event, then we 1) need a consistent
- // "remaining" value to work with, and no worries because 2) all
- // start commands post executeTimersNow, so the timer will be
- // recomputed later, anyway.
+ uint32_t elapsed = now - timer->t0;
- uint32_t elapsed = then - timer->t0;
- int32_t remaining = timer->dt - elapsed;
-
- if (remaining <= 0)
+ if (elapsed >= timer->dt)
{
if (timer->isoneshot)
{
}
else
{
- // The remaining time is non-positive (the timer had fired).
- // So add dt to convert it to remaining for the next event.
+ // Update timer for next event
timer->t0 += timer->dt;
- remaining += timer->dt;
+ // Update elapsed so we compute the time of the next event
+ elapsed -= timer->dt;
}
signal Timer.fired[num]();
}
- // check isrunning in case the timer was stopped in the fired
+ // Check isrunning in case the timer was stopped in the fired
// event or this was a one shot timer; note that a one shot
// timer that was restarted in its fired event will push us
- // through here with remaining <= 0, but we're already scheduled
- // an executeTimersTask in that case (and suppressed setting
- // TimerFrom)
+ // through here with a value of remaining <= 0. But we're
+ // already scheduled an executeTimersNow in that case (and
+ // suppressed setting TimerFrom)
if (timer->isrunning)
{
+ int32_t remaining = timer->dt - elapsed;
+
if (remaining < min_remaining)
- min_remaining = remaining;
- min_remaining_isset = TRUE;
+ {
+ min_remaining = remaining;
+ min_remaining_isset = TRUE;
+ }
}
}
}
- if (!(state & S_TIMER_CHANGED) && min_remaining_isset)
+ if (!m_timers_changed && min_remaining_isset)
{
if (min_remaining <= 0)
post executeTimersNow();
else
- {
- call TimerFrom.startOneShotAt(then, min_remaining);
- state |= S_TIMER_RUNNING;
- }
+ call TimerFrom.startOneShotAt(now, min_remaining);
}
}
event void TimerFrom.fired()
{
- executeTimers(call TimerFrom.gett0() + call TimerFrom.getdt());
+ executeTimers();
}
task void executeTimersNow()
{
- /* If we set a timer, try and stop it. But if it believes it's
- not running, that means that the execution of its fired event
- is pending, so we:
- - don't need to call executeTimers (it will happen soon)
- - we must not call executeTimers, because that would change the
- timer's setting, confusing the pending call to TimerFrom.fired
- (it would call executeTimers with an incorrect "then"
- parameter)
- We need to use an atomic section even though it's a "timer",
- because it's based on an underlying alarm, whose transitions
- are asynchronous (yuck?)
- */
- if (state & S_TIMER_RUNNING)
- atomic
- {
- if (!call TimerFrom.isRunning())
- return;
- call TimerFrom.stop();
- }
- executeTimers(call TimerFrom.getNow());
+ executeTimers();
}
void startTimer(uint8_t num, uint32_t t0, uint32_t dt, bool isoneshot)
timer->dt = dt;
timer->isoneshot = isoneshot;
timer->isrunning = TRUE;
- state |= S_TIMER_CHANGED;
+ m_timers_changed = TRUE;
post executeTimersNow();
}