[MEDIUM] implement a monotonic internal clock

If the system date is set backwards while haproxy is running,
some scheduled events are delayed by the amount of time the
clock went backwards. This is particularly problematic on
systems where the date is set at boot, because it seldom
happens that health-checks do not get sent for a few hours.

Before switching to use clock_gettime() on systems which
provide it, we can at least ensure that the clock is not
going backwards and maintain two clocks : the "date" which
represents what the user wants to see (mostly for logs),
and an internal date stored in "now", used for scheduled
events.
diff --git a/include/common/time.h b/include/common/time.h
index 2a4a142..d615526 100644
--- a/include/common/time.h
+++ b/include/common/time.h
@@ -2,7 +2,7 @@
   include/common/time.h
   Time calculation functions and macros.
 
-  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -49,7 +49,8 @@
 #define MINTIME(old, new)	(((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
 #define SETNOW(a)		(*a=now)
 
-extern struct timeval now;              /* the current date at any moment */
+extern struct timeval now;              /* internal date is a monotonic function of real clock */
+extern struct timeval date;             /* the real current date */
 extern struct timeval start_date;       /* the process's start date */
 
 
@@ -83,13 +84,19 @@
 	return tv;
 }
 
+/* tv_now_mono: sets <date> to the current time (wall clock), <mono> to a value
+ * following a monotonic function, and applies any required correction if the
+ * time goes backwards. Note that while we could improve it a bit by checking
+ * that the new date is not too far in the future, it is not much necessary to
+ * do so. 
+ */
+REGPRM2 struct timeval *tv_now_mono(struct timeval *mono, struct timeval *wall);
+
 /*
  * sets a struct timeval to its highest value so that it can never happen
  * note that only tv_usec is necessary to detect it since a tv_usec > 999999
  * is normally not possible.
- *
  */
