The effect of changing system time on sleeping threads


If you take a look at the clock_gettime() function, which is available in all BSDs and is actually defined as part of the POSIX standard, you see that there is support for at least three types of clocks (many systems support more than these clocks, but actually the POSIX standard only demands one to be present, all others are optional):

  • CLOCK_REALTIME – POSIX demands this to be present. This is the wall time clock.

  • CLOCK_MONOTONIC – No idea what this is (and what SI seconds mean), but I understand that this clock will never jump backwards, it can only monotonically increase in value.

  • CLOCK_UPTIME – I fail to see how this is different to CLOCK_MONOTONIC (uptime also never jumps backwards), but at least I know that this clock starts at zero when the kernel boots (whereas it's not defined what initial value CLOCK_MONOTONIC will have when the kernel boots)

Let's ignore the other clocks for a second. CLOCK_REALTIME is not guaranteed to monotonically count upwards, right? This is the actual "system time". I can alter the system time at will. I can set it 3 month into the past or 5 years into the future and each time my system syncs time using a NTP server on the net, the time might jump forward or backward.

Now we have two sleeping functions in a BSD system. sleep() and nanosleep(). I'm not sure, but I would expect sleep() to be implemented on top of nanosleep, after all I can easily emulate sleep() by using nanosleep() and only set the number of seconds in the struct timespec, keeping nanoseconds zero.

I have read at many sources, that these functions actually work by calculating the wake-up time (get current time, add sleep amount to it) and the system will then check in regular interval if the current time is later than the wake-up time and if so, it will wake up the thread again. The fact that this is only checked in intervals is the reason why the man pages say that the current sleep will sleep for at least this amount of time (shorter only if interrupted by a signal), but it may sleep longer (depending on how often the system checks if we are already past the wake-up time and depending on how long it takes before the scheduler allows this thread to run again).

This is all perfectly sane to me… but there is one question that always bugged me:

According to various sources the sleeps (at least the nanosleep) use CLOCK_REALTIME as clock internally. This means, if tell nanosleep() to sleep for 30 seconds, then change my system clock to 1 hour in the future, the thread will wake up almost immediately (1 hour in the future is way ahead of the wake-up time nanosleep() calculated). This is also perfectly okay. However what happens if I say wake up in 30 seconds and then the user finds out that his system clock is one hour ahead and sets his clock backwards by one hour? Then my thread will sleep for 1 hour and 30 seconds? As that would be rather bad.

Best Solution

As far as I know, sleep functions are usually implemented more like a decrementing counter. You say "sleep for 10 seconds," that translates into "sleep for 1000 schedule ticks" in the scheduler, and then every time the scheduler checks on sleeping processes it decrements the amount of time left.

In this way, the sleep time will always be an amount of real time to sleep, as opposed to sleeping until some time in the future. The reason for this is as you've suspected, if we pick a time in the future we might never get there (or might get there in an unexpected amount of time). This is consistent with what you would want to use sleep for in a program. It's not meant to do calendar-like calculations.

You can also make a simple test, make a program sleep for 30 seconds, use the nix "time" command to time how long the function runs, and after it starts change your system clock back 5 minutes and see what happens.

Related Question