SDL: Make sure stdio handles are in blocking mode

From afee27a5304b5dec9a8694cb408f877bf1c58d6a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 14 Oct 2024 21:45:41 -0700
Subject: [PATCH] Make sure stdio handles are in blocking mode

Standard I/O doesn't work well with non-blocking handles, so make sure any pipes are in blocking mode before launching child processes.

Fixes https://github.com/libsdl-org/SDL/issues/10998
---
 src/process/windows/SDL_windowsprocess.c |  8 ++++++++
 test/childprocess.c                      | 19 +++++++++++++++++++
 test/testprocess.c                       |  4 ++++
 3 files changed, 31 insertions(+)

diff --git a/src/process/windows/SDL_windowsprocess.c b/src/process/windows/SDL_windowsprocess.c
index abfacc21b0f50..dc2001439f445 100644
--- a/src/process/windows/SDL_windowsprocess.c
+++ b/src/process/windows/SDL_windowsprocess.c
@@ -73,6 +73,14 @@ static bool SetupRedirect(SDL_PropertiesID props, const char *property, HANDLE *
         WIN_SetError("DuplicateHandle()");
         return false;
     }
+
+    if (GetFileType(*result) == FILE_TYPE_PIPE) {
+        DWORD wait_mode = PIPE_WAIT;
+        if (!SetNamedPipeHandleState(*result, &wait_mode, NULL, NULL)) {
+            WIN_SetError("SetNamedPipeHandleState()");
+            return false;
+        }
+    }
     return true;
 }
 
diff --git a/test/childprocess.c b/test/childprocess.c
index dede17717d59c..333fc3c9732e2 100644
--- a/test/childprocess.c
+++ b/test/childprocess.c
@@ -13,6 +13,7 @@ int main(int argc, char *argv[]) {
     bool stdin_to_stdout = false;
     bool read_stdin = false;
     bool stdin_to_stderr = false;
+    SDL_IOStream *log_stdin = NULL;
     int exit_code = 0;
 
     state = SDLTest_CommonCreateState(argv, 0);
@@ -45,6 +46,15 @@ int main(int argc, char *argv[]) {
                     fprintf(stderr, "%s", argv[i + 1]);
                     consumed = 2;
                 }
+            } else if (SDL_strcmp(argv[i], "--log-stdin") == 0) {
+                if (i + 1 < argc) {
+                    log_stdin = SDL_IOFromFile(argv[i + 1], "w");
+                    if (!log_stdin) {
+                        fprintf(stderr, "Couldn't open %s\n", argv[i + 1]);
+                        return 2;
+                    }
+                    consumed = 2;
+                }
             } else if (SDL_strcmp(argv[i], "--exit-code") == 0) {
                 if (i + 1 < argc) {
                     char *endptr = NULL;
@@ -75,6 +85,7 @@ int main(int argc, char *argv[]) {
                 "[--print-arguments]",
                 "[--print-environment]",
                 "[--stdin]",
+                "[--log-stdin FILE]",
                 "[--stdin-to-stdout]",
                 "[--stdout TEXT]",
                 "[--stdin-to-stderr]",
@@ -135,6 +146,10 @@ int main(int argc, char *argv[]) {
                 }
                 break;
             }
+            if (log_stdin) {
+                SDL_WriteIO(log_stdin, buffer, result);
+                SDL_FlushIO(log_stdin);
+            }
             if (stdin_to_stdout) {
                 fwrite(buffer, 1, result, stdout);
                 fflush(stdout);
@@ -145,6 +160,10 @@ int main(int argc, char *argv[]) {
         }
     }
 
+    if (log_stdin) {
+        SDL_CloseIO(log_stdin);
+    }
+
     SDLTest_CommonDestroyState(state);
 
     return exit_code;
diff --git a/test/testprocess.c b/test/testprocess.c
index 5f47010e186e8..425b445151835 100644
--- a/test/testprocess.c
+++ b/test/testprocess.c
@@ -667,6 +667,8 @@ static int process_testMultiprocessStdinToStdout(void *arg)
     const char *process_args[] = {
         data->childprocess_path,
         "--stdin-to-stdout",
+        "--log-stdin",
+        NULL,
         NULL,
     };
     SDL_Process *process1 = NULL;
@@ -680,6 +682,7 @@ static int process_testMultiprocessStdinToStdout(void *arg)
     size_t total_read = 0;
     bool finished;
 
+    process_args[3] = "child1-stdin.txt";
     process1 = SDL_CreateProcess(process_args, true);
     SDLTest_AssertCheck(process1 != NULL, "SDL_CreateProcess()");
     if (!process1) {
@@ -692,6 +695,7 @@ static int process_testMultiprocessStdinToStdout(void *arg)
     SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, SDL_GetPointerProperty(SDL_GetProcessProperties(process1), SDL_PROP_PROCESS_STDOUT_POINTER, NULL));
     SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
     SDLTest_AssertPass("About to call SDL_CreateProcessWithProperties");
+    process_args[3] = "child2-stdin.txt";
     process2 = SDL_CreateProcessWithProperties(props);
     SDL_DestroyProperties(props);
     SDLTest_AssertCheck(process2 != NULL, "SDL_CreateProcess()");