]> sjero.net Git - wget/commitdiff
[svn] Window-specific implementation of run_with_timeout.
authorhniksic <devnull@localhost>
Thu, 2 Oct 2003 23:28:49 +0000 (16:28 -0700)
committerhniksic <devnull@localhost>
Thu, 2 Oct 2003 23:28:49 +0000 (16:28 -0700)
By Gisle Vanem.

src/ChangeLog
src/mswindows.c
src/utils.c

index 3ae83a9d09fd2448ddf75e9132115a7c597f13f7..c65bd6e869e7b5231244763e02816ea7f1ab100c 100644 (file)
@@ -1,3 +1,9 @@
+2003-10-02  Gisle Vanem  <giva@bgnett.no>
+        
+        * 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  <hniksic@xemacs.org>
 
        * wget.h (XMALLOC_ARRAY): Removed.
index d1a2a543b55cbe14b08f2d8d415f6a317109c37c..e5ae2f50d7e51c3ee6526dd6b7e9276f45c927d9 100644 (file)
@@ -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);
 }
+\f
+/* 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;
+}
index da3787f8a5b65813fdf75f824d05525d19301947..fb180e8da5a2754a9e116ab732a41148a8020de0 100644 (file)
@@ -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 */