From 6da4142b3b5a03651bd4e7af2351cd32743bb192 Mon Sep 17 00:00:00 2001 From: hniksic Date: Sun, 20 Mar 2005 02:41:46 -0800 Subject: [PATCH] [svn] Use high-resolution timers on Windows. --- src/ChangeLog | 31 ++++++++++ src/cookies.c | 13 +++++ src/gen-md5.c | 34 +++++++++++ src/host.c | 7 +-- src/mswindows.c | 22 ++++--- src/mswindows.h | 58 ++++++++---------- src/utils.c | 152 ++++++++++++++++++++++-------------------------- src/utils.h | 1 - 8 files changed, 182 insertions(+), 136 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 2e996aa0..a2411375 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,34 @@ +2005-03-12 Hrvoje Niksic + + * utils.c (debug_test_md5): Moved to gen-md5.c. + + * mswindows.h: Don't declare inet_ntop, since we don't use it. + + * mswindows.h: For consistency, also wrap closesocket, it being + a Winsock call. + + * mswindows.h: Don't declare sleep and usleep; we're defining + xsleep now. + + * mswindows.h (mkdir): Don't special-case Borland C, _mkdir + works there as well. + + * host.c: Don't include winsock header files; the correct ones + are already included by mswindows.h. + + * mswindows.c (xsleep): Round toward the nearest millisecond + in an attempt to avoid average short sleeps. + + * utils.c (wtimer_granularity): Report correct values for + Windows timers and for high-resolution timers. + + * utils.c (wtimer_initialize_once): New function, called to + initialize the timer frequency. + + * utils.c: Replace the use of GetSystemTime with high-resolution + counters under Windows. When high-resolution counters are + unavailable, use GetTickCount(). + 2005-03-15 Hrvoje Niksic * retr.c (fd_read_body): Undo the 2004-11-18 change. Instead, diff --git a/src/cookies.c b/src/cookies.c index fe9761fd..d0d9e610 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -898,6 +898,19 @@ cookie_handle_set_cookie (struct cookie_jar *jar, /* Support for sending out cookies in HTTP requests, based on previously stored cookies. Entry point is `build_cookies_request'. */ + +/* Return a count of how many times CHR occurs in STRING. */ + +static int +count_char (const char *string, char chr) +{ + const char *p; + int count = 0; + for (p = string; *p; p++) + if (*p == chr) + ++count; + return count; +} /* Find the cookie chains whose domains match HOST and store them to DEST. diff --git a/src/gen-md5.c b/src/gen-md5.c index 250d1c4f..716e4c22 100644 --- a/src/gen-md5.c +++ b/src/gen-md5.c @@ -116,3 +116,37 @@ gen_md5_finish (gen_md5_context *ctx, unsigned char *result) MD5_Final (result, ctx_imp); #endif } + +#if 0 +/* A debugging function for checking whether an MD5 library works. */ + +#include "gen-md5.h" + +char * +debug_test_md5 (char *buf) +{ + unsigned char raw[16]; + static char res[33]; + unsigned char *p1; + char *p2; + int cnt; + ALLOCA_MD5_CONTEXT (ctx); + + gen_md5_init (ctx); + gen_md5_update ((unsigned char *)buf, strlen (buf), ctx); + gen_md5_finish (ctx, raw); + + p1 = raw; + p2 = res; + cnt = 16; + while (cnt--) + { + *p2++ = XNUM_TO_digit (*p1 >> 4); + *p2++ = XNUM_TO_digit (*p1 & 0xf); + ++p1; + } + *p2 = '\0'; + + return res; +} +#endif diff --git a/src/host.c b/src/host.c index b550d786..272ed4f1 100644 --- a/src/host.c +++ b/src/host.c @@ -43,10 +43,7 @@ so, delete this exception statement from your version. */ #include #include -#ifdef WINDOWS -# include -# define SET_H_ERRNO(err) WSASetLastError (err) -#else +#ifndef WINDOWS # include # include # ifndef __BEOS__ @@ -54,6 +51,8 @@ so, delete this exception statement from your version. */ # endif # include # define SET_H_ERRNO(err) ((void)(h_errno = (err))) +#else /* WINDOWS */ +# define SET_H_ERRNO(err) WSASetLastError (err) #endif /* WINDOWS */ #ifndef NO_ADDRESS diff --git a/src/mswindows.c b/src/mswindows.c index 15518a00..a16b4b81 100644 --- a/src/mswindows.c +++ b/src/mswindows.c @@ -84,7 +84,7 @@ xsleep (double seconds) } usleep (seconds * 1000000L); #else /* not HAVE_USLEEP */ - SleepEx (seconds * 1000, FALSE); + SleepEx ((DWORD) (seconds * 1000 + .5), FALSE); #endif /* not HAVE_USLEEP */ } @@ -589,12 +589,9 @@ set_sleep_mode (void) void ws_startup (void) { - WORD requested; WSADATA data; - int err; - - requested = MAKEWORD (1, 1); - err = WSAStartup (requested, &data); + WORD requested = MAKEWORD (1, 1); + int err = WSAStartup (requested, &data); if (err != 0) { fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"), @@ -741,16 +738,16 @@ run_with_timeout (double seconds, void (*fun) (void *), void *arg) return rc; } -/* Wget expects network calls such as bind, connect, etc., to set errno. - To achieve that, we place Winsock calls in wrapper functions that, in - case of error, sets errno to the value of GetLastError(). In addition, - we provide a wrapper around strerror, which recognizes Winsock errors - and prints the appropriate error message. */ +/* Wget expects network calls such as connect, recv, send, etc., to set + errno on failure. To achieve that, Winsock calls are wrapped with code + that, in case of error, sets errno to the value of WSAGetLastError(). + In addition, we provide a wrapper around strerror, which recognizes + Winsock errors and prints the appropriate error message. */ /* Define a macro that creates a function definition that wraps FUN into a function that sets errno the way the rest of the code expects. */ -#define WRAP(fun, decl, call) int wrap_##fun decl { \ +#define WRAP(fun, decl, call) int wrapped_##fun decl { \ int retval = fun call; \ if (retval < 0) \ errno = WSAGetLastError (); \ @@ -768,6 +765,7 @@ WRAP (getsockname, (int s, struct sockaddr *n, int *nlen), (s, n, nlen)) WRAP (getpeername, (int s, struct sockaddr *n, int *nlen), (s, n, nlen)) WRAP (setsockopt, (int s, int level, int opt, const void *val, int len), (s, level, opt, val, len)) +WRAP (closesocket, (int s), (s)) /* Return the text of the error message for Winsock error WSERR. */ diff --git a/src/mswindows.h b/src/mswindows.h index 8691d94c..6750934b 100644 --- a/src/mswindows.h +++ b/src/mswindows.h @@ -124,38 +124,36 @@ __int64 str_to_int64 (const char *, char **, int); #include /* Windows compilers accept only one arg to mkdir. */ -#ifndef __BORLANDC__ -# define mkdir(a, b) _mkdir(a) -#else /* __BORLANDC__ */ -# define mkdir(a, b) mkdir(a) -#endif /* __BORLANDC__ */ +#define mkdir(a, b) _mkdir(a) #ifndef INHIBIT_WRAP /* Winsock functions don't set errno, so we provide wrappers that do. */ -#define socket wrap_socket -#define bind wrap_bind -#define connect wrap_connect -#define recv wrap_recv -#define send wrap_send -#define select wrap_select -#define getsockname wrap_getsockname -#define getpeername wrap_getpeername -#define setsockopt wrap_setsockopt +#define socket wrapped_socket +#define bind wrapped_bind +#define connect wrapped_connect +#define recv wrapped_recv +#define send wrapped_send +#define select wrapped_select +#define getsockname wrapped_getsockname +#define getpeername wrapped_getpeername +#define setsockopt wrapped_setsockopt +#define closesocket wrapped_closesocket #endif /* not INHIBIT_WRAP */ -int wrap_socket (int, int, int); -int wrap_bind (int, struct sockaddr *, int); -int wrap_connect (int, const struct sockaddr *, int); -int wrap_recv (int, void *, int, int); -int wrap_send (int, const void *, int, int); -int wrap_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *); -int wrap_getsockname (int, struct sockaddr *, int *); -int wrap_getpeername (int, struct sockaddr *, int *); -int wrap_setsockopt (int, int, int, const void *, int); +int wrapped_socket (int, int, int); +int wrapped_bind (int, struct sockaddr *, int); +int wrapped_connect (int, const struct sockaddr *, int); +int wrapped_recv (int, void *, int, int); +int wrapped_send (int, const void *, int, int); +int wrapped_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *); +int wrapped_getsockname (int, struct sockaddr *, int *); +int wrapped_getpeername (int, struct sockaddr *, int *); +int wrapped_setsockopt (int, int, int, const void *, int); +int wrapped_closesocket (int); /* Finally, provide a private version of strerror that does the right thing with Winsock errors. */ @@ -204,13 +202,6 @@ const char *windows_strerror (int); /* Public functions. */ -#ifndef HAVE_SLEEP -unsigned int sleep (unsigned); -#endif -#ifndef HAVE_USLEEP -int usleep (unsigned long); -#endif - void ws_startup (void); void ws_changetitle (const char *); void ws_percenttitle (double); @@ -219,11 +210,8 @@ void windows_main (int *, char **, char **); /* Things needed for IPv6; missing in . */ #ifdef ENABLE_IPV6 -# ifndef HAVE_NTOP - extern const char *inet_ntop (int af, const void *src, char *dst, size_t size); -# endif -# ifndef HAVE_PTON - extern int inet_pton (int af, const char *src, void *dst); +# ifndef HAVE_INET_NTOP +extern const char *inet_ntop (int af, const void *src, char *dst, size_t size); # endif #endif /* ENABLE_IPV6 */ diff --git a/src/utils.c b/src/utils.c index e5098be3..2319947a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,5 @@ /* Various utility functions. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -120,19 +120,6 @@ xstrdup_lower (const char *s) return copy; } -/* Return a count of how many times CHR occurs in STRING. */ - -int -count_char (const char *string, char chr) -{ - const char *p; - int count = 0; - for (p = string; *p; p++) - if (*p == chr) - ++count; - return count; -} - /* Copy the string formed by two pointers (one on the beginning, other on the char after the last char) to a new, malloc-ed location. 0-terminate it. */ @@ -1564,11 +1551,7 @@ number_to_static_string (wgint number) only one of the above constants will be defined. Virtually all modern Unix systems will define TIMER_GETTIMEOFDAY; Windows will use TIMER_WINDOWS. TIMER_TIME is a catch-all method for - non-Windows systems without gettimeofday. - - #### Perhaps we should also support ftime(), which exists on old - BSD 4.2-influenced systems? (It also existed under MS DOS Borland - C, if memory serves me.) */ + non-Windows systems without gettimeofday. */ #ifdef WINDOWS # define TIMER_WINDOWS @@ -1589,7 +1572,10 @@ typedef time_t wget_sys_time; #endif #ifdef TIMER_WINDOWS -typedef ULARGE_INTEGER wget_sys_time; +typedef union { + DWORD lores; /* In case GetTickCount is used */ + LARGE_INTEGER hires; /* In case high-resolution timer is used */ +} wget_sys_time; #endif struct wget_timer { @@ -1609,6 +1595,39 @@ struct wget_timer { double elapsed_pre_start; }; +#ifdef TIMER_WINDOWS + +/* Whether high-resolution timers are used. Set by wtimer_initialize_once + the first time wtimer_allocate is called. */ +static int using_hires_timers; + +/* Frequency of high-resolution timers -- number of updates per + millisecond. Calculated the first time wtimer_allocate is called + provided that high-resolution timers are available. */ +static double hires_millisec_freq; + +/* The first time a timer is created, determine whether to use + high-resolution timers. */ + +static void +wtimer_initialize_once (void) +{ + static int init_done; + if (!init_done) + { + LARGE_INTEGER freq; + init_done = 1; + freq.QuadPart = 0; + QueryPerformanceFrequency (&freq); + if (freq.QuadPart != 0) + { + using_hires_timers = 1; + hires_millisec_freq = (double) freq.QuadPart / 1000.0; + } + } +} +#endif /* TIMER_WINDOWS */ + /* Allocate a timer. Calling wtimer_read on the timer will return zero. It is not legal to call wtimer_update with a freshly allocated timer -- use wtimer_reset first. */ @@ -1618,6 +1637,11 @@ wtimer_allocate (void) { struct wget_timer *wt = xnew (struct wget_timer); xzero (*wt); + +#ifdef TIMER_WINDOWS + wtimer_initialize_once (); +#endif + return wt; } @@ -1654,32 +1678,24 @@ wtimer_sys_set (wget_sys_time *wst) #endif #ifdef TIMER_WINDOWS - /* We use GetSystemTime to get the elapsed time. MSDN warns that - system clock adjustments can skew the output of GetSystemTime - when used as a timer and gives preference to GetTickCount and - high-resolution timers. But GetTickCount can overflow, and hires - timers are typically used for profiling, not for regular time - measurement. Since we handle clock skew anyway, we just use - GetSystemTime. */ - FILETIME ft; - SYSTEMTIME st; - GetSystemTime (&st); - - /* As recommended by MSDN, we convert SYSTEMTIME to FILETIME, copy - FILETIME to ULARGE_INTEGER, and use regular 64-bit integer - arithmetic on that. */ - SystemTimeToFileTime (&st, &ft); - wst->HighPart = ft.dwHighDateTime; - wst->LowPart = ft.dwLowDateTime; + if (using_hires_timers) + { + QueryPerformanceCounter (&wst->hires); + } + else + { + /* Where hires counters are not available, use GetTickCount rather + GetSystemTime, because it is unaffected by clock skew and simpler + to use. Note that overflows don't affect us because we never use + absolute values of the ticker, only the differences. */ + wst->lores = GetTickCount (); + } #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. - - If a non-zero value is used as START, the timer's values will be - offset by START. */ + It is allowed to reset a previously used timer. */ void wtimer_reset (struct wget_timer *wt) @@ -1704,10 +1720,10 @@ wtimer_sys_diff (wget_sys_time *wst1, wget_sys_time *wst2) #endif #ifdef WINDOWS - /* VC++ 6 doesn't support direct cast of uint64 to double. To work - around this, we subtract, then convert to signed, then finally to - double. */ - return (double)(signed __int64)(wst1->QuadPart - wst2->QuadPart) / 10000; + if (using_hires_timers) + return (wst1->hires.QuadPart - wst2->hires.QuadPart) / hires_millisec_freq; + else + return wst1->lores - wst2->lores; #endif } @@ -1789,9 +1805,10 @@ wtimer_granularity (void) #endif #ifdef TIMER_WINDOWS - /* According to MSDN, GetSystemTime returns a broken-down time - structure the smallest member of which are milliseconds. */ - return 1; + if (using_hires_timers) + return 1.0 / hires_millisec_freq; + else + return 10; /* according to MSDN */ #endif } @@ -1964,40 +1981,6 @@ random_float (void) int rnd3 = random_number (1000); return rnd1 / 1000.0 + rnd2 / 1000000.0 + rnd3 / 1000000000.0; } - -#if 0 -/* A debugging function for checking whether an MD5 library works. */ - -#include "gen-md5.h" - -char * -debug_test_md5 (char *buf) -{ - unsigned char raw[16]; - static char res[33]; - unsigned char *p1; - char *p2; - int cnt; - ALLOCA_MD5_CONTEXT (ctx); - - gen_md5_init (ctx); - gen_md5_update ((unsigned char *)buf, strlen (buf), ctx); - gen_md5_finish (ctx, raw); - - p1 = raw; - p2 = res; - cnt = 16; - while (cnt--) - { - *p2++ = XNUM_TO_digit (*p1 >> 4); - *p2++ = XNUM_TO_digit (*p1 & 0xf); - ++p1; - } - *p2 = '\0'; - - return res; -} -#endif /* Implementation of run_with_timeout, a generic timeout-forcing routine for systems with Unix-like signal handling. */ @@ -2168,8 +2151,9 @@ xsleep (double seconds) #ifdef HAVE_NANOSLEEP /* nanosleep is the preferred interface because it offers high accuracy and, more importantly, because it allows us to reliably - restart after having been interrupted by a signal such as - SIGWINCH. */ + restart receiving a signal such as SIGWINCH. (There was an + actual Debian bug report about --limit-rate malfunctioning while + the terminal was being resized.) */ struct timespec sleep, remaining; sleep.tv_sec = (long) seconds; sleep.tv_nsec = 1000000000L * (seconds - (long) seconds); diff --git a/src/utils.h b/src/utils.h index d6a118d2..1b3da628 100644 --- a/src/utils.h +++ b/src/utils.h @@ -61,7 +61,6 @@ void print_malloc_debug_stats (); #endif char *xstrdup_lower PARAMS ((const char *)); -int count_char PARAMS ((const char *, char)); char *strdupdelim PARAMS ((const char *, const char *)); char **sepstring PARAMS ((const char *)); -- 2.39.2