-
 REGPRM1 static inline struct timeval *tv_eternity(struct timeval *tv)
 {
 	tv->tv_sec = tv->tv_usec = TV_ETERNITY;
diff --git a/include/types/session.h b/include/types/session.h
index 8885b96..b97a54a 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -117,7 +117,8 @@
 	struct http_txn txn;			/* current HTTP transaction being processed. Should become a list. */
 	struct {
 		int logwait;			/* log fields waiting to be collected : LW_* */
-		struct timeval tv_accept;	/* date of the accept() (beginning of the session) */
+		struct timeval accept_date;	/* date of the accept() in user date */
+		struct timeval tv_accept;	/* date of the accept() in internal date (monotonic) */
 		struct timeval tv_request;	/* date the request arrives, {0,0} if never occurs */
 		long  t_queue;			/* delay before the session gets out of the connect queue, -1 if never occurs */
 		long  t_connect;		/* delay before the connect() to the server succeeds, -1 if never occurs */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 9455fcb..13d2a89 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2831,7 +2831,7 @@
 	 */
 
 	/* will be needed further to delay some tasks */
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	if ((curproxy = proxy) == NULL) {
 		Alert("parsing %s : no <listen> line. Nothing to do !\n",
diff --git a/src/checks.c b/src/checks.c
index be26711..eff0df0 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -356,7 +356,7 @@
 
 			if (s->proxy->options & PR_O_SSL3_CHK) {
 				/* SSL requires that we put Unix time in the request */
-				int gmt_time = htonl(now.tv_sec);
+				int gmt_time = htonl(date.tv_sec);
 				memcpy(s->proxy->check_req + 11, &gmt_time, 4);
 			}
 
diff --git a/src/client.c b/src/client.c
index 1dd318f..06a8ce1 100644
--- a/src/client.c
+++ b/src/client.c
@@ -1,7 +1,7 @@
 /*
  * Client-side variables and functions.
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -201,7 +201,8 @@
 		else
 			s->logs.logwait = p->to_log;
 
-		s->logs.tv_accept = now;
+		s->logs.accept_date = date; /* user-visible date for logging */
+		s->logs.tv_accept = now;  /* corrected date for internal use */
 		tv_zero(&s->logs.tv_request);
 		s->logs.t_queue = -1;
 		s->logs.t_connect = -1;
diff --git a/src/ev_epoll.c b/src/ev_epoll.c
index 0ce68b0..e7aea93 100644
--- a/src/ev_epoll.c
+++ b/src/ev_epoll.c
@@ -1,7 +1,7 @@
 /*
  * FD polling functions for linux epoll()
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -243,7 +243,7 @@
 
 	fd = MIN(maxfd, global.tune.maxpollevents);
 	status = epoll_wait(epoll_fd, epoll_events, fd, wait_time);
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	for (count = 0; count < status; count++) {
 		fd = epoll_events[count].data.fd;
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index f34cdc3..f22aa5b 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -1,7 +1,7 @@
 /*
  * FD polling functions for FreeBSD kqueue()
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -130,7 +130,7 @@
 			kev,       // struct kevent *eventlist
 			fd,        // int nevents
 			to_ptr);   // const struct timespec *timeout
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	for (count = 0; count < status; count++) {
 		fd = kev[count].ident;
diff --git a/src/ev_poll.c b/src/ev_poll.c
index 63dce5b..bfbe999 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -1,7 +1,7 @@
 /*
  * FD polling functions for generic poll()
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -134,7 +134,7 @@
 		wait_time = __tv_ms_elapsed(&now, exp) + 1;
 
 	status = poll(poll_events, nbfd, wait_time);
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	for (count = 0; status > 0 && count < nbfd; count++) {
 		fd = poll_events[count].fd;
diff --git a/src/ev_select.c b/src/ev_select.c
index bbbbfe0..25bd3ec 100644
--- a/src/ev_select.c
+++ b/src/ev_select.c
@@ -1,7 +1,7 @@
 /*
  * FD polling functions for generic select()
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -124,7 +124,7 @@
 			NULL,
 			tv_isset(exp) ? &delta : NULL);
       
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	if (status <= 0)
 		return;
diff --git a/src/ev_sepoll.c b/src/ev_sepoll.c
index 800ac0b..ed2103c 100644
--- a/src/ev_sepoll.c
+++ b/src/ev_sepoll.c
@@ -418,7 +418,7 @@
 		 * returning now without checking epoll_wait().
 		 */
 		if (++last_skipped <= 1) {
-			tv_now(&now);
+			tv_now_mono(&now, &date);
 			return;
 		}
 	}
@@ -452,7 +452,7 @@
 	spec_processed = 0;
 	status = epoll_wait(epoll_fd, epoll_events, fd, wait_time);
 
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 
 	for (count = 0; count < status; count++) {
 		int e = epoll_events[count].events;
diff --git a/src/haproxy.c b/src/haproxy.c
index 166aaf2..f10e47d 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -415,7 +415,7 @@
 	global.rlimit_memmax = HAPROXY_MEMMAX;
 #endif
 
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 	start_date = now;
 
 	init_task();
@@ -897,7 +897,7 @@
 {
 	struct timeval next;
 
-	tv_now(&now);
+	tv_now_mono(&now, &date);
 	while (1) {
 		process_runnable_tasks(&next);
 
diff --git a/src/log.c b/src/log.c
index 8888aa8..d08ad16 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1,7 +1,7 @@
 /*
  * General logging functions.
  *
- * Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -70,7 +70,7 @@
 	if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
 		va_start(argp, fmt);
 
-		get_localtime(now.tv_sec, &tm);
+		get_localtime(date.tv_sec, &tm);
 		fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
 			tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
 		vfprintf(stderr, fmt, argp);
@@ -91,7 +91,7 @@
 	if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
 		va_start(argp, fmt);
 
-		get_localtime(now.tv_sec, &tm);
+		get_localtime(date.tv_sec, &tm);
 		fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
 			tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
 		vfprintf(stderr, fmt, argp);
@@ -185,11 +185,11 @@
 	if (level < 0 || progname == NULL || message == NULL)
 		return;
 
-	if (now.tv_sec != tvsec || dataptr == NULL) {
+	if (unlikely(date.tv_sec != tvsec || dataptr == NULL)) {
 		/* this string is rebuild only once a second */
 		struct tm tm;
 
-		tvsec = now.tv_sec;
+		tvsec = date.tv_sec;
 		get_localtime(tvsec, &tm);
 
 		hdr_len = snprintf(logmsg, sizeof(logmsg),
diff --git a/src/proto_http.c b/src/proto_http.c
index e366ee5..a5319e0 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -772,7 +772,7 @@
 			  (const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
 			  pn, sizeof(pn));
 
-	get_localtime(s->logs.tv_accept.tv_sec, &tm);
+	get_localtime(s->logs.accept_date.tv_sec, &tm);
 
 	/* FIXME: let's limit ourselves to frontend logging for now. */
 	tolog = fe->to_log;
@@ -835,7 +835,7 @@
 		 ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
 		 ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
 		 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
-		 tm.tm_hour, tm.tm_min, tm.tm_sec, s->logs.tv_accept.tv_usec/1000,
+		 tm.tm_hour, tm.tm_min, tm.tm_sec, s->logs.accept_date.tv_usec/1000,
 		 fe->id, be->id, svid,
 		 t_request,
 		 (s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
diff --git a/src/proxy.c b/src/proxy.c
index 402fc88..16804f9 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1,7 +1,7 @@
 /*
  * Proxy variables and functions.
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -385,7 +385,7 @@
 
 	stopping = 1;
 	p = proxy;
-	tv_now(&now); /* else, the old time before select will be used */
+	tv_now_mono(&now, &date); /* else, the old time before select will be used */
 	while (p) {
 		if (p->state != PR_STSTOPPED) {
 			Warning("Stopping proxy %s in %d ms.\n", p->id, p->grace);
@@ -434,7 +434,7 @@
 
 	err = 0;
 	p = proxy;
-	tv_now(&now); /* else, the old time before select will be used */
+	tv_now_mono(&now, &date); /* else, the old time before select will be used */
 	while (p) {
 		if (p->state != PR_STERROR &&
 		    p->state != PR_STSTOPPED &&
@@ -469,7 +469,7 @@
 	struct listener *l;
 
 	p = proxy;
-	tv_now(&now); /* else, the old time before select will be used */
+	tv_now_mono(&now, &date); /* else, the old time before select will be used */
 	while (p) {
 		if (p->state == PR_STPAUSED) {
 			Warning("Enabling proxy %s.\n", p->id);
diff --git a/src/time.c b/src/time.c
index 661c7e2..ccb30b2 100644
--- a/src/time.c
+++ b/src/time.c
@@ -1,7 +1,7 @@
 /*
  * Time calculation functions.
  *
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -16,7 +16,8 @@
 #include <common/standard.h>
 #include <common/time.h>
 
-struct timeval now;             /* the current date at any moment */
+struct timeval now;             /* internal date is a monotonic function of real clock */
+struct timeval date;            /* the real current date */
 struct timeval start_date;      /* the process's start date */
 
 /*
@@ -142,6 +143,27 @@
 	return __tv_isgt(tv1, tv2);
 }
 
+/* tv_now_mono: sets <date> to the current time (wall clock), <mono> to a value
+ * following a monotonic function, and applies any required correction if the
+ * time goes backwards. Note that while we could improve it a bit by checking
+ * that the new date is not too far in the future, it is not much necessary to
+ * do so. 
+ */
+REGPRM2 struct timeval *tv_now_mono(struct timeval *mono, struct timeval *wall)
+{
+	static struct timeval tv_offset;
+	struct timeval adjusted;
+
+	gettimeofday(wall, NULL);
+	__tv_add(&adjusted, wall, &tv_offset);
+	if (unlikely(__tv_islt(&adjusted, mono))) {
+		__tv_remain(wall, mono, &tv_offset);
+		return mono;
+	}
+	*mono = adjusted;
+	return mono;
+}
+
 char *human_time(int t, short hz_div) {
 	static char rv[sizeof("24855d23h")+1];	// longest of "23h59m" and "59m59s"
 	char *p = rv;