Bläddra i källkod

Use PeekNamedPipe to avoid blocking ReadFile when there is nothing to read

Steven Murdoch 13 år sedan
förälder
incheckning
2d5059e08e
2 ändrade filer med 41 tillägg och 11 borttagningar
  1. 39 9
      src/common/util.c
  2. 2 2
      src/test/test_util.c

+ 39 - 9
src/common/util.c

@@ -3287,27 +3287,55 @@ tor_get_exit_code(const process_handle_t process_handle)
 #ifdef MS_WINDOWS
 /* Windows equivalent of read_all */
 static ssize_t
-read_all_handle(HANDLE h, char *buf, size_t count)
+read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess)
 {
   size_t numread = 0;
   BOOL retval;
-  DWORD bytes_read;
+  DWORD byte_count;
+  BOOL process_exited = FALSE;
 
   if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
     return -1;
 
   while (numread != count) {
-    retval = ReadFile(h, buf+numread, count-numread, &bytes_read, NULL);
+    retval = PeekNamedPipe(h, NULL, 0, NULL, &byte_count, NULL);
     if (!retval) {
       log_warn(LD_GENERAL,
-        "Failed to read from stdin pipe: %s",
+        "Failed to peek from handle: %s",
         format_win32_error(GetLastError()));
       return -1;
-    } else if (0 == bytes_read) {
+    } else if (0 == byte_count) {
+      /* Nothing available: process exited or it is busy */
+
+      /* Keep on reading if we don't know whether the process is running */
+      if (NULL == hProcess)
+        continue;
+
+      /* The process exited and there's nothing left to read from it */
+      if (process_exited)
+        break;
+
+      /* If process is not running, check for output one more time in case
+         it wrote something after the peek was performed. Otherwise keep on
+         waiting for output */
+      byte_count = WaitForSingleObject(hProcess, 0);
+      if (WAIT_TIMEOUT != byte_count)
+        process_exited = TRUE;
+
+      continue;
+    }
+
+    retval = ReadFile(h, buf+numread, count-numread, &byte_count, NULL);
+    if (!retval) {
+      log_warn(LD_GENERAL,
+        "Failed to read from handle: %s",
+        format_win32_error(GetLastError()));
+      return -1;
+    } else if (0 == byte_count) {
       /* End of file */
-      return bytes_read;
+      break;
     }
-    numread += bytes_read;
+    numread += byte_count;
   }
   return (ssize_t)numread;
 }
@@ -3318,7 +3346,8 @@ tor_read_all_from_process_stdout(const process_handle_t process_handle,
                                 char *buf, size_t count)
 {
 #ifdef MS_WINDOWS
-  return read_all_handle(process_handle.stdout_pipe, buf, count);
+  return read_all_handle(process_handle.stdout_pipe, buf, count,
+                         process_handle.pid.hProcess);
 #else
   return read_all(process_handle.stdout_pipe, buf, count, 0);
 #endif
@@ -3329,7 +3358,8 @@ tor_read_all_from_process_stderr(const process_handle_t process_handle,
                                  char *buf, size_t count)
 {
 #ifdef MS_WINDOWS
-  return read_all_handle(process_handle.stderr_pipe, buf, count);
+  return read_all_handle(process_handle.stderr_pipe, buf, count,
+                         process_handle.pid.hProcess);
 #else
   return read_all(process_handle.stderr_pipe, buf, count, 0);
 #endif

+ 2 - 2
src/test/test_util.c

@@ -1431,11 +1431,11 @@ test_util_spawn_background_ok(void *ptr)
 #ifdef MS_WINDOWS
   // TODO: Under MSYS, BUILDDIR in orconfig.h needs to be tweaked
   const char *argv[] = {BUILDDIR "/src/test/test-child.exe", "--test", NULL};
-  const char *expected_out = "OUT\r\n--test\r\nDONE\r\n";
+  const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n";
   const char *expected_err = "ERR\r\n";
 #else
   const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
-  const char *expected_out = "OUT\n--test\nDONE\n";
+  const char *expected_out = "OUT\n--test\nSLEEPING\nDONE\n";
   const char *expected_err = "ERR\n";
 #endif