multipart-mixed

Mini-Pattern: Interval

Create an easy way to check if a certain amount of time has elapsed, but avoid rollover problems.

Applicability

Use when an action must be performed only during a certain time interval, and waiting on a condition cannot be used (e.g. pthread_cond_timedwait()).

Motivation

Checking for a timeout condition seems like one of those super-easy problems, and it certainly can be, but there's also a gotcha! case which must be avoided. Specifically, this obvious code has a non-obvious problem:

/* Returns current time in milliseconds */
inline time_t now()
{
    timeval now;
    gettimeofday(&now, 0);
    return (now.tv_sec * 1000) + (now.tv_usec / 1000);
}

time_t start = now();

/* Do stuff, but only for 10 seconds */
while (now() < start + 10000)       // BAD!
{
    // do stuff, may take several ms per loop
}

This logic works most the time. In fact, it'll only fail about every 50 days or so. Why? Consider what happens when the start time is, say, 0xffffd8ef (assuming 32-bit time_t). The loop can easily go out to lunch, because the finish time (start + 10000) is now 0xffffffff. Unless you check the loop guard exactly at time 0xffffffff, the guard will be true. If you check time at 0xfffffffe, the loop will correctly be under the timeout, and continue running. If the loop takes several milliseconds to come back around, however, the time will have rolled over past 0.

There's a simple way to fix this case. Rather than comparing absolute times, compare intervals of time instead. Rewrite the code above as:

time_t start = now();

/* Do stuff, but only for 10 seconds */
while (now() - start < 10000)
{
    // do stuff, may take several ms per loop
}

This works even when values of now() roll over.

Sample Code

As is frequently the case, a commonly used algorithm can be made into a lightweight class for easy reuse. This C++ class provides easy access to the "correct" way to check timeout cases like those discussed above:

/** Time interval class with millisecond accuracy */
class Interval
{
  public:

    /** Constructor sets start time -- use this instance immediately! */
    Interval()
    {
        start = now();
    }

    /** Elapsed time (milliseconds) since creation of instance. */
    inline time_t elapsed() const
    {
        return now() - start;
    }

  private:

    /** Time at creation of instance. */
    time_t start;

    /** Current time (milliseconds). Note: use GetTickCount() on Win32. */
    inline time_t now() const
    {
        timeval now;
        gettimeofday(&now, 0);
        return (now.tv_sec * 1000) + (now.tv_usec / 1000);
    }
};

This can be used as follows:

for (Interval i; i.elapsed() < 10000 /* 10 seconds */;)
{
    // do stuff...
}

Credit

Closely follows the class proposed by Dominic Herity in comments section of this article.