0

Consider the following code:

struct timespec ts;
uint64_t start_time;
uint64_t stop_time;

if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
    abort();
}
 
start_time = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
 
/* some computation... */
 
if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
    abort();
}
 
stop_time = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
 
printf("%" PRIu64 "\n", (stop_time - start_time + 500000000) / 1000000000);

In the vast majority of cases, the code works as I expected, i.e., prints the number of seconds that took the computation. Very rarely, however, one anomaly occurs. The program reports the number of seconds like 18446743875, 18446743877, 18446743962, etc. I figured this number roughly matched 264 nanoseconds (~584 years). So I got the suspicion that ts.tv_nsec is sometimes equal to −1.

So my question is: What's wrong with my code? Where and why does adding 264 nanoseconds happen?

DaBler
  • 2,695
  • 2
  • 26
  • 46
  • What's your OS, including version? And is your system using NTP? – Andrew Henle Oct 09 '21 at 12:19
  • @AndrewHenle `uname -srvmo` gives `Linux 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64 GNU/Linux`. The system uses NTP. However, regarding the NTP, I don't think it can be a jump of 584 years. – DaBler Oct 09 '21 at 12:28
  • Valid values for tv_nsec are in the range [0,999999999] by spec. Can"t be -1. – Jean-Baptiste Yunès Oct 09 '21 at 12:39
  • 2
    You might try `CLOCK_MONOTONIC`. See [Difference between CLOCK_REALTIME and CLOCK_MONOTONIC?](https://stackoverflow.com/questions/3523442) – Steve Summit Oct 09 '21 at 13:15
  • *I don't think it can be a jump of 584 years* No, but you could get a jump that causes your subtraction of unsigned 64-bit values to wrap around – Andrew Henle Oct 09 '21 at 13:18
  • @AndrewHenle I've already thought about this, but it would mean a jump about 500 seconds back. The program normally runs for about 300 seconds. – DaBler Oct 09 '21 at 13:24
  • @SteveSummit Yes, `CLOCK_MONOTONIC` seems like a better choice. – DaBler Oct 09 '21 at 13:26

1 Answers1

0

I don't see anything wrong with your code. I suspect your OS is occasionally delivering an anomalous value for CLOCK_REALTIME — although I'm surprised, and I can't quite imagine what it might be.

I suggest rewriting your code like this:

struct timespec start_ts, stop_ts;
uint64_t start_time;
uint64_t stop_time;

if (clock_gettime(CLOCK_REALTIME, &start_ts) != 0) {
    abort();
}
 
start_time = start_ts.tv_sec * UINT64_C(1000000000) + start_ts.tv_nsec;
 
/* some computation... */
 
if (clock_gettime(CLOCK_REALTIME, &stop_ts) != 0) {
    abort();
}
 
stop_time = stop_ts.tv_sec * UINT64_C(1000000000) + stop_ts.tv_nsec;

uint64_t elapsed = (stop_time - start_time + 500000000) / 1000000000;
printf("%" PRIu64 "\n", elapsed);

if(elapsed > 365 * 86400 * UINT64_C(1000000000)) {
    printf("ANOMALY:\n");
    printf("start_ts = %lu %lu\n", start_ts.tv_sec, start_ts.tv_nsec);
    printf("stop_ts = %lu %lu\n", stop_ts.tv_sec, stop_ts.tv_nsec);
}

Then, if/when it happens again, you'll have more information to go on.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103