A surprising missing Unix command: waiting until a time is reached

August 17, 2016

I tweeted:

A surprising missing Unix command: 'sleep until it is time <X> or later'. Even GNU sleep only sleeps for a duration.

The obvious option here is at, but at has a number of drawbacks that make it far from an ideal experience. For now, I'll just note that it of course runs your commands non-interactively, and sometimes you need interactivity. Perhaps you're doing:

waituntil 17:50; rsync -a login@host:/some/thing .

(Which is in fact more or less one of the things that I wanted this for today.)

Original V7 Unix of course had a perfectly sensible reason not to bother implementing something like this, namely that you can easily put a reasonable version together by using some shell scripting to work out how many seconds to tell sleep to sleep. In the relatively minimal V7 environment, a dedicated command for waituntil would not have been quite out of place and not Unixy. Indeed, I wouldn't expect traditionally minded Unixes like the *BSDs to pick up such a program or option to sleep.

On the other hand, GNU coreutils is a different matter. The GNU people are perfectly happy to add features to traditional Unix commands (often quite handy ones), much to the despair of Unix traditionalists. They have certainly added some features to GNU sleep, like multiple arguments in multiple formats, so it wouldn't have surprised me at all if they'd added a 'sleep until absolute time <X>' option as well. But they haven't (so far) and as far as I could tell on a casual perusal of my Linux systems, no one else has written something like this and gotten it packaged so it's commonly installed.

Because sometimes I'm a system programmer as well as a sysadmin, I wound up writing my own version of a waituntil program. It's in Go because that seemed the right language and I felt like it.

(I picked Go because the hard part is parsing and manipulating the time argument, and Go actually has a quite nice and flexible system for that. This turned out somewhat more complicated than I expected, but these things happen.)

Sidebar: The right way to do this in the modern Unix API

My version of waituntil and any version that is based on top of sleep have a little problem: they don't necessarily cope with the system clock changing. If it's 16:10 now, you say 'waituntil 16:20', and a minute later the system clock jumps to 16:20, you probably want to have the wait finish right then because you've reached the target time.

As far as I can see, the correct way to do this on a modern standards-compliant Unix system is to use clock_nanosleep() with TIMER_ABSTIME to wait to an absolute time, and use CLOCK_REALTIME as the clock you're doing this against. Given this, you should be woken the moment the system's clock hits (or passes) the absolute time you specified, even if the clock is adjusted forward or backward in the mean time.

(Of course, support for this isn't necessarily there in all Unixes, at least right now.)

Unfortunately for me, Go doesn't expose clock_nanosleep() (except as a raw Linux syscall, and I don't feel like going there). For my personal use this is okay, but it would be nice to do better.

Comments on this page:

By newt0311@gmail.com at 2016-08-18 00:55:30:

GNU coreutils' date has a fair amount of date-parsing smarts in it already; seems like ~95% of the functionality could be had for ~1% of the effort with something like:

 waituntil() { sleep $(($(date +%s -d "$*") - $(date +%s))); }

(Still doesn't handle clock adjustments with any grace of course, but as a bonus it would support convenient shorthand like waituntil 14:30 tomorrow, thanks to date.)

By Oliver at 2016-08-18 06:10:31:

What about the at command?

By Dan Astoorian (Dan.Astoorian) at 2016-08-18 12:38:20:

There's a conceptual difference between "wait for a time interval of X" and "wait until the time is X or later": an API which implements the latter needs to, by definition, deal with discontinuities.

Consider, for instance, a Raspberry Pi, which has no hardware real-time clock and relies on the NTP daemon (which may take several minutes after boot to initialize) for its concept of the current time; its clock may jump forward days or weeks.

Changing the clock like this should not (according to POSIX) affect threads that are sleeping for a particular number of seconds (or fractions thereof). So if you implement "sleep until 2pm tomorrow" as "calculate the interval between now and 2pm tomorrow, and sleep that amount of time," you will not get the behaviour you expected if the clock is corrected in the meantime.

Written on 17 August 2016.
« How you tell what signals a Linux process is ignoring
Localhost is (sometimes) a network »

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

Last modified: Wed Aug 17 23:02:47 2016
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.