diff --git a/include/common/time.h b/include/common/time.h index 2a4a14264..d6155265d 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 @@ REGPRM1 static inline struct timeval *tv_now(struct timeval *tv) return tv; } +/* tv_now_mono: sets to the current time (wall clock), 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 8885b966b..b97a54ad4 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -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 */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 9455fcba9..13d2a8930 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -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 line. Nothing to do !\n", diff --git a/src/checks.c b/src/checks.c index be26711e3..eff0df00a 100644 --- a/src/checks.c +++ b/src/checks.c @@ -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); } diff --git a/src/client.c b/src/client.c index 1dd318f5e..06a8ce155 100644 --- a/src/client.c +++ b/src/client.c @@ -1,7 +1,7 @@ /* * Client-side variables and functions. * - * Copyright 2000-2007 Willy Tarreau + * Copyright 2000-2008 Willy Tarreau * * 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; diff --git a/src/ev_epoll.c b/src/ev_epoll.c index 0ce68b05e..e7aea93f3 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 + * Copyright 2000-2008 Willy Tarreau * * 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; diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c index f34cdc3c1..f22aa5b1f 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 + * Copyright 2000-2008 Willy Tarreau * * 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; diff --git a/src/ev_poll.c b/src/ev_poll.c index 63dce5b26..bfbe999ed 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 + * Copyright 2000-2008 Willy Tarreau * * 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; diff --git a/src/ev_select.c b/src/ev_select.c index bbbbfe0cc..25bd3ec60 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 + * Copyright 2000-2008 Willy Tarreau * * 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; diff --git a/src/ev_sepoll.c b/src/ev_sepoll.c index 800ac0bab..ed2103ced 100644 --- a/src/ev_sepoll.c +++ b/src/ev_sepoll.c @@ -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; diff --git a/src/haproxy.c b/src/haproxy.c index 166aaf2e8..f10e47dc4 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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); diff --git a/src/log.c b/src/log.c index 8888aa88f..d08ad16d6 100644 --- a/src/log.c +++ b/src/log.c @@ -1,7 +1,7 @@ /* * General logging functions. * - * Copyright 2000-2006 Willy Tarreau + * Copyright 2000-2008 Willy Tarreau * * 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), diff --git a/src/proto_http.c b/src/proto_http.c index e366ee563..a5319e02f 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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, diff --git a/src/proxy.c b/src/proxy.c index 402fc88cf..16804f913 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -1,7 +1,7 @@ /* * Proxy variables and functions. * - * Copyright 2000-2007 Willy Tarreau + * Copyright 2000-2008 Willy Tarreau * * 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); diff --git a/src/time.c b/src/time.c index 661c7e25a..ccb30b253 100644 --- a/src/time.c +++ b/src/time.c @@ -1,7 +1,7 @@ /* * Time calculation functions. * - * Copyright 2000-2007 Willy Tarreau + * Copyright 2000-2008 Willy Tarreau * * 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 #include -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 to the current time (wall clock), 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;