From ebea9e7e0bdff6c488928253cea82cbb22edd3cc Mon Sep 17 00:00:00 2001 From: hniksic Date: Sat, 13 Sep 2003 16:12:45 -0700 Subject: [PATCH] [svn] Fixed a long-standing bug in the timer code that would cause Wget to crash when the system time was set back during a Wget run. Message-ID: --- src/ChangeLog | 14 ++++++ src/utils.c | 115 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index cd0742ae..d094f5c0 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,17 @@ +2003-09-14 Hrvoje Niksic + + * utils.c (wtimer_sys_set): Extracted the code that sets the + current time here. + (wtimer_reset): Call it. + (wtimer_sys_diff): Extracted the code that calculates the + difference between two system times here. + (wtimer_elapsed): Call it. + (wtimer_elapsed): Don't return a value smaller than the previous + one, which could previously happen when system time is set back. + Instead, reset start time to current time and note the elapsed + offset for future calculations. The returned times are now + guaranteed to be monotonically nondecreasing. + 2003-09-10 Hrvoje Niksic * host.c (lookup_host): Print the result of the DNS lookup. diff --git a/src/utils.c b/src/utils.c index a5b6d9ae..a67ec8de 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1532,19 +1532,30 @@ number_to_string (char *buffer, long number) # endif #endif /* not WINDOWS */ -struct wget_timer { #ifdef TIMER_GETTIMEOFDAY - long secs; - long usecs; +typedef struct timeval wget_sys_time; #endif #ifdef TIMER_TIME - time_t secs; +typedef time_t wget_sys_time; #endif #ifdef TIMER_WINDOWS - ULARGE_INTEGER wintime; +typedef ULARGE_INTEGER wget_sys_time; #endif + +struct wget_timer { + /* The starting point in time which, subtracted from the current + time, yields elapsed time. */ + wget_sys_time start; + + /* The most recent elapsed time, calculated by wtimer_elapsed(). + Measured in milliseconds. */ + long elapsed_last; + + /* Approximately, the time elapsed between the true start of the + measurement and the time represented by START. */ + long elapsed_pre_start; }; /* Allocate a timer. It is not legal to do anything with a freshly @@ -1577,22 +1588,17 @@ wtimer_delete (struct wget_timer *wt) xfree (wt); } -/* Reset timer WT. This establishes the starting point from which - wtimer_elapsed() will return the number of elapsed - milliseconds. It is allowed to reset a previously used timer. */ +/* Store system time to WST. */ -void -wtimer_reset (struct wget_timer *wt) +static void +wtimer_sys_set (wget_sys_time *wst) { #ifdef TIMER_GETTIMEOFDAY - struct timeval t; - gettimeofday (&t, NULL); - wt->secs = t.tv_sec; - wt->usecs = t.tv_usec; + gettimeofday (wst, NULL); #endif #ifdef TIMER_TIME - wt->secs = time (NULL); + time (wst); #endif #ifdef TIMER_WINDOWS @@ -1600,8 +1606,38 @@ wtimer_reset (struct wget_timer *wt) SYSTEMTIME st; GetSystemTime (&st); SystemTimeToFileTime (&st, &ft); - wt->wintime.HighPart = ft.dwHighDateTime; - wt->wintime.LowPart = ft.dwLowDateTime; + wst->HighPart = ft.dwHighDateTime; + wst->LowPart = ft.dwLowDateTime; +#endif +} + +/* Reset timer WT. This establishes the starting point from which + wtimer_elapsed() will return the number of elapsed + milliseconds. It is allowed to reset a previously used timer. */ + +void +wtimer_reset (struct wget_timer *wt) +{ + /* Set the start time to the current time. */ + wtimer_sys_set (&wt->start); + wt->elapsed_last = 0; + wt->elapsed_pre_start = 0; +} + +static long +wtimer_sys_diff (wget_sys_time *wst1, wget_sys_time *wst2) +{ +#ifdef TIMER_GETTIMEOFDAY + return ((wst1->tv_sec - wst2->tv_sec) * 1000 + + (wst1->tv_usec - wst2->tv_usec) / 1000); +#endif + +#ifdef TIMER_TIME + return 1000 * (*wst1 - *wst2); +#endif + +#ifdef WINDOWS + return (long)(wst1->QuadPart - wst2->QuadPart) / 10000; #endif } @@ -1612,27 +1648,34 @@ wtimer_reset (struct wget_timer *wt) long wtimer_elapsed (struct wget_timer *wt) { -#ifdef TIMER_GETTIMEOFDAY - struct timeval t; - gettimeofday (&t, NULL); - return (t.tv_sec - wt->secs) * 1000 + (t.tv_usec - wt->usecs) / 1000; -#endif + wget_sys_time now; + long elapsed; -#ifdef TIMER_TIME - time_t now = time (NULL); - return 1000 * (now - wt->secs); -#endif + wtimer_sys_set (&now); + elapsed = wt->elapsed_pre_start + wtimer_sys_diff (&now, &wt->start); -#ifdef WINDOWS - FILETIME ft; - SYSTEMTIME st; - ULARGE_INTEGER uli; - GetSystemTime (&st); - SystemTimeToFileTime (&st, &ft); - uli.HighPart = ft.dwHighDateTime; - uli.LowPart = ft.dwLowDateTime; - return (long)((uli.QuadPart - wt->wintime.QuadPart) / 10000); -#endif + /* Ideally we'd just return the difference between NOW and + wt->start. However, the system timer can be set back, and we + could return a value smaller than when we were last called, even + a negative value. Both of these would confuse the callers, which + expect us to return monotonically nondecreasing values. + + Therefore: if ELAPSED is smaller than its previous known value, + we reset wt->start to the current time and effectively start + measuring from this point. But since we don't want the elapsed + value to start from zero, we set elapsed_pre_start to the last + elapsed time and increment all future calculations by that + amount. */ + + if (elapsed < wt->elapsed_last) + { + wt->start = now; + wt->elapsed_pre_start = wt->elapsed_last; + elapsed = wt->elapsed_last; + } + + wt->elapsed_last = elapsed; + return elapsed; } /* Return the assessed granularity of the timer implementation. This