]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: threads/time: Store the time deviation in an 64-bits integer
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 31 Oct 2017 08:03:51 +0000 (09:03 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 31 Oct 2017 12:58:33 +0000 (13:58 +0100)
In function tv_update_date, we keep an offset reprenting the time deviation to
adjust the system time. At every call, we check if this offset must be updated
or not. Of course, It must be shared by all threads. It was store in a
timeval. But it cannot be atomically updated. So now, instead, we store it in a
64-bits integer. And in tv_update_date, we convert this integer in a
timeval. Once updated, it is converted back in an integer to be atomically
stored.

To store a tv_offset into an integer, we use 32 bits from tv_sec and 32 bits
tv_usec to avoid shift operations.

src/time.c

index 188ccf5b12416b139ab05e13ea91346cb8306f4a..0975b292df15da778c6dfbdfe4b97fa67927162b 100644 (file)
@@ -15,6 +15,7 @@
 #include <common/config.h>
 #include <common/standard.h>
 #include <common/time.h>
+#include <common/hathreads.h>
 
 THREAD_LOCAL unsigned int   ms_left_scaled;  /* milliseconds left for current second (0..2^32-1) */
 THREAD_LOCAL unsigned int   now_ms;          /* internal date in milliseconds (may wrap) */
@@ -159,22 +160,51 @@ REGPRM2 int _tv_isgt(const struct timeval *tv1, const struct timeval *tv2)
  * value means that we have not expired the timeout). Calling it with (-1,*)
  * sets both <date> and <now> to current date, and calling it with (0,1) simply
  * updates the values.
+ *
+ * tv_offset is used to adjust the current time (date), to have a monotonic time
+ * (now). It must be global and thread-safe. But a timeval cannot be atomically
+ * updated. So instead, we store it in a 64-bits integer (offset). And in
+ * tv_update_date, we convert this integer into a timeval (tv_offset). Once
+ * updated, it is converted back into an integer to be atomically stored.
+ *
+ * To store a tv_offset into an integer, we use 32 bits from tv_sec and 32 bits
+ * tv_usec to avoid shift operations.
  */
+#define OFFSET_TO_TIMEVAL(off, tv)                             \
+       do {                                                    \
+               unsigned long long __i = (off);                 \
+               (tv)->tv_sec  = (__i << 32);                    \
+               (tv)->tv_usec = (__i & 0xFFFFFFFFU);            \
+       } while (0)
+
+#define TIMEVAL_TO_OFFSET(tv, off)                                     \
+       do {                                                            \
+               unsigned long long __i = (((tv).tv_sec & 0xFFFFFFFFULL) << 32) + (unsigned int)(tv).tv_usec; \
+               HA_ATOMIC_STORE((off), __i);                            \
+       } while (0)
+
+#define RESET_OFFSET(off)                      \
+       do {                                    \
+               HA_ATOMIC_STORE((off), 0);      \
+       } while (0)
+
 REGPRM2 void tv_update_date(int max_wait, int interrupted)
 {
-       static struct timeval tv_offset; /* warning: signed offset! */
+       static long long offset = 0;  /* warning: signed offset! */
+       struct timeval tv_offset; /* offset converted into a timeval */
        struct timeval adjusted, deadline;
        unsigned int   curr_sec_ms;     /* millisecond of current second (0..999) */
 
        gettimeofday(&date, NULL);
        if (unlikely(max_wait < 0)) {
-               tv_zero(&tv_offset);
+               RESET_OFFSET(&offset);
                adjusted = date;
                after_poll = date;
                samp_time = idle_time = 0;
                idle_pct = 100;
                goto to_ms;
        }
+       OFFSET_TO_TIMEVAL(offset, &tv_offset);
        __tv_add(&adjusted, &date, &tv_offset);
        if (unlikely(__tv_islt(&adjusted, &now))) {
                goto fixup; /* jump in the past */
@@ -200,6 +230,7 @@ REGPRM2 void tv_update_date(int max_wait, int interrupted)
                tv_offset.tv_usec += 1000000;
                tv_offset.tv_sec--;
        }
+       TIMEVAL_TO_OFFSET(tv_offset, &offset);
  to_ms:
        now = adjusted;
        curr_sec_ms = now.tv_usec / 1000;            /* ms of current second */