From 5a905bcec36adc75c7ab3d66b66b9d449874eafc Mon Sep 17 00:00:00 2001 From: hniksic Date: Thu, 2 Oct 2003 16:28:49 -0700 Subject: [PATCH] [svn] Window-specific implementation of run_with_timeout. By Gisle Vanem. --- src/ChangeLog | 6 +++ src/mswindows.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.c | 23 +++++++--- 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 3ae83a9d..c65bd6e8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,9 @@ +2003-10-02 Gisle Vanem + + * mswindows.c (run_with_timeout): For Windows: Run the 'fun' in a + thread via a helper function. Continually query the thread's + exit-code until finished or timed out. + 2003-10-02 Hrvoje Niksic * wget.h (XMALLOC_ARRAY): Removed. diff --git a/src/mswindows.c b/src/mswindows.c index d1a2a543..e5ae2f50 100644 --- a/src/mswindows.c +++ b/src/mswindows.c @@ -340,4 +340,116 @@ DWORD set_sleep_mode (DWORD mode) DEBUGP (("set_sleep_mode(): mode 0x%08lX, rc 0x%08lX\n", mode, rc)); return (rc); } + +/* run_with_timeout Windows implementation. */ + +/* Wait for thread completion in 0.1s intervals (a tradeoff between + * CPU loading and resolution). + */ +#define THREAD_WAIT_INTV 100 +#define THREAD_STACK_SIZE 4096 + +struct thread_data { + void (*fun) (void *); + void *arg; + DWORD ws_error; +}; + +static DWORD WINAPI +thread_helper (void *arg) +{ + struct thread_data *td = (struct thread_data *) arg; + + WSASetLastError (0); + td->ws_error = 0; + (*td->fun) (td->arg); + + /* Since run_with_timeout() is only used for Winsock functions and + * Winsock errors are per-thread, we must return this to caller. + */ + td->ws_error = WSAGetLastError(); + return (0); +} + +#ifdef GV_DEBUG /* I'll remove this eventually */ +# define DEBUGN(lvl,x) do { if (opt.verbose >= (lvl)) DEBUGP (x); } while (0) +#else +# define DEBUGN(lvl,x) ((void)0) +#endif + +/* + * Run FUN with timeout. This is done by creating a thread for 'fun' + * to run in. Since call-convention of 'fun' is undefined [1], we + * must call it via thread_helper() which must be __stdcall/WINAPI. + * + * [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is the + * default (wcc386 -3r). + */ + +int +run_with_timeout (double seconds, void (*fun) (void *), void *arg) +{ + static HANDLE thread_hnd = NULL; + struct thread_data thread_arg; + struct wget_timer *timer; + DWORD thread_id, exitCode; + + DEBUGN (2, ("seconds %.2f, ", seconds)); + + if (seconds == 0) + { + blocking_fallback: + fun (arg); + return 0; + } + /* Should never happen, but test for recursivety anyway */ + assert (thread_hnd == NULL); + thread_arg.arg = arg; + thread_arg.fun = fun; + thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, + thread_helper, (void*)&thread_arg, + 0, &thread_id); + if (!thread_hnd) + { + DEBUGP (("CreateThread() failed; %s\n", strerror(GetLastError()))); + goto blocking_fallback; + } + + timer = wtimer_new(); + + exitCode = 0; + + /* Keep checking for thread's state until the timeout expires. */ + while (wtimer_elapsed (timer) < 1000 * seconds) + { + GetExitCodeThread (thread_hnd, &exitCode); + DEBUGN (2, ("thread exit-code %lu\n", exitCode)); + if (exitCode != STILL_ACTIVE) + break; + Sleep (THREAD_WAIT_INTV); + } + + DEBUGN (2, ("elapsed %.2f, wtimer_elapsed %.2f, ", elapsed, + wtimer_elapsed (timer))); + + wtimer_delete (timer); + + /* If we timed out kill the thread. Normal thread exitCode would be 0. + */ + if (exitCode == STILL_ACTIVE) + { + DEBUGN (2, ("thread timed out\n")); + TerminateThread (thread_hnd, 0); + } + else + { + DEBUGN (2, ("thread exit-code %lu, WS error %lu\n", exitCode, thread_arg.ws_error)); + + /* Propagate error state (which is per-thread) to this thread, + so the caller can inspect it. */ + WSASetLastError (thread_arg.ws_error); + } + thread_hnd = NULL; + return exitCode == STILL_ACTIVE; +} diff --git a/src/utils.c b/src/utils.c index da3787f8..fb180e8d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2017,8 +2017,6 @@ alarm_cancel (void) #endif /* not ITIMER_REAL */ } -#endif /* USE_SIGNAL_TIMEOUT */ - /* Run FUN(ARG) for not more than TIMEOUT seconds. Returns non-zero if the function was interrupted with a timeout, zero otherwise. @@ -2048,10 +2046,6 @@ alarm_cancel (void) int run_with_timeout (double timeout, void (*fun) (void *), void *arg) { -#ifndef USE_SIGNAL_TIMEOUT - fun (arg); - return 0; -#else int saved_errno; if (timeout == 0) @@ -2077,5 +2071,20 @@ run_with_timeout (double timeout, void (*fun) (void *), void *arg) errno = saved_errno; return 0; -#endif } + +#else /* not USE_SIGNAL_TIMEOUT */ + +#ifndef WINDOWS +/* A stub version of run_with_timeout that just calls FUN(ARG). Don't + define it under Windows, because Windows has its own version of + run_with_timeout that uses threads. */ + +int +run_with_timeout (double timeout, void (*fun) (void *), void *arg) +{ + fun (arg); + return 0; +} +#endif /* not WINDOWS */ +#endif /* not USE_SIGNAL_TIMEOUT */ -- 2.39.2