MEDIUM: time: make the clock offset global and no per-thread

Since 1.8 for simplicity the time offset used to compensate for time
drift and jumps had been stored per thread. But with a global time,
the complexit has significantly increased.

What this patch does in order to address this is to get back to the
origins of the pre-thread time drift correction, and keep a single
offset between the system's date and the current global date.

The thread first verifies from the before_poll date if the time jumped
backwards or forward, then either fixes it by computing the new most
likely date, or applies the current offset to this latest system date.
In the first case, if the date is out of range, the old one is reused
with the max_wait offset or not depending on the interrupted flag.
Then it compares its date to the global date and updates both so that
both remain monotonic and that the local date always reflects the
latest known global date.

In order to support atomic updates to the offset, it's saved as a
ullong which contains both the tv_sec and tv_usec parts in its high
and low words. Note that a part of the patch comes from the inlining
of the equivalent of tv_add applied to the offset to make sure that
signed ints are permitted (otherwise it depends on how timeval is
defined).

This is significantly more reliable than the previous model as the
global time should move in a much smoother way, and not according
to what thread last updated it, and the thread-local time should
always be very close to the global one.

Note that (at least for debugging) a cheap way to measure processing
lag would consist in measuring the difference between global_now_ms
and now_ms, as long as other threads keep it up-to-date.
diff --git a/src/time.c b/src/time.c
index 5054982..41e4d6f 100644
--- a/src/time.c
+++ b/src/time.c
@@ -27,9 +27,9 @@
 THREAD_LOCAL struct timeval before_poll;     /* system date before calling poll() */
 THREAD_LOCAL struct timeval after_poll;      /* system date after leaving poll() */
 
-static THREAD_LOCAL struct timeval tv_offset;  /* per-thread time ofsset relative to global time */
-volatile unsigned long long global_now;      /* common date between all threads (32:32) */
-volatile unsigned int global_now_ms;         /* common date in milliseconds (may wrap) */
+static unsigned long long now_offset;        /* global offset between system time and global time */
+volatile unsigned long long global_now;      /* common monotonic date between all threads (32:32) */
+volatile unsigned int global_now_ms;         /* common monotonic date in milliseconds (may wrap) */
 
 static THREAD_LOCAL unsigned int iso_time_sec;     /* last iso time value for this thread */
 static THREAD_LOCAL char         iso_time_str[34]; /* ISO time representation of gettimeofday() */
@@ -168,39 +168,62 @@
  * tv_init_process_date() must have been called once first, and
  * tv_init_thread_date() must also have been called once for each thread.
  *
