Waiting for a specific wall-clock time in Unix

February 16, 2017

At least on Unix systems, time is a subtle but big pain for programmers. The problem is that because the clock can jump forward, stand still (during leap seconds), or even go backwards, your expectations about what subtracting and adding times does can wind up being wrong under uncommon or rare circumstances. For instance, you can write code that assumes that the difference between a time in the past and now() can be at most zero. This assumption recently led to a Cloudflare DNS outage during a leap second, as covered in Cloudflare's great writeup of this incident.

The solution to this is a new sort of time. Instead of being based on wall-clock time, it is monotonic; it always ticks forward and ticks at a constant rate. Changes in wall-clock time don't affect the monotonic clock, whether those are leap seconds, large scale corrections to the clock, or simply your NTP daemon running the clock a little bit slow or fast in order to get it to the right time. Monotonic clocks are increasingly supported by Unix systems and more and more programming environments are either supporting them explicitly or quietly supporting them behind the scenes. All of this is good and fine and all that, and it's generally just what you want.

I have an unusual case, though, where I'd actually like the reverse functionality. I have a utility that wants to wait until a specific wall-clock time. If the system's wall-clock time is adjusted, I'd like my waiting to immediately be updated to reflect that and my program woken up if appropriate. Until I started writing this entry, I was going to say that this is impossible, but now I believe that it's possible in POSIX. Well, in theory it's possible in POSIX; in practice it's not portable to at least one major Unix OS, because FreeBSD doesn't currently support the necessary features.

On a system that supports this POSIX feature, you have two options: just sleeping, or using timers. Sleeping is easier; you use clock_nanosleep using the CLOCK_REALTIME clock with the TIMER_ABSTIME flag. The POSIX standard (and Linux) specify that if the wall-clock time is changed, you still get woken up when appropriate. With timers, you use a similar but more intricate process. You create a CLOCK_REALTIME timer with timer_create and then use timer_settime to set a TIMER_ABSTIME wait time. When the timer expires, you get signalled in whatever way you asked for.

In practice, though, this doesn't help me. Not only is this clearly not supported on every Unix, but as far as I can see Go doesn't expose any API for clock_nanosleep or equivalent functionality. This isn't terribly surprising, since sleeping in Go is already deeply intertwined with its multi-threaded runtime. Right now my program just approximates what I want by waking up periodically in order to check the clock; this is probably the best I can do in general for a portable program, even outside of Go.

(If I was happy with a non-portable program that only worked on Linux, probably the easiest path would be to use Python with the ctypes module to directly call clock_nanosleep with appropriate arguments. I'm picking Python here because I expect it's the easiest language for easy and reasonably general time parsing code. Anyways, I already know Python and I've never used the ctypes module, so it'd be fun.)

Sidebar: The torture case here is DST transitions

I started out thinking that DST transitions would be a real problem, since either an hour disappears or happens twice. For example, if I say 'wait until 2:30 am' on the night of a transition into DST, I probably want my code to wake up again when the wall-clock time ticks from 2 am straight to 3 am. Similarly, on a transition out of DST, should I say 'wake up at 2:10 am', I probably don't want my code waking up at the second 1:10 am.

However, the kernel actually deals in UTC time, not local time. In practice all of the complexity is in the translation from your (local time) time string into UTC time, and in theory a fully timezone and DST aware library could get this (mostly) right. For '2:30 am during the transition into DST', it would probably return an error (since that time doesn't actually exist), and for '2:10 am during the transition out of DST' it should return a UTC time that is an hour later than you'd innocently expect.

(This does suggest that parsing such times is sort of current-time dependent. Since there are two '1:30 am' times on the transition out of DST, which one you want depends in part on what time it is now. If the transition hasn't happened yet, you probably want the first one; if the transition has happened but it's not yet the new 1:30 am yet, you probably want the second.)

Written on 16 February 2017.
« Another risk of hardware RAID controllers is the manufacturer vanishing
Sometimes, firmware updates can be a good thing to do »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Thu Feb 16 01:33:44 2017
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.