Windows 10 and 11 introduce the timeout.exe command. This is a similar program with same argument options.
https://bugs.winehq.org/show_bug.cgi?id=56068
-- v42: ntdll: int_handler has to raise CTRL_C_EVENT
From: Michele Dionisio michele.dionisio@powersoft.com
--- configure.ac | 1 + programs/timeout/Makefile.in | 8 ++ programs/timeout/main.c | 189 +++++++++++++++++++++++++++++++++++ programs/timeout/timeout.h | 29 ++++++ programs/timeout/timeout.rc | 34 +++++++ 5 files changed, 261 insertions(+) create mode 100644 programs/timeout/Makefile.in create mode 100644 programs/timeout/main.c create mode 100644 programs/timeout/timeout.h create mode 100644 programs/timeout/timeout.rc
diff --git a/configure.ac b/configure.ac index 3f8be71bd21..0f2bfd93b9b 100644 --- a/configure.ac +++ b/configure.ac @@ -3514,6 +3514,7 @@ WINE_CONFIG_MAKEFILE(programs/tasklist) WINE_CONFIG_MAKEFILE(programs/tasklist/tests) WINE_CONFIG_MAKEFILE(programs/taskmgr) WINE_CONFIG_MAKEFILE(programs/termsv) +WINE_CONFIG_MAKEFILE(programs/timeout) WINE_CONFIG_MAKEFILE(programs/uninstaller) WINE_CONFIG_MAKEFILE(programs/unlodctr) WINE_CONFIG_MAKEFILE(programs/view) diff --git a/programs/timeout/Makefile.in b/programs/timeout/Makefile.in new file mode 100644 index 00000000000..708cca355ff --- /dev/null +++ b/programs/timeout/Makefile.in @@ -0,0 +1,8 @@ +MODULE = timeout.exe +IMPORTS = user32 + +EXTRADLLFLAGS = -mconsole -municode + +SOURCES = \ + timeout.rc \ + main.c diff --git a/programs/timeout/main.c b/programs/timeout/main.c new file mode 100644 index 00000000000..ce5e69e66ee --- /dev/null +++ b/programs/timeout/main.c @@ -0,0 +1,189 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <synchapi.h> +#include <stdbool.h> +#include <wchar.h> +#include <conio.h> +#include "timeout.h" + +static int WINAPIV timeout_error_wprintf(int msg, ...) +{ + WCHAR msg_buffer[MAXSTRING]; + va_list va_args; + int ret; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + va_start(va_args, msg); + ret = vfwprintf(stderr, msg_buffer, va_args); + va_end(va_args); + return ret; +} + +static int WINAPIV timeout_message(int msg) +{ + WCHAR msg_buffer[MAXSTRING]; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + return fwprintf(stdout, msg_buffer); +} + +static int WINAPIV timeout_wprintf(int msg, ...) +{ + WCHAR msg_buffer[MAXSTRING]; + va_list va_args; + int ret; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + va_start(va_args, msg); + ret = vfwprintf(stdout, msg_buffer, va_args); + va_end(va_args); + return ret; +} + +static volatile bool stop = false; +static int nobreak = 0; + +static BOOL WINAPI ctrl_c_handler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT) + { + stop = true; + return (nobreak == 0) ? FALSE : TRUE; + } + return FALSE; +} + +static BOOL WINAPI parseTime(const WCHAR *in, int *out) +{ + WCHAR *endptr = NULL; + long l_wait_time; + l_wait_time = wcstol(in, &endptr, 10); + if (*endptr != L'\0') { + return FALSE; + } + if ((l_wait_time < -1) || (l_wait_time > 99999)) + { + return FALSE; + } + *out = (int)l_wait_time; + return TRUE; +} + +int __cdecl wmain(int argc, WCHAR *argv[]) +{ + DWORD dummy; + int wait_time = 0; + int wait_time_valid = 0; + int i; + + if (argc <= 1) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + + for (i = 1; i < argc; i++) + { + if (wcscmp(argv[i], L"/?") == 0) + { + timeout_message(STRING_USAGE); + return 0; + } + else if ((wcsicmp(argv[i], L"/t") == 0) && (wait_time_valid == 0)) + { + if ((i + 1) < argc) + { + i++; + if (!parseTime(argv[i], &wait_time)) + { + timeout_error_wprintf(STRING_TIMEOUT_INVALID); + return 1; + } + wait_time_valid = 1; + } + else + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + } + else if (wcsicmp(argv[i], L"/nobreak") == 0) + { + nobreak = 1; + } + else if (wait_time_valid == 0) + { + if (!parseTime(argv[i], &wait_time)) + { + timeout_error_wprintf(STRING_TIMEOUT_INVALID); + return 1; + } + wait_time_valid = 1; + } + else + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + } + + if (wait_time_valid == 0) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + + if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dummy)) + { + timeout_error_wprintf(STRING_ERROR_REDIRECT); + return 101; + } + + if (nobreak) + { + SetConsoleCtrlHandler(ctrl_c_handler, TRUE); + } + + for (i = 0; (wait_time < 0) || (i < wait_time); i++) + { + timeout_wprintf(STRING_WAITING_SINCE, i); + timeout_message((nobreak == 0) ? STRING_PRESS_KEY : STRING_PRESS_CTRLC); + if (stop) + { + break; + } + if ((nobreak == 0) && _kbhit()) + { + break; + } + Sleep(1000); + } + putc('\n', stdout); + + if (stop) + { + return 1; + } + + return 0; +} diff --git a/programs/timeout/timeout.h b/programs/timeout/timeout.h new file mode 100644 index 00000000000..346f69fea56 --- /dev/null +++ b/programs/timeout/timeout.h @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windef.h> + +#define MAXSTRING 8192 + +#define STRING_USAGE 101 +#define STRING_BAD_COMMAND_LINE 102 +#define STRING_WAITING_SINCE 103 +#define STRING_PRESS_CTRLC 104 +#define STRING_PRESS_KEY 105 +#define STRING_ERROR_REDIRECT 106 +#define STRING_TIMEOUT_INVALID 107 diff --git a/programs/timeout/timeout.rc b/programs/timeout/timeout.rc new file mode 100644 index 00000000000..70abf9cd6dc --- /dev/null +++ b/programs/timeout/timeout.rc @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "timeout.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + STRING_USAGE, "Usage: timeout /T <timeout> [</NOBREAK>]\n" + STRING_BAD_COMMAND_LINE, "TIMEOUT: Bad command line\n" + STRING_WAITING_SINCE, "waiting since %d sec. " + STRING_PRESS_CTRLC, "press CTRL+C...\r" + STRING_PRESS_KEY, "press a key to continue...\r" + STRING_ERROR_REDIRECT, "ERROR: not possible to redirect input\n" + STRING_TIMEOUT_INVALID, "ERROR: Timeout value out of range or invalid number. Valid range is -1 to 99999.\n" +}
From: Michele Dionisio michele.dionisio@powersoft.com
--- configure.ac | 1 + programs/timeout/tests/Makefile.in | 4 + programs/timeout/tests/timeout.c | 160 +++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 programs/timeout/tests/Makefile.in create mode 100644 programs/timeout/tests/timeout.c
diff --git a/configure.ac b/configure.ac index 0f2bfd93b9b..5955eb89c78 100644 --- a/configure.ac +++ b/configure.ac @@ -3515,6 +3515,7 @@ WINE_CONFIG_MAKEFILE(programs/tasklist/tests) WINE_CONFIG_MAKEFILE(programs/taskmgr) WINE_CONFIG_MAKEFILE(programs/termsv) WINE_CONFIG_MAKEFILE(programs/timeout) +WINE_CONFIG_MAKEFILE(programs/timeout/tests) WINE_CONFIG_MAKEFILE(programs/uninstaller) WINE_CONFIG_MAKEFILE(programs/unlodctr) WINE_CONFIG_MAKEFILE(programs/view) diff --git a/programs/timeout/tests/Makefile.in b/programs/timeout/tests/Makefile.in new file mode 100644 index 00000000000..a50cdee3062 --- /dev/null +++ b/programs/timeout/tests/Makefile.in @@ -0,0 +1,4 @@ +TESTDLL = timeout.exe + +SOURCES = \ + timeout.c diff --git a/programs/timeout/tests/timeout.c b/programs/timeout/tests/timeout.c new file mode 100644 index 00000000000..b0ddaf07e57 --- /dev/null +++ b/programs/timeout/tests/timeout.c @@ -0,0 +1,160 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include "wine/test.h" + +static DWORD run_timeout_stdin(const char *commandline) +{ + PROCESS_INFORMATION process_info = {0}; + STARTUPINFOA startup_info; + char cmd[4096]; + DWORD exitcode; + + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = NULL; + startup_info.hStdError = NULL; + + sprintf(cmd, "timeout.exe %s", commandline); + + CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &exitcode); + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + return exitcode; +} + +static void test_basic(void) +{ + DWORD exit_code; + + /* No options */ + exit_code = run_timeout_stdin(""); + ok(exit_code == 1, "Unexpected exit_code %lu\n", exit_code); + + /* /? */ + exit_code = run_timeout_stdin("/?"); + ok(exit_code == 0, "Unexpected exit_code %lu\n", exit_code); + + /* /T 1 /NOBREAK */ + exit_code = run_timeout_stdin("/T 1 /NOBREAK"); + ok(exit_code == 0, "Unexpected exit_code %lu\n", exit_code); + + /* /T 1 */ + exit_code = run_timeout_stdin("/T 1"); + ok(exit_code == 0, "Unexpected exit_code %lu\n", exit_code); + + /* 1 /NOBREAK */ + exit_code = run_timeout_stdin("1 /NOBREAK"); + ok(exit_code == 0, "Unexpected exit_code %lu\n", exit_code); + + /* /T 1ab /NOBREAK */ + exit_code = run_timeout_stdin("/T 1ab /NOBREAK"); + ok(exit_code == 1, "Unexpected exit_code %lu\n", exit_code); + + /* /T -3 /NOBREAK */ + exit_code = run_timeout_stdin("/T -3 /NOBREAK"); + ok(exit_code == 1, "Unexpected exit_code %lu\n", exit_code); + + /* /T 10000000 /NOBREAK */ + exit_code = run_timeout_stdin("/T 10000000 /NOBREAK"); + ok(exit_code == 1, "Unexpected exit_code %lu\n", exit_code); +} + +#define run_timeout_ctrlc(a, b) _run_timeout_ctrlc(__FILE__, __LINE__, a, b) +static void _run_timeout_ctrlc(const char *file, int line, const char *option, DWORD exitcode_expected) +{ + PROCESS_INFORMATION process_info = {0}; + STARTUPINFOA startup_info; + char cmd[4096]; + DWORD status, exitcode; + DWORD64 tick_count; + BOOL ret; + + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = NULL; + startup_info.hStdError = NULL; + + SetConsoleCtrlHandler(NULL, FALSE); + sprintf(cmd, "timeout.exe /T 12 %s", option); + ret = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + ok(ret, "CreateProcessA failed: %lu\n", GetLastError()); + + /* wait for process to be started */ + status = WaitForSingleObject(process_info.hProcess, 2000); + ok_(file, line)(status == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx (expecting WAIT_TIMEOUT)\n", status); + + tick_count = GetTickCount64(); + + SetConsoleCtrlHandler(NULL, TRUE); + ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + ok_(file, line)(ret, "GenerateConsoleCtrlEvent failed: %lu\n", GetLastError()); + + status = WaitForSingleObject(process_info.hProcess, INFINITE); + ok_(file, line)(status == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx (expecting WAIT_OBJECT_0)\n", status); + + tick_count = GetTickCount64() - tick_count; + ok_(file, line)(tick_count < 2000, "Process has not been stopped by ctrl-c\n"); + + ret = GetExitCodeProcess(process_info.hProcess, &exitcode); + ok_(file, line)(ret, "GetExitCodeProcess failed\n"); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %ld, got %lx\n", + exitcode_expected, exitcode); +} + +static void test_ctrlc(void) +{ + run_timeout_ctrlc("", STATUS_CONTROL_C_EXIT); + run_timeout_ctrlc("/nobreak", 1); +} + +START_TEST(timeout) +{ + BOOL ret; + /* always run on a separate console, so that: + * - we're sure to have a valid console input handle (no Wine unix console...) + * - we can send ctrl-c events without interfering with parent (winetest...) + */ + ret = FreeConsole(); + ok(ret, "FreeConsole() failed\n"); + SetStdHandle(STD_INPUT_HANDLE, NULL); /* will force reallocation of std handle in AllocConsole() */ + SetStdHandle(STD_OUTPUT_HANDLE, NULL); /* will force reallocation of std handle in AllocConsole() */ + SetStdHandle(STD_ERROR_HANDLE, NULL); /* will force reallocation of std handle in AllocConsole() */ + ret = AllocConsole(); + ok(ret, "AllocConsole() failed\n"); + + if (broken(run_timeout_stdin("/T 0") == 1)) /* Win7 */ + { + win_skip("Skipping tests on Windows 7\n"); + return; + } + + test_basic(); + test_ctrlc(); +}
From: Michele Dionisio michele.dionisio@powersoft.com
--- dlls/kernelbase/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c index 10c8bb25037..26c52ff5a32 100644 --- a/dlls/kernelbase/console.c +++ b/dlls/kernelbase/console.c @@ -527,7 +527,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateConsoleScreenBuffer( DWORD access, DWORD s */ DWORD WINAPI CtrlRoutine( void *arg ) { - DWORD_PTR event = (DWORD_PTR)arg; + DWORD event = (DWORD_PTR)arg; struct ctrl_handler *handler;
if (event == CTRL_C_EVENT)
From: Michele Dionisio michele.dionisio@powersoft.com
--- dlls/kernelbase/console.c | 2 +- server/thread.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c index 26c52ff5a32..47eefb8e6aa 100644 --- a/dlls/kernelbase/console.c +++ b/dlls/kernelbase/console.c @@ -72,7 +72,7 @@ struct ctrl_handler static BOOL WINAPI default_ctrl_handler( DWORD type ) { FIXME( "Terminating process %lx on event %lx\n", GetCurrentProcessId(), type ); - RtlExitUserProcess( 0 ); + RtlExitUserProcess( 666 ); return TRUE; }
diff --git a/server/thread.c b/server/thread.c index 663d4819711..84569343fb7 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1289,7 +1289,7 @@ void kill_thread( struct thread *thread, int violent_death ) /* if it is waiting on the socket, we don't need to send a SIGQUIT */ violent_death = 0; } - kill_console_processes( thread, 0 ); + kill_console_processes( thread, 667 ); abandon_mutexes( thread ); wake_up( &thread->obj, 0 ); if (violent_death) send_thread_signal( thread, SIGQUIT );
From: Michele Dionisio michele.dionisio@powersoft.com
--- dlls/ntdll/unix/signal_arm64.c | 2 +- dlls/ntdll/unix/signal_i386.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 6fd893b390f..43ace303a4c 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -1226,7 +1226,7 @@ static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext )
if (!p__wine_ctrl_routine) return; if (!NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), - p__wine_ctrl_routine, 0 /* CTRL_C_EVENT */, 0, 0, 0, 0, NULL )) + p__wine_ctrl_routine, (void *)STATUS_CONTROL_C_EXIT, 0, 0, 0, 0, NULL )) NtClose( handle ); }
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 8ae7afc769e..10ba17e75b9 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2087,7 +2087,7 @@ static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext )
if (!p__wine_ctrl_routine) return; if (!NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), - p__wine_ctrl_routine, 0 /* CTRL_C_EVENT */, 0, 0, 0, 0, NULL )) + p__wine_ctrl_routine, (void *)STATUS_CONTROL_C_EXIT, 0, 0, 0, 0, NULL )) NtClose( handle ); }
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 537e4e1f60e..44157b11ae5 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2152,7 +2152,7 @@ static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext ) if (p__wine_ctrl_routine) { if (!NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), - p__wine_ctrl_routine, 0 /* CTRL_C_EVENT */, 0, 0, 0, 0, NULL )) + p__wine_ctrl_routine, (void *)STATUS_CONTROL_C_EXIT, 0, 0, 0, 0, NULL )) NtClose( handle ); } leave_handler( ucontext );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=150251
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/kernelbase/console.c:72 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/kernelbase/console.c:72 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: dlls/kernelbase/console.c:72 Task: Patch failed to apply
On Thu Dec 5 09:00:40 2024 +0000, Zhiyi Zhang wrote:
To avoid spamming the GitLab mailing list, you can test the CI pipeline by creating MRs in your private repo.
Sorry, but I'm finish my idea to fix the issue.
On Wed Dec 4 16:47:41 2024 +0000, Michele Dionisio wrote:
the problem continue to exist
do you have any idea? I cleanup this merge rewuest removing the test that not works or rewriting to never use the default crtl-c handler?
On Tue Dec 10 08:03:08 2024 +0000, Michele Dionisio wrote:
do you have any idea? I cleanup this merge rewuest removing the test that not works or rewriting to never use the default crtl-c handler?
The best forward IMO is to mark the failing test with flaky_wine, and keep timeout as it should be (don't try to emulate STATUS_CONTROL_C_EXIT). From examination of the test results, the function behavior is correct (timeout is stopped by ctrl-c cf previous post), it's only the exit code which isn't the correct one. As of today we don't have applications expecting the STATUS_CONTROL_C_EXIT exit code (it's something that has been discovered by the timeout tests). Marking the test as flaky_wine: 1) clearly marks that Wine behavior is not exactly what native does 2) and lists the expected behavior
For the cause, it could be a race between the two threads when exiting the process, and also a race with conhost termination. Or something else. Unfortunately, I can't repro locally. Unless you want to keep investigating, I could take over on this precise point.