I've patched util.c to make run_with_timeout() work on
Windows (better than it does with alarm()!).

In short it creates and starts a thread, then loops querying 
the thread exit-code. breaks if != STLL_ACTIVE, else sleep
for 0.1 sec. Uses a wget_timer too for added accuracy.

Tested with --dns-timeout, --connect-timeout, gethostbyname()
and getaddrinfo(). Built and tested wih MingW/gcc 3,3,1, OpenWatcom 
1.1 and DMC 8.36, but not MSVC 6. All seems okay. 

I have a problem with run_with_timeout() returning 1 and hence
lookup_host() reporting ETIMEDOUT. Isn't TRY_AGAIN more suited
indicating the caller should try a longer timeout?

Patch against beta-2 (I think):

--- src/utils.c.orig Sun Sep 21 01:12:18 2003
+++ src/utils.c Thu Oct 02 22:04:01 2003
@@ -1965,12 +1965,141 @@
 # endif /* not HAVE_SIGSETJMP */
 #endif /* USE_SIGNAL_TIMEOUT */
 
+
+#if defined(WINDOWS)
+
+/* 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  
+
+/*
+ * Create 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.
+ *
+ * Return -1 if illegal timeout or failed to create thread.
+ * Return +1 on thread timeout,
+ * else 0 (okay)
+ *
+ * [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is the
+ *     default (wcc386 -3r). 
+ */
+static BOOL
+spawn_thread (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;
+  double elapsed, max_msec;
+  
+  DEBUGN (2, ("seconds %.2f, ", seconds));
+  
+  if (seconds == 0.0)
+    return (-1);     /* run blocking 'fun' */
+    
+  if (seconds < 1.0)
+    seconds = 1.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())));
+    return (-1);  
+  }    
+     
+  exitCode = STILL_ACTIVE;
+  max_msec = 1000.0 * seconds;
+  timer = wtimer_new();  
+  
+  /* Sleep() isn't very accurate, so do a double check in the for-loop */
+  for (elapsed = 0.0; 
+       elapsed < max_msec && wtimer_elapsed(timer) < max_msec;
+       elapsed += (double)THREAD_WAIT_INTV)
+  {
+    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"));
+    exitCode = 1;
+    TerminateThread (thread_hnd, exitCode);
+    WSASetLastError (ETIMEDOUT); /* overridden by caller */
+  }  
+  else
+  {
+    DEBUGN (2, ("thread exit-code %lu, WS error %lu\n", exitCode, 
thread_arg.ws_error));
+    exitCode = 0; 
+    WSASetLastError (thread_arg.ws_error);
+  }  
+  thread_hnd = NULL;
+  return (exitCode);
+}
+#endif  /* WINDOWS */
+
 int
 run_with_timeout (double timeout, void (*fun) (void *), void *arg)
 {
-#ifndef USE_SIGNAL_TIMEOUT
+#if defined(WINDOWS)
+  int rc = spawn_thread (timeout, fun, arg);
+  
+  if (rc < 0)
+  {
+    fun (arg);
+    rc = 0;
+  }  
+  return rc;
+  
+#elif !defined(USE_SIGNAL_TIMEOUT)
   fun (arg);
   return 0;
+
 #else
   int saved_errno;

----------------------------------------------------------------

Gisle V.

# rm /bin/laden 
/bin/laden: Not found

Reply via email to