time() and gettimeofday() return different seconds
Asked Answered
M

3

21

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?

Mustang answered 7/4, 2014 at 15:56 Comment(8)
I've added a clarification to your question to make it clearer that you're getting results that appear to show time going backwards.Transpose
Is an ntp client running and changing the clock?Pedropedrotti
I have tried your code on a RedHat linux implementation and found no different values. Seems my vsyscall implementation is better.Prospect
@Pedropedrotti - I shut down the NTP client and still see this behavior.Mustang
I wonder if it is because you are running it in the loop that you are seeing the slow down inside the kernel.Letterhead
I see similar behavior on my Linux Mint x86_64 system. A version of your program that prints more information shows that seems to indicate that the time() value is fairly consistently updated between 7000 and 7003 microseconds after the gettimeofday() value is updated. On my x86_64 Debian system (running an older kernel), it shows no inconsistencies.Transpose
If I ask you the time on your watch, then ask you the time on your wall clock, are they the same? Sometimes they are, sometimes not. So why are you expecting two different calls to get the time to be equal. Even calling time(NULL) twice might give different results. I remember in the old dos days that calling the time function would randomise the fraction part to make it more 'real-time'. If you need your timekeeping to be consistent, always the same function.Kusin
@Neil: It is not the fact that they're unequal but the fact that the second time requested is often before the first. A more apt analogy is: I ask you the time in hours and minutes from your watch, then I ask you then time just in hours, and sometimes you give me answer pairs like "05:00" followed by "04".Hitherto
M
23

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:

time():

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);
Moleskins answered 11/5, 2014 at 20:44 Comment(2)
This Is a helpful answer, but I felt it didn't really explain why timekeeping_get_ns() might return more than a billion nanoseconds.Cousins
@Cousins bugs.php.net/bug.php?id=69044, thanks to NikiCLost
P
9

Both time and gettimeofday are implemented as so called Linux vsyscalls. Means that your code will be redirected to kernel owned, but userspace mapped pages containing the results which are only periodically updated.

In Ubuntu (I have not observed this behaviour in RedHat Linux) the value for gettimeofday is updated before the value for time thus it is possible to get inconsistent values:

kernel updates gettimeofday

You query gettimeofday

You query time

kernel updates time

Swapping your calls around gives consistent results:

t = time(NULL);
gettimeofday(&tv, NULL);
if (t > tv.tv_sec) { ...
Prospect answered 7/4, 2014 at 16:27 Comment(2)
This would certainly explain what I'm seeing, but do you have any links to documentation or kernel source that confirms it? I've tried poking around the kernel source myself and haven't had any luck.Mustang
the order does not matter, see my answer. both functions also use the same time-struct as the base which is updated at the same time (since it is the same instance) for both functions. the difference iis the amount of accumulated nanoseconds and if they are added to the returned seconds.Moleskins
C
7

This behaviour is due to the implementation of timekeeping in the Linux kernel.

Linux maintains a variable which tracks the current wall-clock time; this is maintained to nanosecond precision and is updated periodically. (It is tk_core.timekeeper.{xtime_secs, tkr_mono.xtime_nsec} in recent kernel versions.)

time() calls get_seconds() which simply returns the seconds part of this variable - so, depending on how long ago the wall-clock time was updated, may return a slightly out-of-date value.

gettimeofday() not only reads the latest value of the wall-clock variables, but also (via timekeeping_get_ns()) makes a new reading from the hardware clock (typically the TSC in x86 systems, though this is configurable at runtime) and applies a correction.

Thanks to that correction calculation, it is possible for the result returned by gettimeofday() to roll over into the next second, and hence return a tv_sec value higher than the result of time().

Cousins answered 9/7, 2015 at 10:41 Comment(1)
Thanks! I cite your post Printing current time in milliseconds or nanoseconds with printf builtinDunston

© 2022 - 2024 — McMap. All rights reserved.