14

I'm writing a socket program that maintains FIFO queues for two input sockets. When deciding which queue to service, the program pulls the most recent time-stamp from each queue.

I need a reliable method for comparing two timeval structs. I tried using timercmp(), but my version of gcc doesn't support it, and documentation states that the function is not POSIX compliant.

What should I do?

Vishal Kotcherlakota
  • 1,134
  • 4
  • 13
  • 36

4 Answers4

17

timercmp() is just a macro in libc (sys/time.h):

# define timercmp(a, b, CMP)                                                  \
  (((a)->tv_sec == (b)->tv_sec) ?                                             \
   ((a)->tv_usec CMP (b)->tv_usec) :                                          \
   ((a)->tv_sec CMP (b)->tv_sec))

If you need timersub():

# define timersub(a, b, result)                                               \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)
Gonzalo
  • 20,805
  • 3
  • 75
  • 78
7

googling timeval give this first result. From that page:

It is often necessary to subtract two values of type struct timeval or struct timespec. Here is the best way to do this. It works even on some peculiar operating systems where the tv_sec member has an unsigned type.

 /* Subtract the `struct timeval' values X and Y,
    storing the result in RESULT.
    Return 1 if the difference is negative, otherwise 0.  */

 int
 timeval_subtract (result, x, y)
      struct timeval *result, *x, *y;
 {
   /* Perform the carry for the later subtraction by updating y. */
   if (x->tv_usec < y->tv_usec) {
     int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
     y->tv_usec -= 1000000 * nsec;
     y->tv_sec += nsec;
   }
   if (x->tv_usec - y->tv_usec > 1000000) {
     int nsec = (x->tv_usec - y->tv_usec) / 1000000;
     y->tv_usec += 1000000 * nsec;
     y->tv_sec -= nsec;
   }

   /* Compute the time remaining to wait.
      tv_usec is certainly positive. */
   result->tv_sec = x->tv_sec - y->tv_sec;
   result->tv_usec = x->tv_usec - y->tv_usec;

   /* Return 1 if result is negative. */
   return x->tv_sec < y->tv_sec;
 }
AShelly
  • 34,686
  • 15
  • 91
  • 152
  • 1
    Thanks for the tip, but I'm already using the code. As a matter of fact, I just realized what my problem was. Thanks for checking Google, it's probably the first thing I would have posted as an answer. =) – Vishal Kotcherlakota Dec 07 '09 at 06:07
  • 2
    To clarify, I realized that the timevals were initialized to have `tv_sec` and `tv_usec` values of 0. Using `gettimeofday()` to properly initialize the queues fixed the issue. Thanks for the support. =) – Vishal Kotcherlakota Dec 07 '09 at 06:19
  • 2AShelly, I'd add, that IMHO it's better to change the second condition the following way `x->tv_usec - y->tv_usec >= 1000000` in order to convert time like `5/1000000` to `6/0`. – Kolyunya Aug 20 '12 at 10:55
  • 2
    Just so that others dont get hit by the same bug I did, be aware that calling this function has a 50-50 chance of modifying the y parameter, causing the timeval you passed in to be modified. – mjs Oct 11 '13 at 07:20
  • @AShelly : Wudn't ` int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;` this operation always put `nsec = 1` since both y->tv_usec and x->tv_usec are less than 1 e6 – Prakhar Agrawal Sep 04 '16 at 10:26
0

This is slightly different, but I think clearly illustrates the logic involved. I'm working on some MSP430 code in C, and have a timestamp struct very similar to timeval, but with nsecs instead of usecs.

This code keeps everything positive, so unsigned ints would work fine, and avoids overflows (I think). It also doesn't modify the timestamps/timevals being passed in, except the result of course.

typedef struct timestamp {
    int32_t secs;
    int32_t nsecs;
} timestamp_t;

int timestamp_sub(timestamp_t * x, timestamp_t * y, timestamp_t * result){
    // returns 1 if difference is negative, 0 otherwise
    // result is the absolute value of the difference between x and y
    negative = 0;
    if( x->secs > y->secs ){
        if( x->nsecs > y->nsecs ){
            result->secs = x->secs - y->secs;
            result->nsecs = x->nsecs - y->nsecs;
        }else{
            result->secs = x->secs - y->secs - 1;
            result->nsecs = (1000*1000*1000) - y->nsecs + x->nsecs;
        }
    }else{
        if( x->secs == y->secs ){
            result->secs = 0;
            if( x->nsecs > y->nsecs ){
                result->nsecs = x->nsecs - y->nsecs;
            }else{
                negative = 1;
                result->nsecs = y->nsecs - x->nsecs;
            }
        }else{
            negative = 1;
            if( x->nsecs > y->nsecs ){
                result->secs = y->secs - x->secs - 1;
                result->nsecs = (1000*1000*1000) - x->nsecs + y->nsecs;
            }else{
                result->secs = y->secs - x->secs;
                result->nsecs = y->nsecs - x->nsecs;
            }
        }
    }
    return negative;
}
travc
  • 1,788
  • 1
  • 18
  • 9
  • 2
    You're better off using integer `1000000000` (or `1000*1000*1000` if you prefer) rather than floating-point `1e9`, as the former is likely to be more efficient. – Toby Speight Jul 19 '17 at 10:12
0

For viewing timevals I just whipped this up. It returns a timeval as a string that you can print or send to a text file:

char *tv2str(struct timeval *intv) {
  static char ans[200];
  snprintf(ans,200,"%u.%u",(unsigned int)intv->tv_sec, \
    (unsigned int) intv->tv_usec);
  return ans;
}

Use like:

printf("nowtv: %s\n",tv2str(&nowtv));

nowtv: 1568407554.646623

Timercmp() didn't seem to work right so I wanted a way to check up on it by actually looking at some values.

Alan Corey
  • 577
  • 6
  • 10