- * An 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) whose 32 MSB
- * contain the signed seconds adjustment and the 32 LSB contain the unsigned
- * microsecond adjustment. We cannot use a timeval for this since it's never
- * clearly specified whether a timeval may hold negative values or not.
+ * An offset is used to adjust the current time (date), to figure a monotonic
+ * local time (now). The offset is not critical, as it is only updated after a
+ * clock jump is detected. From this point all threads will apply it to their
+ * locally measured time, and will then agree around a common monotonic
+ * global_now value that serves to further refine their local time. As it is
+ * not possible to atomically update a timeval, both global_now and the
+ * now_offset values are instead stored as 64-bit integers made of two 32 bit
+ * values for the tv_sec and tv_usec parts. The offset is made of two signed
+ * ints so that the clock can be adjusted in the two directions.
  */
 void tv_update_date(int max_wait, int interrupted)
 {
-	struct timeval adjusted, deadline, tmp_now;
+	struct timeval min_deadline, max_deadline, tmp_now;
 	unsigned int old_now_ms;
 	unsigned long long old_now;
 	unsigned long long new_now;
+	ullong ofs = HA_ATOMIC_LOAD(&now_offset);
+	uint sec_ofs, usec_ofs;
 
 	gettimeofday(&date, NULL);
-	__tv_add(&adjusted, &date, &tv_offset);
 
 	/* compute the minimum and maximum local date we may have reached based
-	 * on our past date and the associated timeout.
+	 * on our past date and the associated timeout. There are three possible
+	 * extremities:
+	 *    - the new date cannot be older than before_poll
+	 *    - if not interrupted, the new date cannot be older than
+	 *      before_poll+max_wait
+	 *    - in any case the new date cannot be newer than
+	 *      before_poll+max_wait+some margin (100ms used here).
+	 * In case of violation, we'll ignore the current date and instead
+	 * restart from the last date we knew.
 	 */
-	_tv_ms_add(&deadline, &now, max_wait + MAX_DELAY_MS);
+	_tv_ms_add(&min_deadline, &before_poll, max_wait);
+	_tv_ms_add(&max_deadline, &before_poll, max_wait + 100);
 
-	if (unlikely(__tv_islt(&adjusted, &now) || __tv_islt(&deadline, &adjusted))) {
-		/* Large jump. If the poll was interrupted, we consider that the
-		 * date has not changed (immediate wake-up), otherwise we add
-		 * the poll time-out to the previous date. The new offset is
-		 * recomputed.
+	if (unlikely(__tv_islt(&date, &before_poll)                    || // big jump backwards
+		     (!interrupted && __tv_islt(&date, &min_deadline)) || // small jump backwards
+		     __tv_islt(&max_deadline, &date))) {                  // big jump forwards
+		if (!interrupted)
+			_tv_ms_add(&now, &now, max_wait);
+	} else {
+		/* The date is still within expectations. Let's apply the
+		 * now_offset to the system date. Note: ofs if made of two
+		 * independent signed ints.
 		 */
-		_tv_ms_add(&adjusted, &now, interrupted ? 0 : max_wait);
+		now.tv_sec  = date.tv_sec  + (int)(ofs >> 32); // note: may be positive or negative
+		now.tv_usec = date.tv_usec + (int)ofs;         // note: may be positive or negative
+		if ((int)now.tv_usec < 0) {
+			now.tv_usec += 1000000;
+			now.tv_sec  -= 1;
+		} else if (now.tv_usec >= 1000000) {
+			now.tv_usec -= 1000000;
+			now.tv_sec  += 1;
+		}
 	}
 
-	now = adjusted;
-
 	/* now that we have bounded the local time, let's check if it's
 	 * realistic regarding the global date, which only moves forward,
 	 * otherwise catch up.
@@ -230,23 +253,26 @@
 	} while (!_HA_ATOMIC_CAS(&global_now, &old_now, new_now) ||
 		 !_HA_ATOMIC_CAS(&global_now_ms, &old_now_ms, now_ms));
 
-	/* the new global date when we looked was old_now, and the new one is
-	 * new_now == now. We can recompute our local offset.
+	/* <now> and <now_ms> are now updated to the last value of global_now
+	 * and global_now_ms, which were also monotonically updated. We can
+	 * compute the latest offset, we don't care who writes it last, the
+	 * variations will not break the monotonic property.
 	 */
-	tv_offset.tv_sec  = now.tv_sec  - date.tv_sec;
-	tv_offset.tv_usec = now.tv_usec - date.tv_usec;
-	if (tv_offset.tv_usec < 0) {
-		tv_offset.tv_usec += 1000000;
-		tv_offset.tv_sec--;
-	}
 
-	return;
+	sec_ofs  = now.tv_sec  - date.tv_sec;
+	usec_ofs = now.tv_usec - date.tv_usec;
+	if ((int)usec_ofs < 0) {
+		usec_ofs += 1000000;
+		sec_ofs  -= 1;
+	}
+	ofs = ((ullong)sec_ofs << 32) + usec_ofs;
+	HA_ATOMIC_STORE(&now_offset, ofs);
 }
 
 /* must be called once at boot to initialize some global variables */
 void tv_init_process_date()
 {
-	tv_zero(&tv_offset);
+	now_offset = 0;
 	gettimeofday(&date, NULL);
 	now = after_poll = before_poll = date;
 	global_now = ((ullong)date.tv_sec << 32) + (uint)date.tv_usec;
@@ -269,14 +295,6 @@
 	old_now = _HA_ATOMIC_LOAD(&global_now);
 	now.tv_sec = old_now >> 32;
 	now.tv_usec = (uint)old_now;
-
-	tv_offset.tv_sec  = now.tv_sec  - date.tv_sec;
-	tv_offset.tv_usec = now.tv_usec - date.tv_usec;
-	if (tv_offset.tv_usec < 0) {
-		tv_offset.tv_usec += 1000000;
-		tv_offset.tv_sec--;
-	}
-
 	samp_time = idle_time = 0;
 	ti->idle_pct = 100;
 	tv_update_date(0, 1);