]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
vendor: update subprocess.h (#26061)
authorXuan-Son Nguyen <redacted>
Fri, 24 Jul 2026 06:02:23 +0000 (08:02 +0200)
committerGitHub <redacted>
Fri, 24 Jul 2026 06:02:23 +0000 (08:02 +0200)
scripts/sync_vendor.py
tools/server/server-models.cpp
vendor/sheredom/subprocess.h

index 7bab415a8311c4bf6cd979e2d114ad278ab388fb..a9343e37c1cb7d49c27ab349d8710f1a1d6b9c6a 100755 (executable)
@@ -21,7 +21,7 @@ vendor = {
     f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/split.py":  "split.py",
     f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/LICENSE":   "vendor/cpp-httplib/LICENSE",
 
-    "https://raw.githubusercontent.com/sheredom/subprocess.h/b49c56e9fe214488493021017bf3954b91c7c1f5/subprocess.h": "vendor/sheredom/subprocess.h",
+    "https://raw.githubusercontent.com/sheredom/subprocess.h/8671cee1fc09f11a70ce3782a0ee13177c3aa387/subprocess.h": "vendor/sheredom/subprocess.h",
 }
 
 for url, filename in vendor.items():
index 7c6bfda0ff67b21fac97f96a455bb4e7e6c0b3f0..9eac58e9df27b59b57aa1721a150606e4912f864 100644 (file)
@@ -852,7 +852,7 @@ void server_models::load(const std::string & name, const load_options & opts) {
         //                so that we can use stdout for commands and stderr for logging
         int options = subprocess_option_no_window | subprocess_option_combined_stdout_stderr;
         inst.subproc->sproc.emplace();
-        int result = subprocess_create_ex(argv.data(), options, envp.data(), &inst.subproc->get());
+        int result = subprocess_create_ex(argv.data(), options, envp.data(), nullptr, &inst.subproc->get());
         if (result != 0) {
             throw std::runtime_error("failed to spawn server instance");
         }
index 3e40bae046a21339752bf15221ff7d1d39f6a8ce..5e809023a4f5509039133b54c9ed8fb15235a703 100644 (file)
@@ -89,7 +89,25 @@ enum subprocess_option_e {
   // Search for program names in the PATH variable. Always enabled on Windows.
   // Note: this will **not** search for paths in any provided custom environment
   // and instead uses the PATH of the spawning process.
-  subprocess_option_search_user_path = 0x10
+  subprocess_option_search_user_path = 0x10,
+
+  // Make subprocess_read_stdout and subprocess_read_stderr return immediately
+  // with 0 if no data is available. Requires subprocess_option_enable_async.
+  subprocess_option_enable_async_no_wait = 0x20
+};
+
+// Error codes returned by subprocess_create and subprocess_create_ex.
+// subprocess_error_success is always zero; all errors are non-zero.
+enum subprocess_error_e {
+  subprocess_error_success = 0,
+  subprocess_error_unknown = -1,
+  subprocess_error_invalid_options = -2,
+  subprocess_error_invalid_environment = -3,
+  subprocess_error_not_found = -4,
+  subprocess_error_permission_denied = -5,
+  subprocess_error_no_memory = -6,
+  subprocess_error_pipe = -7,
+  subprocess_error_spawn = -8
 };
 
 #if defined(__cplusplus)
@@ -99,11 +117,14 @@ extern "C" {
 /// @brief Create a process.
 /// @param command_line An array of strings for the command line to execute for
 /// this process. The last element must be NULL to signify the end of the array.
-/// The memory backing this parameter only needs to persist until this function
-/// returns.
+/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode
+/// process creation APIs. The memory backing this parameter only needs to
+/// persist until this function returns.
 /// @param options A bit field of subprocess_option_e's to pass.
 /// @param out_process The newly created process.
-/// @return On success zero is returned.
+/// @return On success zero is returned. On failure a non-zero
+/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms
+/// or `GetLastError()` on Windows for the platform-specific failure reason.
 subprocess_weak int subprocess_create(const char *const command_line[],
                                       int options,
                                       struct subprocess_s *const out_process);
@@ -111,20 +132,28 @@ subprocess_weak int subprocess_create(const char *const command_line[],
 /// @brief Create a process (extended create).
 /// @param command_line An array of strings for the command line to execute for
 /// this process. The last element must be NULL to signify the end of the array.
-/// The memory backing this parameter only needs to persist until this function
-/// returns.
+/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode
+/// process creation APIs. The memory backing this parameter only needs to
+/// persist until this function returns.
 /// @param options A bit field of subprocess_option_e's to pass.
 /// @param environment An optional array of strings for the environment to use
 /// for a child process (each element of the form FOO=BAR). The last element
-/// must be NULL to signify the end of the array.
+/// must be NULL to signify the end of the array. On Windows these strings are
+/// interpreted as UTF-8.
+/// @param process_cwd The current working directory of the newly created
+/// process. If NULL, will be the same as the parent process. On Windows this
+/// string is interpreted as UTF-8.
 /// @param out_process The newly created process.
-/// @return On success zero is returned.
+/// @return On success zero is returned. On failure a non-zero
+/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms
+/// or `GetLastError()` on Windows for the platform-specific failure reason.
 ///
 /// If `options` contains `subprocess_option_inherit_environment`, then
 /// `environment` must be NULL.
 subprocess_weak int
 subprocess_create_ex(const char *const command_line[], int options,
                      const char *const environment[],
+                     const char *const process_cwd,
                      struct subprocess_s *const out_process);
 
 /// @brief Get the standard input file for a process.
@@ -189,7 +218,8 @@ subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
 /// @param buffer The buffer to read into.
 /// @param size The maximum number of bytes to read.
 /// @return The number of bytes actually read into buffer. Can only be 0 if the
-/// process has complete.
+/// process has complete, or if the process was created with
+/// `subprocess_option_enable_async_no_wait` and no data is currently available.
 ///
 /// The only safe way to read from the standard output of a process during it's
 /// execution is to use the `subprocess_option_enable_async` option in
@@ -203,7 +233,8 @@ subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
 /// @param buffer The buffer to read into.
 /// @param size The maximum number of bytes to read.
 /// @return The number of bytes actually read into buffer. Can only be 0 if the
-/// process has complete.
+/// process has complete, or if the process was created with
+/// `subprocess_option_enable_async_no_wait` and no data is currently available.
 ///
 /// The only safe way to read from the standard error of a process during it's
 /// execution is to use the `subprocess_option_enable_async` option in
@@ -230,9 +261,14 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
 #endif
 
 #if !defined(_WIN32)
+#include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <spawn.h>
 #include <stdlib.h>
+#if defined(__APPLE__)
+#include <AvailabilityMacros.h>
+#endif
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -240,6 +276,23 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
 
 #if defined(_WIN32)
 
+#include <wchar.h>
+
+#if defined(__clang__)
+#if __has_warning("-Wc++-keyword")
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++-keyword"
+#endif
+#endif
+
+typedef wchar_t subprocess_wchar_t;
+
+#if defined(__clang__)
+#if __has_warning("-Wc++-keyword")
+#pragma clang diagnostic pop
+#endif
+#endif
+
 #if (_MSC_VER < 1920)
 #ifdef _WIN64
 typedef __int64 subprocess_intptr_t;
@@ -262,7 +315,7 @@ typedef size_t subprocess_size_t;
 
 typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
 typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
+typedef struct _STARTUPINFOW *LPSTARTUPINFOW;
 typedef struct _OVERLAPPED *LPOVERLAPPED;
 
 #ifdef __clang__
@@ -276,6 +329,10 @@ typedef struct _OVERLAPPED *LPOVERLAPPED;
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wpedantic"
 #endif
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
 
 struct subprocess_subprocess_information_s {
   void *hProcess;
@@ -292,9 +349,9 @@ struct subprocess_security_attributes_s {
 
 struct subprocess_startup_info_s {
   unsigned long cb;
-  char *lpReserved;
-  char *lpDesktop;
-  char *lpTitle;
+  subprocess_wchar_t *lpReserved;
+  subprocess_wchar_t *lpDesktop;
+  subprocess_wchar_t *lpTitle;
   unsigned long dwX;
   unsigned long dwY;
   unsigned long dwXSize;
@@ -325,6 +382,9 @@ struct subprocess_overlapped_s {
   void *hEvent;
 };
 
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
 #ifdef __MINGW32__
 #pragma GCC diagnostic pop
 #endif
@@ -333,6 +393,7 @@ struct subprocess_overlapped_s {
 #endif
 
 __declspec(dllimport) unsigned long __stdcall GetLastError(void);
+__declspec(dllimport) void __stdcall SetLastError(unsigned long);
 __declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
                                                          unsigned long);
 __declspec(dllimport) int __stdcall CreatePipe(void **, void **,
@@ -352,9 +413,12 @@ __declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
                                                   void *);
 __declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
                                                    int, const char *);
-__declspec(dllimport) int __stdcall CreateProcessA(
-    const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
-    unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall CreateProcessW(
+    const subprocess_wchar_t *, subprocess_wchar_t *, LPSECURITY_ATTRIBUTES,
+    LPSECURITY_ATTRIBUTES, int, unsigned long, void *,
+    const subprocess_wchar_t *, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall MultiByteToWideChar(
+    unsigned int, unsigned long, const char *, int, subprocess_wchar_t *, int);
 __declspec(dllimport) int __stdcall CloseHandle(void *);
 __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
     void *, unsigned long);
@@ -365,6 +429,10 @@ __declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
     unsigned long, void *const *, int, unsigned long);
 __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
                                                         unsigned long *, int);
+__declspec(dllimport) int __stdcall PeekNamedPipe(void *, void *, unsigned long,
+                                                  unsigned long *,
+                                                  unsigned long *,
+                                                  unsigned long *);
 
 #if defined(_DLL)
 #define SUBPROCESS_DLLIMPORT __declspec(dllimport)
@@ -380,6 +448,7 @@ __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
 SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
 SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
 SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
+SUBPROCESS_DLLIMPORT int __cdecl _close(int);
 
 #ifndef __MINGW32__
 void *__cdecl _alloca(subprocess_size_t);
@@ -414,12 +483,68 @@ struct subprocess_s {
   int return_status;
 #endif
 
-  subprocess_size_t alive;
+  int alive;
+  int no_wait;
 };
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
 
+#if defined(_WIN32)
+subprocess_weak int subprocess_error_from_windows_error(unsigned long error);
+int subprocess_error_from_windows_error(unsigned long error) {
+  enum {
+    errorFileNotFound = 2,
+    errorPathNotFound = 3,
+    errorTooManyOpenFiles = 4,
+    errorAccessDenied = 5,
+    errorInvalidHandle = 6,
+    errorNotEnoughMemory = 8,
+    errorOutOfMemory = 14,
+    errorInvalidDrive = 15,
+    errorBadPathname = 161,
+    errorDirectory = 267
+  };
+
+  switch (error) {
+  case errorFileNotFound:
+  case errorPathNotFound:
+  case errorInvalidDrive:
+  case errorBadPathname:
+  case errorDirectory:
+    return subprocess_error_not_found;
+  case errorAccessDenied:
+    return subprocess_error_permission_denied;
+  case errorTooManyOpenFiles:
+  case errorNotEnoughMemory:
+  case errorOutOfMemory:
+    return subprocess_error_no_memory;
+  case errorInvalidHandle:
+    return subprocess_error_pipe;
+  default:
+    return subprocess_error_unknown;
+  }
+}
+#else
+subprocess_weak int subprocess_error_from_errno(int error);
+int subprocess_error_from_errno(int error) {
+  switch (error) {
+  case ENOENT:
+  case ENOTDIR:
+    return subprocess_error_not_found;
+  case EACCES:
+  case EPERM:
+    return subprocess_error_permission_denied;
+  case EMFILE:
+  case ENFILE:
+  case ENOMEM:
+    return subprocess_error_no_memory;
+  default:
+    return subprocess_error_unknown;
+  }
+}
+#endif
+
 #if defined(__clang__)
 #if __has_warning("-Wunsafe-buffer-usage")
 #pragma clang diagnostic push
@@ -429,6 +554,18 @@ struct subprocess_s {
 
 #if defined(_WIN32)
 subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
+subprocess_weak void subprocess_close_handle(void **handle);
+void subprocess_close_handle(void **handle) {
+  const void *const invalidHandleValue =
+      SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0)));
+
+  if (*handle && (invalidHandleValue != *handle)) {
+    CloseHandle(*handle);
+  }
+
+  *handle = SUBPROCESS_NULL;
+}
+
 int subprocess_create_named_pipe_helper(void **rd, void **wr) {
   const unsigned long pipeAccessInbound = 0x00000001;
   const unsigned long fileFlagOverlapped = 0x40000000;
@@ -442,9 +579,23 @@ int subprocess_create_named_pipe_helper(void **rd, void **wr) {
   struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
                                                     SUBPROCESS_NULL, 1};
   char name[256] = {0};
+#if defined(__clang__)
+#if __has_warning("-Wunique-object-duplication")
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#endif
+#endif
   static subprocess_tls long index = 0;
+#if defined(__clang__)
+#if __has_warning("-Wunique-object-duplication")
+#pragma clang diagnostic pop
+#endif
+#endif
   const long unique = index++;
 
+  *rd = SUBPROCESS_NULL;
+  *wr = SUBPROCESS_NULL;
+
 #if defined(_MSC_VER) && _MSC_VER < 1900
 #pragma warning(push, 1)
 #pragma warning(disable : 4996)
@@ -472,6 +623,7 @@ int subprocess_create_named_pipe_helper(void **rd, void **wr) {
                     openExisting, fileAttributeNormal, SUBPROCESS_NULL);
 
   if (invalidHandleValue == *wr) {
+    subprocess_close_handle(rd);
     return -1;
   }
 
@@ -482,27 +634,41 @@ int subprocess_create_named_pipe_helper(void **rd, void **wr) {
 int subprocess_create(const char *const commandLine[], int options,
                       struct subprocess_s *const out_process) {
   return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
-                              out_process);
+                              SUBPROCESS_NULL, out_process);
 }
 
 int subprocess_create_ex(const char *const commandLine[], int options,
                          const char *const environment[],
+                         const char *const process_cwd,
                          struct subprocess_s *const out_process) {
 #if defined(_WIN32)
   int fd;
-  void *rd, *wr;
+  int async_no_wait;
+  void *rd = SUBPROCESS_NULL;
+  void *wr = SUBPROCESS_NULL;
   char *commandLineCombined;
+  subprocess_wchar_t *commandLineCombinedWide = SUBPROCESS_NULL;
+  subprocess_wchar_t *process_cwd_wide = SUBPROCESS_NULL;
   subprocess_size_t len;
+  int wide_len;
   int i, j;
   int need_quoting;
   unsigned long flags = 0;
+  unsigned long last_error = 0;
+  int result = subprocess_error_unknown;
+  const unsigned int codePageUtf8 = 65001;
+  const unsigned long mbErrInvalidChars = 0x00000008;
   const unsigned long startFUseStdHandles = 0x00000100;
   const unsigned long handleFlagInherit = 0x00000001;
   const unsigned long createNoWindow = 0x08000000;
-  struct subprocess_subprocess_information_s processInfo;
+  const unsigned long createUnicodeEnvironment = 0x00000400;
+  struct subprocess_subprocess_information_s processInfo = {SUBPROCESS_NULL,
+                                                            SUBPROCESS_NULL, 0,
+                                                            0};
   struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
                                                     SUBPROCESS_NULL, 1};
-  char *used_environment = SUBPROCESS_NULL;
+  subprocess_wchar_t empty_environment[2] = {0, 0};
+  subprocess_wchar_t *used_environment = SUBPROCESS_NULL;
   struct subprocess_startup_info_s startInfo = {0,
                                                 SUBPROCESS_NULL,
                                                 SUBPROCESS_NULL,
@@ -522,6 +688,14 @@ int subprocess_create_ex(const char *const commandLine[], int options,
                                                 SUBPROCESS_NULL,
                                                 SUBPROCESS_NULL};
 
+  async_no_wait = subprocess_option_enable_async_no_wait ==
+                  (options & subprocess_option_enable_async_no_wait);
+
+  if (async_no_wait && (subprocess_option_enable_async !=
+                        (options & subprocess_option_enable_async))) {
+    return subprocess_error_invalid_options;
+  }
+
   startInfo.cb = sizeof(startInfo);
   startInfo.dwFlags = startFUseStdHandles;
 
@@ -529,93 +703,142 @@ int subprocess_create_ex(const char *const commandLine[], int options,
     flags |= createNoWindow;
   }
 
+  memset(out_process, 0, sizeof(*out_process));
+
   if (subprocess_option_inherit_environment !=
       (options & subprocess_option_inherit_environment)) {
+    flags |= createUnicodeEnvironment;
+
     if (SUBPROCESS_NULL == environment) {
-      used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0");
+      used_environment = empty_environment;
     } else {
-      // We always end with two null terminators.
-      len = 2;
+      // We always end with two null terminators. MultiByteToWideChar includes
+      // each environment string's null terminator, so start with one extra.
+      len = 1;
 
       for (i = 0; environment[i]; i++) {
-        for (j = 0; '\0' != environment[i][j]; j++) {
-          len++;
+        wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
+                                       environment[i], -1, SUBPROCESS_NULL, 0);
+        if (0 == wide_len) {
+          result = subprocess_error_from_windows_error(GetLastError());
+          if (subprocess_error_unknown == result) {
+            result = subprocess_error_spawn;
+          }
+          goto cleanup;
         }
 
-        // For the null terminator too.
-        len++;
+        len += SUBPROCESS_CAST(subprocess_size_t, wide_len);
       }
 
-      used_environment = SUBPROCESS_CAST(char *, _alloca(len));
+      if (((SUBPROCESS_CAST(subprocess_size_t, -1)) /
+           sizeof(subprocess_wchar_t)) < len) {
+        result = subprocess_error_no_memory;
+        goto cleanup;
+      }
 
-      // Re-use len for the insertion position
+      used_environment = SUBPROCESS_CAST(
+          subprocess_wchar_t *, _alloca(len * sizeof(subprocess_wchar_t)));
+      if (!used_environment) {
+        result = subprocess_error_no_memory;
+        goto cleanup;
+      }
+
+      // Re-use len for the insertion position.
       len = 0;
 
       for (i = 0; environment[i]; i++) {
-        for (j = 0; '\0' != environment[i][j]; j++) {
-          used_environment[len++] = environment[i][j];
+        wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
+                                       environment[i], -1, SUBPROCESS_NULL, 0);
+        if (0 == wide_len) {
+          result = subprocess_error_from_windows_error(GetLastError());
+          if (subprocess_error_unknown == result) {
+            result = subprocess_error_spawn;
+          }
+          goto cleanup;
+        }
+
+        if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
+                                     environment[i], -1,
+                                     &used_environment[len], wide_len)) {
+          result = subprocess_error_from_windows_error(GetLastError());
+          if (subprocess_error_unknown == result) {
+            result = subprocess_error_spawn;
+          }
+          goto cleanup;
         }
 
-        used_environment[len++] = '\0';
+        len += SUBPROCESS_CAST(subprocess_size_t, wide_len);
       }
 
-      // End with the two null terminators.
-      used_environment[len++] = '\0';
-      used_environment[len++] = '\0';
+      // End with the second null terminator.
+      used_environment[len++] = 0;
     }
   } else {
     if (SUBPROCESS_NULL != environment) {
-      return -1;
+      return subprocess_error_invalid_environment;
     }
   }
 
   if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
                   0)) {
-    return -1;
+    result = subprocess_error_pipe;
+    goto cleanup;
   }
 
   if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
