Millis() rollover bug

You idle() does this:

    TimeMs t = g_schedule[nid];
    if (t && t < now)
        markNodeDirty(nid);

Which will fail when millis() rolls over (~49 days). I had the same problem, and same pattern, and just recently looked up the right way to do this. The comparison has to calculate a duration:

if (now - started > duration) { expired! }

Both the times have to be unsigned long, then you get modulus arithmetic, and subtraction of times always gives you the duration.

Unfortunately, that means you will have to store 2 values for each schedule: started and duration.

Of course, isTimedOut() has the same problem.

1 Like

Yes, you’re right. Thank you.

I wonder may we avoid storing two values if declare that the timeout duration can’t be more than a half of the storage domain? For uint32_t it would be 2^31-1. Let me show a simplified example if we used unit8_t instead.

  now=10                         t=240
     +                              +
     |                              |
     |                              |
     |                              |
     |                              |
     |                              |
+----+------------------------------+---------->
0                                        255

MAX_TIMEOUT = 127
isTimedOut = t < now || t - now > MAX_TIMEOUT

I guess it would work. What do you think?

Yes, you could do something like that (though your example code doesn’t look quite right to me: “t < now” is wrong for now==254, t=5). But, as you note, at the cost of limiting the duration. Supposedly, millis() rolls over at about 49 days on the UNO. It seems to vary by processor though.