]> git.meshlink.io Git - utcp/commitdiff
Use clock_gettime() instead of gettimeofday().
authorGuus Sliepen <guus@sliepen.org>
Sat, 28 Mar 2020 21:30:04 +0000 (22:30 +0100)
committerGuus Sliepen <guus@sliepen.org>
Sat, 28 Mar 2020 21:30:04 +0000 (22:30 +0100)
It is more portable, and allows reading out the monotonic clock. On some
systems it is also faster than gettimeofday().

test.c
utcp.c
utcp.h
utcp_priv.h

diff --git a/test.c b/test.c
index 300ce51e4d3a0cb8f636038a7ca8b766f4c7e39c..2a273de70463dbe4f39117cc50ed438dc46c4834 100644 (file)
--- a/test.c
+++ b/test.c
@@ -291,7 +291,7 @@ int main(int argc, char *argv[]) {
 
        char buf[102400];
 
-       struct timeval timeout = utcp_timeout(u);
+       struct timespec timeout = utcp_timeout(u);
 
        while(!connected || utcp_is_active(u)) {
                size_t max = c ? utcp_get_sndbuf_free(c) : 0;
@@ -304,7 +304,7 @@ int main(int argc, char *argv[]) {
                        max = read_size;
                }
 
-               int timeout_ms = timeout.tv_sec * 1000 + timeout.tv_usec / 1000 + 1;
+               int timeout_ms = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000 + 1;
 
                debug("polling, dir = %d, timeout = %d\n", dir, timeout_ms);
 
diff --git a/utcp.c b/utcp.c
index cf722e51d2afafd9008c179aea5ce70645cbd561..cc17b0f744b0f4b63109daed01b55996f50f7613 100644 (file)
--- a/utcp.c
+++ b/utcp.c
@@ -27,8 +27,6 @@
 #include <stdbool.h>
 #include <string.h>
 #include <unistd.h>
-#include <sys/time.h>
-#include <sys/socket.h>
 #include <time.h>
 
 #include "utcp_priv.h"
 #undef poll
 #endif
 
-#ifndef timersub
-#define timersub(a, b, r)\
-       do {\
-               (r)->tv_sec = (a)->tv_sec - (b)->tv_sec;\
-               (r)->tv_usec = (a)->tv_usec - (b)->tv_usec;\
-               if((r)->tv_usec < 0)\
-                       (r)->tv_sec--, (r)->tv_usec += USEC_PER_SEC;\
-       } while (0)
-#endif
+static void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *r) {
+       r->tv_sec = a->tv_sec - b->tv_sec;
+       r->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+       if(r->tv_nsec < 0) {
+               r->tv_sec--, r->tv_nsec += NSEC_PER_SEC;
+       }
+}
+
+static int32_t timespec_diff_usec(const struct timespec *a, const struct timespec *b) {
+       int64_t diff = (a->tv_sec - b->tv_sec) * 1000000000 + a->tv_sec - b->tv_sec;
+       return diff / 1000;
+}
+
+static bool timespec_lt(const struct timespec *a, const struct timespec *b) {
+       if(a->tv_sec == b->tv_sec) {
+               return a->tv_nsec < b->tv_nsec;
+       } else {
+               return a->tv_sec < b->tv_sec;
+       }
+}
+
+static void timespec_clear(struct timespec *a) {
+       a->tv_sec = 0;
+}
+
+static bool timespec_isset(const struct timespec *a) {
+       return a->tv_sec;
+}
+
+static long CLOCK_GRANULARITY;
 
 static inline size_t min(size_t a, size_t b) {
        return a < b ? a : b;
@@ -70,6 +90,14 @@ static inline size_t max(size_t a, size_t b) {
 #define UTCP_DEBUG_DATALEN 20
 #endif
 
+#ifndef UTCP_CLOCK
+#if defined(CLOCK_MONOTONIC_RAW) && defined(__x86_64__)
+#define UTCP_CLOCK CLOCK_MONOTONIC_RAW
+#else
+#define UTCP_CLOCK CLOCK_MONOTONIC
+#endif
+#endif
+
 static void debug(struct utcp_connection *c, const char *format, ...) {
        struct timespec tv;
        char buf[1024];
@@ -140,7 +168,7 @@ static void set_state(struct utcp_connection *c, enum state state) {
        c->state = state;
 
        if(state == ESTABLISHED) {
-               timerclear(&c->conn_timeout);
+               timespec_clear(&c->conn_timeout);
        }
 
        debug(c, "state %s\n", strstate[state]);
@@ -493,19 +521,27 @@ static void update_rtt(struct utcp_connection *c, uint32_t rtt) {
 }
 
 static void start_retransmit_timer(struct utcp_connection *c) {
-       gettimeofday(&c->rtrx_timeout, NULL);
-       c->rtrx_timeout.tv_usec += c->utcp->rto;
+       clock_gettime(UTCP_CLOCK, &c->rtrx_timeout);
+
+       uint32_t rto = c->utcp->rto;
 
-       while(c->rtrx_timeout.tv_usec >= 1000000) {
-               c->rtrx_timeout.tv_usec -= 1000000;
+       while(rto > USEC_PER_SEC) {
                c->rtrx_timeout.tv_sec++;
+               rto -= USEC_PER_SEC;
        }
 
-       debug(c, "rtrx_timeout %ld.%06lu\n", c->rtrx_timeout.tv_sec, c->rtrx_timeout.tv_usec);
+       c->rtrx_timeout.tv_nsec += c->utcp->rto * 1000;
+
+       if(c->rtrx_timeout.tv_nsec >= NSEC_PER_SEC) {
+               c->rtrx_timeout.tv_nsec -= NSEC_PER_SEC;
+               c->rtrx_timeout.tv_sec++;
+       }
+
+       debug(c, "rtrx_timeout %ld.%06lu\n", c->rtrx_timeout.tv_sec, c->rtrx_timeout.tv_nsec);
 }
 
 static void stop_retransmit_timer(struct utcp_connection *c) {
-       timerclear(&c->rtrx_timeout);
+       timespec_clear(&c->rtrx_timeout);
        debug(c, "rtrx_timeout cleared\n");
 }
 
@@ -544,7 +580,7 @@ struct utcp_connection *utcp_connect_ex(struct utcp *utcp, uint16_t dst, utcp_re
        print_packet(c, "send", &pkt, sizeof(pkt));
        utcp->send(utcp, &pkt, sizeof(pkt));
 
-       gettimeofday(&c->conn_timeout, NULL);
+       clock_gettime(UTCP_CLOCK, &c->conn_timeout);
        c->conn_timeout.tv_sec += utcp->timeout;
 
        start_retransmit_timer(c);
@@ -618,7 +654,7 @@ static void ack(struct utcp_connection *c, bool sendatleastone) {
 
                if(!c->rtt_start.tv_sec) {
                        // Start RTT measurement
-                       gettimeofday(&c->rtt_start, NULL);
+                       clock_gettime(UTCP_CLOCK, &c->rtt_start);
                        c->rtt_seq = pkt->hdr.seq + seglen;
                        debug(c, "starting RTT measurement, expecting ack %u\n", c->rtt_seq);
                }
@@ -715,12 +751,12 @@ ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) {
                buffer_discard(&c->sndbuf, c->sndbuf.used);
        }
 
-       if(is_reliable(c) && !timerisset(&c->rtrx_timeout)) {
+       if(is_reliable(c) && !timespec_isset(&c->rtrx_timeout)) {
                start_retransmit_timer(c);
        }
 
-       if(is_reliable(c) && !timerisset(&c->conn_timeout)) {
-               gettimeofday(&c->conn_timeout, NULL);
+       if(is_reliable(c) && !timespec_isset(&c->conn_timeout)) {
+               clock_gettime(UTCP_CLOCK, &c->conn_timeout);
                c->conn_timeout.tv_sec += c->utcp->timeout;
        }
 
@@ -1415,10 +1451,10 @@ synack:
                // RTT measurement
                if(c->rtt_start.tv_sec) {
                        if(c->rtt_seq == hdr.ack) {
-                               struct timeval now, diff;
-                               gettimeofday(&now, NULL);
-                               timersub(&now, &c->rtt_start, &diff);
-                               update_rtt(c, diff.tv_sec * 1000000 + diff.tv_usec);
+                               struct timespec now;
+                               clock_gettime(UTCP_CLOCK, &now);
+                               int32_t diff = timespec_diff_usec(&now, &c->rtt_start);
+                               update_rtt(c, diff);
                                c->rtt_start.tv_sec = 0;
                        } else if(c->rtt_seq < hdr.ack) {
                                debug(c, "cancelling RTT measurement: %u < %u\n", c->rtt_seq, hdr.ack);
@@ -1490,7 +1526,7 @@ synack:
 
                case CLOSING:
                        if(c->snd.una == c->snd.last) {
-                               gettimeofday(&c->conn_timeout, NULL);
+                               clock_gettime(UTCP_CLOCK, &c->conn_timeout);
                                c->conn_timeout.tv_sec += utcp->timeout;
                                set_state(c, TIME_WAIT);
                        }
@@ -1541,10 +1577,10 @@ synack:
        if(advanced) {
                if(c->snd.una == c->snd.last) {
                        stop_retransmit_timer(c);
-                       timerclear(&c->conn_timeout);
+                       timespec_clear(&c->conn_timeout);
                } else if(is_reliable(c)) {
                        start_retransmit_timer(c);
-                       gettimeofday(&c->conn_timeout, NULL);
+                       clock_gettime(UTCP_CLOCK, &c->conn_timeout);
                        c->conn_timeout.tv_sec += utcp->timeout;
                }
        }
@@ -1672,7 +1708,7 @@ skip_ack:
                        break;
 
                case FIN_WAIT_2:
-                       gettimeofday(&c->conn_timeout, NULL);
+                       clock_gettime(UTCP_CLOCK, &c->conn_timeout);
                        c->conn_timeout.tv_sec += utcp->timeout;
                        set_state(c, TIME_WAIT);
                        break;
@@ -1803,7 +1839,7 @@ int utcp_shutdown(struct utcp_connection *c, int dir) {
 
        ack(c, false);
 
-       if(!timerisset(&c->rtrx_timeout)) {
+       if(!timespec_isset(&c->rtrx_timeout)) {
                start_retransmit_timer(c);
        }
 
@@ -1921,10 +1957,10 @@ int utcp_abort(struct utcp_connection *c) {
  * The return value is the time to the next timeout in milliseconds,
  * or maybe a negative value if the timeout is infinite.
  */
-struct timeval utcp_timeout(struct utcp *utcp) {
-       struct timeval now;
-       gettimeofday(&now, NULL);
-       struct timeval next = {now.tv_sec + 3600, now.tv_usec};
+struct timespec utcp_timeout(struct utcp *utcp) {
+       struct timespec now;
+       clock_gettime(UTCP_CLOCK, &now);
+       struct timespec next = {now.tv_sec + 3600, now.tv_nsec};
 
        for(int i = 0; i < utcp->nconnections; i++) {
                struct utcp_connection *c = utcp->connections[i];
@@ -1944,7 +1980,7 @@ struct timeval utcp_timeout(struct utcp *utcp) {
                        continue;
                }
 
-               if(timerisset(&c->conn_timeout) && timercmp(&c->conn_timeout, &now, <)) {
+               if(timespec_isset(&c->conn_timeout) && timespec_lt(&c->conn_timeout, &now)) {
                        errno = ETIMEDOUT;
                        c->state = CLOSED;
 
@@ -1959,7 +1995,7 @@ struct timeval utcp_timeout(struct utcp *utcp) {
                        continue;
                }
 
-               if(timerisset(&c->rtrx_timeout) && timercmp(&c->rtrx_timeout, &now, <)) {
+               if(timespec_isset(&c->rtrx_timeout) && timespec_lt(&c->rtrx_timeout, &now)) {
                        debug(c, "retransmitting after timeout\n");
                        retransmit(c);
                }
@@ -1976,18 +2012,18 @@ struct timeval utcp_timeout(struct utcp *utcp) {
                        }
                }
 
-               if(timerisset(&c->conn_timeout) && timercmp(&c->conn_timeout, &next, <)) {
+               if(timespec_isset(&c->conn_timeout) && timespec_lt(&c->conn_timeout, &next)) {
                        next = c->conn_timeout;
                }
 
-               if(timerisset(&c->rtrx_timeout) && timercmp(&c->rtrx_timeout, &next, <)) {
+               if(timespec_isset(&c->rtrx_timeout) && timespec_lt(&c->rtrx_timeout, &next)) {
                        next = c->rtrx_timeout;
                }
        }
 
-       struct timeval diff;
+       struct timespec diff;
 
-       timersub(&next, &now, &diff);
+       timespec_sub(&next, &now, &diff);
 
        return diff;
 }
@@ -2017,6 +2053,12 @@ struct utcp *utcp_init(utcp_accept_t accept, utcp_pre_accept_t pre_accept, utcp_
                return NULL;
        }
 
+       if(!CLOCK_GRANULARITY) {
+               struct timespec res;
+               clock_getres(UTCP_CLOCK, &res);
+               CLOCK_GRANULARITY = res.tv_sec * NSEC_PER_SEC + res.tv_nsec;
+       }
+
        utcp->accept = accept;
        utcp->pre_accept = pre_accept;
        utcp->send = send;
@@ -2091,9 +2133,9 @@ void utcp_reset_timers(struct utcp *utcp) {
                return;
        }
 
-       struct timeval now, then;
+       struct timespec now, then;
 
-       gettimeofday(&now, NULL);
+       clock_gettime(UTCP_CLOCK, &now);
 
        then = now;
 
@@ -2106,11 +2148,11 @@ void utcp_reset_timers(struct utcp *utcp) {
                        continue;
                }
 
-               if(timerisset(&c->rtrx_timeout)) {
+               if(timespec_isset(&c->rtrx_timeout)) {
                        c->rtrx_timeout = now;
                }
 
-               if(timerisset(&c->conn_timeout)) {
+               if(timespec_isset(&c->conn_timeout)) {
                        c->conn_timeout = then;
                }
 
@@ -2251,21 +2293,21 @@ void utcp_expect_data(struct utcp_connection *c, bool expect) {
 
        if(expect) {
                // If we expect data, start the connection timer.
-               if(!timerisset(&c->conn_timeout)) {
-                       gettimeofday(&c->conn_timeout, NULL);
+               if(!timespec_isset(&c->conn_timeout)) {
+                       clock_gettime(UTCP_CLOCK, &c->conn_timeout);
                        c->conn_timeout.tv_sec += c->utcp->timeout;
                }
        } else {
                // If we want to cancel expecting data, only clear the timer when there is no unACKed data.
                if(c->snd.una == c->snd.last) {
-                       timerclear(&c->conn_timeout);
+                       timespec_clear(&c->conn_timeout);
                }
        }
 }
 
 void utcp_offline(struct utcp *utcp, bool offline) {
-       struct timeval now;
-       gettimeofday(&now, NULL);
+       struct timespec now;
+       clock_gettime(UTCP_CLOCK, &now);
 
        for(int i = 0; i < utcp->nconnections; i++) {
                struct utcp_connection *c = utcp->connections[i];
@@ -2277,7 +2319,7 @@ void utcp_offline(struct utcp *utcp, bool offline) {
                utcp_expect_data(c, offline);
 
                if(!offline) {
-                       if(timerisset(&c->rtrx_timeout)) {
+                       if(timespec_isset(&c->rtrx_timeout)) {
                                c->rtrx_timeout = now;
                        }
 
diff --git a/utcp.h b/utcp.h
index 76840facf21c49196af665afb6abf83003f89cca..40cfef6ad49cc372662b160573a2a8001b0e4c66 100644 (file)
--- a/utcp.h
+++ b/utcp.h
@@ -73,7 +73,7 @@ extern ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len);
 extern int utcp_close(struct utcp_connection *connection);
 extern int utcp_abort(struct utcp_connection *connection);
 extern int utcp_shutdown(struct utcp_connection *connection, int how);
-extern struct timeval utcp_timeout(struct utcp *utcp);
+extern struct timespec utcp_timeout(struct utcp *utcp);
 extern void utcp_set_recv_cb(struct utcp_connection *connection, utcp_recv_t recv);
 extern void utcp_set_poll_cb(struct utcp_connection *connection, utcp_poll_t poll);
 extern void utcp_set_accept_cb(struct utcp *utcp, utcp_accept_t accept, utcp_pre_accept_t pre_accept);
index 7d2d07396c7c6b097d39ea39bd23dfca98f43be1..1cf9e8743da561020be4165e533b553778798639 100644 (file)
 
 #define DEFAULT_MTU 1000
 
-#define USEC_PER_SEC 1000000
-#define DEFAULT_USER_TIMEOUT 60 // sec
-#define CLOCK_GRANULARITY 1000 // usec
-#define START_RTO 1000000 // usec
-#define MAX_RTO 3000000 // usec
+static const long USEC_PER_SEC = 1000000;
+static const long NSEC_PER_SEC = 1000000000;
+static const int DEFAULT_USER_TIMEOUT = 60; // sec
+static const long START_RTO = 1 * USEC_PER_SEC; // usec
+static const long MAX_RTO  = 3 * USEC_PER_SEC; // usec
 
 struct hdr {
        uint16_t src; // Source port
@@ -138,9 +138,9 @@ struct utcp_connection {
 
        // Timers
 
-       struct timeval conn_timeout;
-       struct timeval rtrx_timeout;
-       struct timeval rtt_start;
+       struct timespec conn_timeout;
+       struct timespec rtrx_timeout;
+       struct timespec rtt_start;
        uint32_t rtt_seq;
 
        // Buffers
@@ -157,7 +157,7 @@ struct utcp_connection {
 
        // Congestion avoidance state
 
-       struct timeval tlast;
+       struct timespec tlast;
        uint64_t bandwidth;
 };