-    return -1;
+    result = subprocess_error_pipe;
+    goto cleanup;
   }
 
   fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0);
+  if (-1 == fd) {
+    result = subprocess_error_pipe;
+    goto cleanup;
+  }
+  wr = SUBPROCESS_NULL;
 
-  if (-1 != fd) {
-    out_process->stdin_file = _fdopen(fd, "wb");
-
-    if (SUBPROCESS_NULL == out_process->stdin_file) {
-      return -1;
-    }
+  out_process->stdin_file = _fdopen(fd, "wb");
+  if (SUBPROCESS_NULL == out_process->stdin_file) {
+    _close(fd);
+    goto cleanup;
   }
 
   startInfo.hStdInput = rd;
+  rd = SUBPROCESS_NULL;
 
   if (options & subprocess_option_enable_async) {
     if (subprocess_create_named_pipe_helper(&rd, &wr)) {
-      return -1;
+      result = subprocess_error_pipe;
+      goto cleanup;
     }
   } else {
     if (!CreatePipe(&rd, &wr,
                     SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
-      return -1;
+      result = subprocess_error_pipe;
+      goto cleanup;
     }
   }
 
   if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
-    return -1;
+    result = subprocess_error_pipe;
+    goto cleanup;
   }
 
   fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
+  if (-1 == fd) {
+    result = subprocess_error_pipe;
+    goto cleanup;
+  }
+  rd = SUBPROCESS_NULL;
 
-  if (-1 != fd) {
-    out_process->stdout_file = _fdopen(fd, "rb");
-
-    if (SUBPROCESS_NULL == out_process->stdout_file) {
-      return -1;
-    }
+  out_process->stdout_file = _fdopen(fd, "rb");
+  if (SUBPROCESS_NULL == out_process->stdout_file) {
+    _close(fd);
+    goto cleanup;
   }
 
   startInfo.hStdOutput = wr;
+  wr = SUBPROCESS_NULL;
 
   if (subprocess_option_combined_stdout_stderr ==
       (options & subprocess_option_combined_stdout_stderr)) {
@@ -624,30 +847,37 @@ int subprocess_create_ex(const char *const commandLine[], int options,
   } else {
     if (options & subprocess_option_enable_async) {
       if (subprocess_create_named_pipe_helper(&rd, &wr)) {
-        return -1;
+        result = subprocess_error_pipe;
+        goto cleanup;
       }
     } else {
       if (!CreatePipe(&rd, &wr,
                       SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
-        return -1;
+        result = subprocess_error_pipe;
+        goto cleanup;
       }
     }
 
     if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
-      return -1;
+      result = subprocess_error_pipe;
+      goto cleanup;
     }
 
     fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
+    if (-1 == fd) {
+      result = subprocess_error_pipe;
+      goto cleanup;
+    }
+    rd = SUBPROCESS_NULL;
 
-    if (-1 != fd) {
-      out_process->stderr_file = _fdopen(fd, "rb");
-
-      if (SUBPROCESS_NULL == out_process->stderr_file) {
-        return -1;
-      }
+    out_process->stderr_file = _fdopen(fd, "rb");
+    if (SUBPROCESS_NULL == out_process->stderr_file) {
+      _close(fd);
+      goto cleanup;
     }
 
     startInfo.hStdError = wr;
+    wr = SUBPROCESS_NULL;
   }
 
   if (options & subprocess_option_enable_async) {
@@ -657,6 +887,13 @@ int subprocess_create_ex(const char *const commandLine[], int options,
     out_process->hEventError =
         CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
                      SUBPROCESS_NULL);
+    if (!out_process->hEventOutput || !out_process->hEventError) {
+      result = subprocess_error_from_windows_error(GetLastError());
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_no_memory;
+      }
+      goto cleanup;
+    }
   } else {
     out_process->hEventOutput = SUBPROCESS_NULL;
     out_process->hEventError = SUBPROCESS_NULL;
@@ -694,7 +931,8 @@ int subprocess_create_ex(const char *const commandLine[], int options,
   commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len));
 
   if (!commandLineCombined) {
-    return -1;
+    result = subprocess_error_no_memory;
+    goto cleanup;
   }
 
   // Gonna re-use len to store the write index into commandLineCombined
@@ -735,27 +973,93 @@ int subprocess_create_ex(const char *const commandLine[], int options,
 
   commandLineCombined[len] = '\0';
 
-  if (!CreateProcessA(
+  wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
+                                 commandLineCombined, -1, SUBPROCESS_NULL, 0);
+  if (0 == wide_len) {
+    result = subprocess_error_from_windows_error(GetLastError());
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
+  }
+
+  commandLineCombinedWide = SUBPROCESS_CAST(
+      subprocess_wchar_t *,
+      _alloca(SUBPROCESS_CAST(subprocess_size_t, wide_len) *
+              sizeof(subprocess_wchar_t)));
+  if (!commandLineCombinedWide) {
+    result = subprocess_error_no_memory;
+    goto cleanup;
+  }
+
+  if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
+                               commandLineCombined, -1,
+                               commandLineCombinedWide, wide_len)) {
+    result = subprocess_error_from_windows_error(GetLastError());
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
+  }
+
+  if (process_cwd) {
+    wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars, process_cwd,
+                                   -1, SUBPROCESS_NULL, 0);
+    if (0 == wide_len) {
+      result = subprocess_error_from_windows_error(GetLastError());
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
+    }
+
+    process_cwd_wide = SUBPROCESS_CAST(
+        subprocess_wchar_t *,
+        _alloca(SUBPROCESS_CAST(subprocess_size_t, wide_len) *
+                sizeof(subprocess_wchar_t)));
+    if (!process_cwd_wide) {
+      result = subprocess_error_no_memory;
+      goto cleanup;
+    }
+
+    if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars, process_cwd,
+                                 -1, process_cwd_wide, wide_len)) {
+      result = subprocess_error_from_windows_error(GetLastError());
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
+    }
+  }
+
+  if (!CreateProcessW(
           SUBPROCESS_NULL,
-          commandLineCombined, // command line
-          SUBPROCESS_NULL,     // process security attributes
-          SUBPROCESS_NULL,     // primary thread security attributes
-          1,                   // handles are inherited
-          flags,               // creation flags
-          used_environment,    // used environment
-          SUBPROCESS_NULL,     // use parent's current directory
-          SUBPROCESS_PTR_CAST(LPSTARTUPINFOA,
+          commandLineCombinedWide, // command line
+          SUBPROCESS_NULL,         // process security attributes
+          SUBPROCESS_NULL,         // primary thread security attributes
+          1,                       // handles are inherited
+          flags,                   // creation flags
+          used_environment,        // used environment
+          process_cwd_wide,        // use specified current directory
+          SUBPROCESS_PTR_CAST(LPSTARTUPINFOW,
                               &startInfo), // STARTUPINFO pointer
           SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
-    return -1;
+    result = subprocess_error_from_windows_error(GetLastError());
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
   }
 
   out_process->hProcess = processInfo.hProcess;
+  processInfo.hProcess = SUBPROCESS_NULL;
 
   out_process->hStdInput = startInfo.hStdInput;
+  startInfo.hStdInput = SUBPROCESS_NULL;
 
   // We don't need the handle of the primary thread in the called process.
   CloseHandle(processInfo.hThread);
+  processInfo.hThread = SUBPROCESS_NULL;
 
   if (SUBPROCESS_NULL != startInfo.hStdOutput) {
     CloseHandle(startInfo.hStdOutput);
@@ -763,40 +1067,111 @@ int subprocess_create_ex(const char *const commandLine[], int options,
     if (startInfo.hStdError != startInfo.hStdOutput) {
       CloseHandle(startInfo.hStdError);
     }
+
+    startInfo.hStdOutput = SUBPROCESS_NULL;
+    startInfo.hStdError = SUBPROCESS_NULL;
   }
 
   out_process->alive = 1;
+  out_process->no_wait = async_no_wait;
 
   return 0;
+
+cleanup:
+  last_error = GetLastError();
+
+  if (subprocess_error_unknown == result) {
+    result = subprocess_error_from_windows_error(last_error);
+  }
+
+  if (out_process->stdin_file) {
+    fclose(out_process->stdin_file);
+    out_process->stdin_file = SUBPROCESS_NULL;
+  }
+
+  if (out_process->stdout_file) {
+    fclose(out_process->stdout_file);
+    if (out_process->stderr_file &&
+        (out_process->stdout_file != out_process->stderr_file)) {
+      fclose(out_process->stderr_file);
+    }
+    out_process->stdout_file = SUBPROCESS_NULL;
+    out_process->stderr_file = SUBPROCESS_NULL;
+  }
+
+  subprocess_close_handle(&rd);
+  subprocess_close_handle(&wr);
+  subprocess_close_handle(&startInfo.hStdInput);
+
+  if (startInfo.hStdOutput == startInfo.hStdError) {
+    subprocess_close_handle(&startInfo.hStdOutput);
+    startInfo.hStdError = SUBPROCESS_NULL;
+  } else {
+    subprocess_close_handle(&startInfo.hStdOutput);
+    subprocess_close_handle(&startInfo.hStdError);
+  }
+
+  subprocess_close_handle(&out_process->hEventOutput);
+  subprocess_close_handle(&out_process->hEventError);
+  subprocess_close_handle(&processInfo.hThread);
+  subprocess_close_handle(&processInfo.hProcess);
+
+  SetLastError(last_error);
+
+  return result;
 #else
-  int stdinfd[2];
-  int stdoutfd[2];
-  int stderrfd[2];
-  pid_t child;
+  int stdinfd[2] = {-1, -1};
+  int stdoutfd[2] = {-1, -1};
+  int stderrfd[2] = {-1, -1};
+  int fd, fd_flags;
+  int async_no_wait;
+  int actions_created = 0;
+  int result = subprocess_error_unknown;
+  int saved_errno = 0;
+  int posix_error;
+  pid_t child = 0;
   extern char **environ;
   char *const empty_environment[1] = {SUBPROCESS_NULL};
   posix_spawn_file_actions_t actions;
   char *const *used_environment;
 
+  async_no_wait = subprocess_option_enable_async_no_wait ==
+                  (options & subprocess_option_enable_async_no_wait);
+
+  if (async_no_wait && (subprocess_option_enable_async !=
+                        (options & subprocess_option_enable_async))) {
+    errno = EINVAL;
+    return subprocess_error_invalid_options;
+  }
+
   if (subprocess_option_inherit_environment ==
       (options & subprocess_option_inherit_environment)) {
     if (SUBPROCESS_NULL != environment) {
-      return -1;
+      errno = EINVAL;
+      return subprocess_error_invalid_environment;
     }
   }
 
+  memset(out_process, 0, sizeof(*out_process));
+
   if (0 != pipe(stdinfd)) {
-    return -1;
+    saved_errno = errno;
+    result = subprocess_error_pipe;
+    goto cleanup;
   }
 
   if (0 != pipe(stdoutfd)) {
-    return -1;
+    saved_errno = errno;
+    result = subprocess_error_pipe;
+    goto cleanup;
   }
 
   if (subprocess_option_combined_stdout_stderr !=
       (options & subprocess_option_combined_stdout_stderr)) {
     if (0 != pipe(stderrfd)) {
-      return -1;
+      saved_errno = errno;
+      result = subprocess_error_pipe;
+      goto cleanup;
     }
   }
 
@@ -817,54 +1192,120 @@ int subprocess_create_ex(const char *const commandLine[], int options,
     used_environment = empty_environment;
   }
 
-  if (0 != posix_spawn_file_actions_init(&actions)) {
-    return -1;
+  posix_error = posix_spawn_file_actions_init(&actions);
+  if (0 != posix_error) {
+    saved_errno = posix_error;
+    result = subprocess_error_from_errno(posix_error);
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
+  }
+  actions_created = 1;
+
+  // Set working directory
+  if (process_cwd) {
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000
+    posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
+#else
+#if defined(__APPLE__) && defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+    posix_error = posix_spawn_file_actions_addchdir_np(&actions, process_cwd);
+#if defined(__APPLE__) && defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
+    }
   }
 
   // Close the stdin write end
-  if (0 != posix_spawn_file_actions_addclose(&actions, stdinfd[1])) {
-    posix_spawn_file_actions_destroy(&actions);
-    return -1;
+  posix_error = posix_spawn_file_actions_addclose(&actions, stdinfd[1]);
+  if (0 != posix_error) {
+    saved_errno = posix_error;
+    result = subprocess_error_from_errno(posix_error);
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
   }
 
   // Map the read end to stdin
-  if (0 !=
-      posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO)) {
-    posix_spawn_file_actions_destroy(&actions);
-    return -1;
+  posix_error =
+      posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO);
+  if (0 != posix_error) {
+    saved_errno = posix_error;
+    result = subprocess_error_from_errno(posix_error);
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
   }
 
   // Close the stdout read end
-  if (0 != posix_spawn_file_actions_addclose(&actions, stdoutfd[0])) {
-    posix_spawn_file_actions_destroy(&actions);
-    return -1;
+  posix_error = posix_spawn_file_actions_addclose(&actions, stdoutfd[0]);
+  if (0 != posix_error) {
+    saved_errno = posix_error;
+    result = subprocess_error_from_errno(posix_error);
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
   }
 
   // Map the write end to stdout
-  if (0 !=
-      posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO)) {
-    posix_spawn_file_actions_destroy(&actions);
-    return -1;
+  posix_error =
+      posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO);
+  if (0 != posix_error) {
+    saved_errno = posix_error;
+    result = subprocess_error_from_errno(posix_error);
+    if (subprocess_error_unknown == result) {
+      result = subprocess_error_spawn;
+    }
+    goto cleanup;
   }
 
   if (subprocess_option_combined_stdout_stderr ==
       (options & subprocess_option_combined_stdout_stderr)) {
-    if (0 != posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO,
-                                              STDERR_FILENO)) {
-      posix_spawn_file_actions_destroy(&actions);
-      return -1;
+    posix_error = posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO,
+                                                   STDERR_FILENO);
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
     }
   } else {
     // Close the stderr read end
-    if (0 != posix_spawn_file_actions_addclose(&actions, stderrfd[0])) {
-      posix_spawn_file_actions_destroy(&actions);
-      return -1;
+    posix_error = posix_spawn_file_actions_addclose(&actions, stderrfd[0]);
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
     }
     // Map the write end to stdout
-    if (0 != posix_spawn_file_actions_adddup2(&actions, stderrfd[1],
-                                              STDERR_FILENO)) {
-      posix_spawn_file_actions_destroy(&actions);
-      return -1;
+    posix_error = posix_spawn_file_actions_adddup2(&actions, stderrfd[1],
+                                                   STDERR_FILENO);
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
     }
   }
 
@@ -875,18 +1316,30 @@ int subprocess_create_ex(const char *const commandLine[], int options,
 #endif
   if (subprocess_option_search_user_path ==
       (options & subprocess_option_search_user_path)) {
-    if (0 != posix_spawnp(&child, commandLine[0], &actions, SUBPROCESS_NULL,
-                          SUBPROCESS_CONST_CAST(char *const *, commandLine),
-                          used_environment)) {
-      posix_spawn_file_actions_destroy(&actions);
-      return -1;
+    posix_error = posix_spawnp(&child, commandLine[0], &actions,
+                               SUBPROCESS_NULL,
+                               SUBPROCESS_CONST_CAST(char *const *, commandLine),
+                               used_environment);
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
     }
   } else {
-    if (0 != posix_spawn(&child, commandLine[0], &actions, SUBPROCESS_NULL,
-                         SUBPROCESS_CONST_CAST(char *const *, commandLine),
-                         used_environment)) {
-      posix_spawn_file_actions_destroy(&actions);
-      return -1;
+    posix_error = posix_spawn(&child, commandLine[0], &actions,
+                              SUBPROCESS_NULL,
+                              SUBPROCESS_CONST_CAST(char *const *, commandLine),
+                              used_environment);
+    if (0 != posix_error) {
+      saved_errno = posix_error;
+      result = subprocess_error_from_errno(posix_error);
+      if (subprocess_error_unknown == result) {
+        result = subprocess_error_spawn;
+      }
+      goto cleanup;
     }
   }
 #ifdef __clang__
@@ -895,13 +1348,34 @@ int subprocess_create_ex(const char *const commandLine[], int options,
 
   // Close the stdin read end
   close(stdinfd[0]);
+  stdinfd[0] = -1;
   // Store the stdin write end
   out_process->stdin_file = fdopen(stdinfd[1], "wb");
+  if (SUBPROCESS_NULL == out_process->stdin_file) {
+    saved_errno = errno;
+    result = subprocess_error_from_errno(saved_errno);
+    goto cleanup;
+  }
+  stdinfd[1] = -1;
 
   // Close the stdout write end
   close(stdoutfd[1]);
+  stdoutfd[1] = -1;
   // Store the stdout read end
   out_process->stdout_file = fdopen(stdoutfd[0], "rb");
+  if (SUBPROCESS_NULL == out_process->stdout_file) {
+    saved_errno = errno;
+    result = subprocess_error_from_errno(saved_errno);
+    goto cleanup;
+  }
+  stdoutfd[0] = -1;
+
+  // Set non blocking if we are async and asked not to wait.
+  if (async_no_wait) {
+    fd = fileno(out_process->stdout_file);
+    fd_flags = fcntl(fd, F_GETFL, 0);
+    fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
+  }
 
   if (subprocess_option_combined_stdout_stderr ==
       (options & subprocess_option_combined_stdout_stderr)) {
@@ -909,17 +1383,92 @@ int subprocess_create_ex(const char *const commandLine[], int options,
   } else {
     // Close the stderr write end
     close(stderrfd[1]);
+    stderrfd[1] = -1;
     // Store the stderr read end
     out_process->stderr_file = fdopen(stderrfd[0], "rb");
+    if (SUBPROCESS_NULL == out_process->stderr_file) {
+      saved_errno = errno;
+      result = subprocess_error_from_errno(saved_errno);
+      goto cleanup;
+    }
+    stderrfd[0] = -1;
+
+    // Set non blocking if we are async and asked not to wait.
+    if (async_no_wait) {
+      fd = fileno(out_process->stderr_file);
+      fd_flags = fcntl(fd, F_GETFL, 0);
+      fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
+    }
   }
 
   // Store the child's pid
   out_process->child = child;
+  child = 0;
 
   out_process->alive = 1;
+  out_process->no_wait = async_no_wait;
 
-  posix_spawn_file_actions_destroy(&actions);
-  return 0;
+  result = 0;
+
+cleanup:
+  if ((0 != result) && (0 == saved_errno)) {
+    saved_errno = errno;
+  }
+
+  if ((subprocess_error_unknown == result) && (0 != saved_errno)) {
+    result = subprocess_error_from_errno(saved_errno);
+  }
+
+  if (actions_created) {
+    posix_spawn_file_actions_destroy(&actions);
+  }
+
+  if (0 != result) {
+    if (child) {
+      kill(child, 9);
+      waitpid(child, SUBPROCESS_NULL, 0);
+    }
+
+    if (out_process->stdin_file) {
+      fclose(out_process->stdin_file);
+      out_process->stdin_file = SUBPROCESS_NULL;
+    }
+
+    if (out_process->stdout_file) {
+      fclose(out_process->stdout_file);
+      if (out_process->stderr_file &&
+          (out_process->stdout_file != out_process->stderr_file)) {
+        fclose(out_process->stderr_file);
+      }
+      out_process->stdout_file = SUBPROCESS_NULL;
+      out_process->stderr_file = SUBPROCESS_NULL;
+    }
+  }
+
+  if (-1 != stdinfd[0]) {
+    close(stdinfd[0]);
+  }
+  if (-1 != stdinfd[1]) {
+    close(stdinfd[1]);
+  }
+  if (-1 != stdoutfd[0]) {
+    close(stdoutfd[0]);
+  }
+  if (-1 != stdoutfd[1]) {
+    close(stdoutfd[1]);
+  }
+  if (-1 != stderrfd[0]) {
+    close(stderrfd[0]);
+  }
+  if (-1 != stderrfd[1]) {
+    close(stderrfd[1]);
+  }
+
+  if ((0 != result) && (0 != saved_errno)) {
+    errno = saved_errno;
+  }
+
+  return result;
 #endif
 }
 
@@ -1060,6 +1609,7 @@ unsigned subprocess_read_stdout(struct subprocess_s *const process,
                                 char *const buffer, unsigned size) {
 #if defined(_WIN32)
   void *handle;
+  unsigned long bytes_available = 0;
   unsigned long bytes_read = 0;
   struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
   overlapped.hEvent = process->hEventOutput;
@@ -1067,6 +1617,17 @@ unsigned subprocess_read_stdout(struct subprocess_s *const process,
   handle = SUBPROCESS_PTR_CAST(void *,
                                _get_osfhandle(_fileno(process->stdout_file)));
 
+  if (process->no_wait) {
+    if (!PeekNamedPipe(handle, SUBPROCESS_NULL, 0, SUBPROCESS_NULL,
+                       &bytes_available, SUBPROCESS_NULL)) {
+      return 0;
+    }
+
+    if (0 == bytes_available) {
+      return 0;
+    }
+  }
+
   if (!ReadFile(handle, buffer, size, &bytes_read,
                 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
     const unsigned long errorIoPending = 997;
@@ -1074,14 +1635,20 @@ unsigned subprocess_read_stdout(struct subprocess_s *const process,
 
     // Means we've got an async read!
     if (error == errorIoPending) {
+      const uintptr_t statusPending = 0x00000103;
+
+      const int wait = statusPending == overlapped.Internal;
+
       if (!GetOverlappedResult(handle,
                                SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
-                               &bytes_read, 1)) {
-        const unsigned long errorIoIncomplete = 996;
+                               &bytes_read, wait)) {
         const unsigned long errorHandleEOF = 38;
+        const unsigned long errorBrokenPipe = 109;
+        const unsigned long errorIoIncomplete = 996;
         error = GetLastError();
 
-        if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+        if ((errorHandleEOF != error) && (errorBrokenPipe != error) &&
+            (errorIoIncomplete != error)) {
           return 0;
         }
       }
@@ -1105,6 +1672,7 @@ unsigned subprocess_read_stderr(struct subprocess_s *const process,
                                 char *const buffer, unsigned size) {
 #if defined(_WIN32)
   void *handle;
+  unsigned long bytes_available = 0;
   unsigned long bytes_read = 0;
   struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
   overlapped.hEvent = process->hEventError;
@@ -1112,6 +1680,17 @@ unsigned subprocess_read_stderr(struct subprocess_s *const process,
   handle = SUBPROCESS_PTR_CAST(void *,
                                _get_osfhandle(_fileno(process->stderr_file)));
 
+  if (process->no_wait) {
+    if (!PeekNamedPipe(handle, SUBPROCESS_NULL, 0, SUBPROCESS_NULL,
+                       &bytes_available, SUBPROCESS_NULL)) {
+      return 0;
+    }
+
+    if (0 == bytes_available) {
+      return 0;
+    }
+  }
+
   if (!ReadFile(handle, buffer, size, &bytes_read,
                 SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
     const unsigned long errorIoPending = 997;