Search code examples
c++linuxtimedrift

Linux/c++ timing method to gaurentee execution every N seconds despite drift?


I have a program that needs to execute every X seconds to write some output. The program will do some intermittent polling and processing between each output. So for example I may be outputing every 5 seconds, but I wake up to poll every .1 seconds until I hit the next 5 second mark. The program in theory will run for months between restart, possible even longer.

I need the the execution at every X seconds to stay consistent to wall clock. In other words I can't allow clock drift to cause me to drift away from the X second mark. I don't need quite the same level of accuracy in the intermitent polling, but I would like to poll more often then a second so I need a structur that can represent sub-second percision.

I realize that by the very nature of running on an OS there wil be a certain inconsistency/latency in execution of any timer such that I can't gaurentee that I'll execute at exactly ever x seconds. But that is fine so long at it stays a small normal distribution, what I don't want is for drift to allow the time to consistently get further and further off.

I would also prefer to try to minimize the CPU cost of the polling as much as possible, but that's a secondary concern.

What timing constructs are available for Linux that can best provide me this level of percision? I'm trying to avoid including boost in the application due to hassles in distribution, but can use it if I have to. So methods using the standard c++ libraries are prefered, but if Bosst can do it better I would like to know that as well.

Thank you.

-ps, I can't use c++11. It's not an option so there is no way I can use any constructs from it.


Solution

  • clock_gettime and clock_nanosleep both operate on sub-second times, and use of CLOCK_MONOTONIC should prevent any skew due to adjustments to system time (such as NTP or adjtimex). For example,

    long delay_ns = 250000000;
    struct timespec next;
    clock_gettime(CLOCK_MONOTONIC, &next);
    while (1) {
        next.ts_nsec += delay_ns;
        next.ts_sec += ts_nsec / 1000000000;
        next.ts_nsec %= 1000000000;
        clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
        /* do something after the wait */
    }
    

    In reality, you should check whether you've returned early due to a signal and whether you should skip an interval because too much time passed while you were sleeping.

    Other methods of sleeping, such as nanosleep and select, only allow for specifying a time interval and use CLOCK_REALTIME, which is system time and may change.