py-monotime, CLOCK_MONOTONIC, and time.monotonic()
An increasingly common problem nowadays is caused by the fact that the
time() or gettimeofday() system calls do not always return monotonically
increasing values. That is, sometimes they go backwards, and sometimes they
take giant leaps.
In Unix, thankfully, timezones don't affect this: you don't have to worry
about, say, daylight savings time messing up your time calculations, as long
as you use time_t or struct timeval instead of struct tm for any long-lived
storage. (Example of not doing it right: if your log file includes the
current local time of each message, then every autumn when daylight savings
causes a one-hour backwards jump, you will have log messages that appear out
of order in the file, and if you sort them by date, it'll be a lie.)
However, there are other things that do make a mess, because Unix time was
not entirely well thought out when it was invented. The most commonly known
problem is NTP, which jumps your clock around sometimes. People mostly try
to ignore this by just assuming that after boot, NTP won't *jump* the time,
it'll only *slew* the time (which it tries very hard to do), and this mostly
works, so people get away with this assumption 99.9% of the time, and their
program goes slightly bananas the other 0.1% of the time, after which they
typically reboot and are happy.
There's another annoyingly niggly one though, which is virtually impossible
to work around: leap seconds. As implemented in Unix, a leap second
literally causes the same time_t to occur *twice*. That turns out to be
most definitely the wrong answer; the right answer would have been to
include leap seconds in the localtime() calculation, not in the kernel's
implementation of time. But sadly, mistakes were made, and we have what we
have. There are many stories in computer lore of programs that work great
except at the exact moment of a leap second, at which time all sorts of
things (notably multimedia timing algorithms) go completely wrong. Once
again, though, rebooting fixes it.
Anyway, it turns out there is already a solution for all these problems, and
it's been out there for a long time, but not well standardized. The
solution is called a "monotonic clock." In a monotonic clock, absolute
times aren't very meaningful (they are usually "seconds since the system
booted" or something like that) but *relative* times, which are almost always
what you care about, always do what you want. So if you say, "give me an
event 60 seconds from now" then you schedule it for time monotonic() + 60,
and life is simple and good, and you don't need any crazy
hackery to make sure you deal correctly with backwards-flowing time.
If you want access to this lovely thing on a system compliant with
POSIX.1-2001, such as Linux, what you want is the
clock_gettime(CLOCK_MONOTONIC) system call. Unfortunately, non-Linux
systems seem to largely not support it. On MacOS, you can use this
advice from Apple instead.
What's worse, if you're writing in python, there is no access to monotonic
time even on systems that *do* support it. Well, there is, starting in
python 3.3 apparently (which adds a time.monotonic() function), but nobody
uses python 3, so that doesn't really help most of us. For the rest of the
world, I just made a new python module called monotime. If you import
it, then time.monotonic() suddenly appears, and you can write your program
to use it, just like if it were supported internally in your copy of python.
It's licensed under the Apache 2.0 license. You can get it through pypi and I also
added Debian packaging scripts.
I should also mention python-monotonic-time,
which I didn't use for two reasons. First, it's under the GPLv3 (not the
LGPL), which would infect any program that uses it. Secondly, it's written
using python's ctypes, which is great for hackery, but is very brittle (it
depends tightly on system-dependent library names and struct formats,
without actually including the system header files during build) and much
slower than a C module, which is what my monotime module uses. Programs
that need monotonic time typically need a *lot* of monotonic time, and you
want it to be fast.
For more time-related trivia than you can possibly imagine, check out
python's PEP 418,
which introduced the time.monotonic() function and a few other things.
By the way, getting approval to release this code through Google's
super-streamlined open source releasing process took only 47 (monotonic)
minutes and almost no CPU time.
August 10, 2012 22:27