]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] implement a monotonic internal clock
authorWilly Tarreau <w@1wt.eu>
Sun, 22 Jun 2008 15:18:02 +0000 (17:18 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 22 Jun 2008 15:18:02 +0000 (17:18 +0200)
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.

15 files changed:
include/common/time.h
include/types/session.h
src/cfgparse.c
src/checks.c
src/client.c
src/ev_epoll.c
src/ev_kqueue.c
src/ev_poll.c
src/ev_select.c
src/ev_sepoll.c
src/haproxy.c
src/log.c
src/proto_http.c
src/proxy.c
src/time.c

index 2a4a14264ea75d84ffd8bafea705faa33cee879e..d6155265d6108ca92572e547c42aac3960079834 100644 (file)
@@ -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 @@ REGPRM1 static inline struct timeval *tv_now(struct timeval *tv)
        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;
index 8885b966bc94ffa11b5199ead3c33282b886dabe..b97a54ad45a7fbb35e9245b226f8e0efc145f55d 100644 (file)
@@ -117,7 +117,8 @@ struct session {
        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 */
index 9455fcba97f748eda4e3b46f504dc6ad0dc7c036..13d2a8930573e27d4e38eb0deefe102d4e30b29c 100644 (file)
@@ -2831,7 +2831,7 @@ int readcfgfile(const char *file)
         */
 
        /* 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",
index be26711e3d2809350961d956b2eb3700aeb5e134..eff0df00aa4c3989c80ceaa02f8c3b554d559dc0 100644 (file)
@@ -356,7 +356,7 @@ static int event_srv_chk_w(int fd)
 
                        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);
                        }
 
index 1dd318f5ee78d26469e3346e47c01d0fd0a3c7a8..06a8ce155ae0bd9e427caa78b04f2295fc1ca361 100644 (file)
@@ -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 @@ int event_accept(int fd) {
                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;
index 0ce68b05ec473793d49fbf368059e28e313733a5..e7aea93f36bce752646dec0565d30b6557d7e7bf 100644 (file)
@@ -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 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
 
        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;
index f34cdc3c1bcb33b53fafee5a12990adb0a08b9d0..f22aa5b1fbcc52b777f0efa2c49312eba5a83d6b 100644 (file)
@@ -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 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
                        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;
index 63dce5b26ca9c82bcc7610558546934093ff8833..bfbe999ed03bb7bf4a7dc576521f108ae483b5d1 100644 (file)
@@ -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 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
                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;
index bbbbfe0ccc5f419ca4d1b2ea1cb368d837a559bf..25bd3ec60543cf8943751af3078a492fba746660 100644 (file)
@@ -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 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
                        NULL,
                        tv_isset(exp) ? &delta : NULL);
       
-       tv_now(&now);
+       tv_now_mono(&now, &date);
 
        if (status <= 0)
                return;
index 800ac0bab2d763d013a3667dc500b0557a7c381c..ed2103ced8b89b1b84ea93dbaafdbeb1370bc3b9 100644 (file)
@@ -418,7 +418,7 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
                 * returning now without checking epoll_wait().
                 */
                if (++last_skipped <= 1) {
-                       tv_now(&now);
+                       tv_now_mono(&now, &date);
                        return;
                }
        }
@@ -452,7 +452,7 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
        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;
index 166aaf2e891917092b9c384311bc5aca4f19ca3a..f10e47dc4c6928feed29fae0b72d257fd52a0659 100644 (file)
@@ -415,7 +415,7 @@ void init(int argc, char **argv)
        global.rlimit_memmax = HAPROXY_MEMMAX;
 #endif
 
-       tv_now(&now);
+       tv_now_mono(&now, &date);
        start_date = now;
 
        init_task();
@@ -897,7 +897,7 @@ void run_poll_loop()
 {
        struct timeval next;
 
-       tv_now(&now);
+       tv_now_mono(&now, &date);
        while (1) {
                process_runnable_tasks(&next);
 
index 8888aa88f4819db9f7a79cf0d3beae95ff0508da..d08ad16d6e4bf4b6a248656ed11f9bff35a36432 100644 (file)
--- 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 @@ void Alert(const char *fmt, ...)
        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 @@ void Warning(const char *fmt, ...)
        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 @@ void send_log(struct proxy *p, int level, const char *message, ...)
        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),
index e366ee5638e2c1b91098902ddc682ed3aea0ba83..a5319e02fdc0fdd65a312d34ffee75a1fa4fe716 100644 (file)
@@ -772,7 +772,7 @@ static void http_sess_log(struct session *s)
                          (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 @@ static void http_sess_log(struct session *s)
                 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,
index 402fc88cf0757f637dddbc447c90074610961890..16804f913f53ff79bc0aedd18829ece21ff7d0f7 100644 (file)
@@ -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 @@ void soft_stop(void)
 
        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 @@ void pause_proxies(void)
 
        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 @@ void listen_proxies(void)
        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);
index 661c7e25ad5ffc8dd9c24aabfd1ec357d19811fa..ccb30b2535360f7ff8242537f273e9361b33a6fd 100644 (file)
@@ -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 @@ REGPRM2 int _tv_isgt(const struct timeval *tv1, const struct timeval *tv2)
        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;