On the two systems I've tested (a 32-bit Ubuntu 12.04 server and a 64-bit Ubuntu 13.10 VM), the seconds since the epoch given by time() may differ from gettimeofday()'s.
Specifically, though I call time()
after calling gettimeofday()
, the value returned by time()
is sometimes less than the tv_sec
value returned by gettimeofday()
.
This apparently occurs just after the clock rolls over to a new second.
This caused bugs in some of my code that expected time()'s and gettimeofday()'s seconds to be interchangeable.
Sample code demonstrating this problem:
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main()
{
time_t start = time(NULL);
int same = 0;
int different = 0;
int max_usec = 0;
while (1) {
time_t t;
struct timeval tv;
gettimeofday(&tv, NULL);
t = time(NULL);
if (t < tv.tv_sec) {
different++;
if (tv.tv_usec > max_usec) {
max_usec = tv.tv_usec;
}
} else {
same++;
}
if (t > start + 5) {
break;
}
}
printf("Same: %i\n", same);
printf("Different: %i\n", different);
printf("Largest difference seen at %i\n", max_usec);
}
Note that I'm calling time() second and only complaining if its value is less than gettimeofday()'s.
Sample output:
Same: 33282836
Different: 177076
Largest difference seen at 5844
I.e., the two values were the same 33 million times, they were different 177k times, and they were always different within 5844 microseconds of a new second.
Is this a known issue? What causes this?
Both calls are implemented as kernel syscalls. Both functions end up reading a struct timekeeper
, both refer to the very same instance. But they differ in what they do with it:
uses the get_seconds()
function, which is a shortcut to this:
struct timekeeper *tk = &timekeeper;
return tk->xtime_sec;
it just returns xktime_sec
.
gettimeofday()
:gettimeofday()
on the other hand uses do_gettimeofday()
(via getnstimeofday
) which reads both fields xktime_sec
as well as xktime_nsec
(via timekeeping_get_ns
). Here it might happen that xktime_nsec
holds more nanoseconds than a second. This potential extra time is used to increase the tv_sec
field by calling the function timespec_add_ns()
which does this:
a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
a->tv_nsec = ns;
So, tv_sec
might get bigger than the xktime_sec
field was. And there you have it: a little difference in what time()
gives you and what gettimeofday()
gives you.
I fought against this issue in fluxbox today and until a better solution occurs I live with this:
uint64_t t_usec = gettimeofday_in_usecs(); // calcs usecs since epoch
time_t t = static_cast<time_t>(t_usec / 1000000L);