From 736bdc28d98c97e01bda9e3da44f90d338fff68a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 26 Sep 2026 14:56:11 +0000 Subject: [PATCH] msys2-runtime: update to 3.6.10-3 Signed-off-by: Johannes Schindelin --- ...r-allow-compiling-with-binutils-2.47.patch | 50 ++ ...n-Unlock-fdtab-before-open_with_arch.patch | 90 ++ ...sole-Fix-regression-in-console-input.patch | 100 +++ ...et-console-mode-only-if-std-in-out-e.patch | 121 +++ ...ix-undesired-mode-change-at-exit-of-.patch | 813 ++++++++++++++++++ ...bort-setting-disable_master_thread-w.patch | 165 ++++ msys2-runtime/PKGBUILD | 28 +- msys2-runtime/msys2-runtime.commit | 2 +- 8 files changed, 1363 insertions(+), 6 deletions(-) create mode 100644 msys2-runtime/0056-dumper-allow-compiling-with-binutils-2.47.patch create mode 100644 msys2-runtime/0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch create mode 100644 msys2-runtime/0058-Cygwin-console-Fix-regression-in-console-input.patch create mode 100644 msys2-runtime/0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch create mode 100644 msys2-runtime/0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch create mode 100644 msys2-runtime/0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch diff --git a/msys2-runtime/0056-dumper-allow-compiling-with-binutils-2.47.patch b/msys2-runtime/0056-dumper-allow-compiling-with-binutils-2.47.patch new file mode 100644 index 00000000000..ee4a8ef3c15 --- /dev/null +++ b/msys2-runtime/0056-dumper-allow-compiling-with-binutils-2.47.patch @@ -0,0 +1,50 @@ +From 154c310bfa2d19e1e1547e866bb624817b8ecf4d Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 28 Jul 2026 10:48:45 +0200 +Subject: [PATCH 56/N] dumper: allow compiling with binutils 2.47 + +In the latest version, binutils' BFD no longer declares a `bfd_boolean`, +and the `bfd_record_phdr()` function accepts plain `bool` parameters +instead. + +Let's accommodate for that to avoid these build failures: + + .../winsup/utils/dumper.cc: In member function 'int dumper::prepare_core_dump()': + .../winsup/utils/dumper.cc:830:7: error: 'bfd_boolean' was not declared in this scope; did you mean 'boolean'? + 830 | bfd_boolean filehdr = 0; + | ^~~~~~~~~~~ + | boolean + .../winsup/utils/dumper.cc:831:18: error: expected ';' before 'phdrs' + 831 | bfd_boolean phdrs = 0; + | ^~~~~~ + | ; + [...] + +Signed-off-by: Johannes Schindelin +--- + winsup/utils/dumper.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/winsup/utils/dumper.cc b/winsup/utils/dumper.cc +index 994f9b6..ac261d0 100644 +--- a/winsup/utils/dumper.cc ++++ b/winsup/utils/dumper.cc +@@ -827,14 +827,14 @@ dumper::prepare_core_dump () + p->section = new_section; + int section_count = 1; + +- bfd_boolean filehdr = 0; +- bfd_boolean phdrs = 0; ++ bool filehdr = false; ++ bool phdrs = false; + + bfd_vma at = 0; +- bfd_boolean valid_at = 0; ++ bool valid_at = false; + + flagword flags = 0; +- bfd_boolean valid_flags = 1; ++ bool valid_flags = true; + + if (p->type == pr_ent_memory) + { diff --git a/msys2-runtime/0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch b/msys2-runtime/0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch new file mode 100644 index 00000000000..6cc508b9bcb --- /dev/null +++ b/msys2-runtime/0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch @@ -0,0 +1,90 @@ +From 8fbd9808447ee78ed485deead9b79cd1e40c07b7 Mon Sep 17 00:00:00 2001 +From: Takashi Yano +Date: Fri, 17 Jul 2026 10:48:11 +0900 +Subject: [PATCH 57/N] Cygwin: open: Unlock fdtab before open_with_arch() + +Since the commit 31bf91f867c5, opening fifo causes a deadlock. This +is because, open_with_arch() for fifo can be blocked until the other +side of the fifo is opened. The commit 31bf91f867c5 moves the creating +cygheap_fdnew before open_with_arch() to address the issue: +https://cygwin.com/pipermail/cygwin/2026-May/259664.html +However, cygheap_fdnew locks fdtab, so open() for the other side of +fifo cannot create cygheap_fdnew until fdtab is unlocked. This is +the cause of the deadlock. + +With this patch, fdtab is unlocked before open_with_arch(), but marked +as used using tentative fhandler. The summary of open() is as follows. + 1) Lock fdtab. + 2) Create new fd. + 3) Mark fd as used using tentative fhandler. + 4) Unlock fdtab. + 5) Call open_with_arch(). + 6) Set final fhandler to fd. + +The important point is that create fd before open_with_arch() to +address https://cygwin.com/pipermail/cygwin/2026-May/259664.html, +but unlock fdtab before open_with_arch() to address +https://cygwin.com/pipermail/cygwin/2026-July/259884.html. + +Fixes: 31bf91f867c5 ("Cygwin: Ensure unused fd available for open()") +Addresses: https://cygwin.com/pipermail/cygwin/2026-July/259884.html +Reported-by: kikairoya +Signed-off-by: Takashi Yano +Reviewed-by: Mark Geisert +(cherry picked from commit 524d75ff73986b263161665af771cc90e55b5e01) +--- + winsup/cygwin/syscalls.cc | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc +index 3ad2039..f04b7d3 100644 +--- a/winsup/cygwin/syscalls.cc ++++ b/winsup/cygwin/syscalls.cc +@@ -1451,6 +1451,7 @@ extern "C" int + open (const char *unix_path, int flags, ...) + { + int res = -1; ++ int fd = -1; + va_list ap; + mode_t mode = 0; + fhandler_base *fh = NULL; +@@ -1550,9 +1551,12 @@ open (const char *unix_path, int flags, ...) + /* Reserve an fdtable entry here, before calling open_with_arch() below. + Otherwise there's a tiny chance of hitting OPEN_MAX further on which + could create a new file without any way for Cygwin to refer to it. */ +- cygheap_fdnew fd; ++ cygheap->fdtab.lock(); ++ fd = cygheap->fdtab.find_unused_handle (); + if (fd < 0) +- __leave; /* errno already set */ ++ __leave; /* errno already set */ ++ cygheap->fdtab[fd] = fh; /* tentative setting to mark as used */ ++ cygheap->fdtab.unlock(); + + if (fh->dev () == FH_PROCESSFD && fh->pc.follow_fd_symlink ()) + { +@@ -1580,13 +1584,23 @@ open (const char *unix_path, int flags, ...) + try_to_bin (fh->pc, fh->get_handle (), DELETE, + FILE_OPEN_FOR_BACKUP_INTENT); + +- fd = fh; ++ cygheap->fdtab.lock (); ++ cygheap->fdtab[fd] = fh; ++ fh->inc_refcnt (); ++ cygheap->fdtab.unlock (); ++ + if (fd <= 2) + set_std_handle (fd); + res = fd; + } + __except (EFAULT) {} + __endtry ++ if (res < 0 && fd >= 0) ++ { ++ cygheap->fdtab.lock (); ++ cygheap->fdtab[fd] = NULL; /* Mark as unused */ ++ cygheap->fdtab.unlock (); ++ } + if (res < 0 && fh) + delete fh; + syscall_printf ("%R = open(%s, %y)", res, unix_path, flags); diff --git a/msys2-runtime/0058-Cygwin-console-Fix-regression-in-console-input.patch b/msys2-runtime/0058-Cygwin-console-Fix-regression-in-console-input.patch new file mode 100644 index 00000000000..6b9c2207f1e --- /dev/null +++ b/msys2-runtime/0058-Cygwin-console-Fix-regression-in-console-input.patch @@ -0,0 +1,100 @@ +From da84778cbc92590c1996c02c68c5a3d3c49e00af Mon Sep 17 00:00:00 2001 +From: Takashi Yano +Date: Mon, 3 Aug 2026 10:41:14 +0900 +Subject: [PATCH 58/N] Cygwin: console: Fix regression in console input + +The commit fac73911f5a0 ("Cygwin: console: Fix typeahead input for +bash") introduced a bug where select() consumes some input chars in +canonical mode, preventing read() from reading them. This is due to +discarding input events when process_input_message() does not return +`input_ok` even if it is called from select(). +The basic idea of that commit was making process_input_message() +not to store processed chars into readahead buffer. This was not +correct because the key input events were processed twice, once by +select() and again by read(). Thus even if that commit worked as +intended, the side effect such as input echo would be applied twice. + +With this patch, process_input_message() handles only the minimum +necessary of input events in both cases, those processed by select() +and those processed by read(). To achieve this behaviour, the function +returns without processing when `input_ready` is already satisfied, +or after it has processed the specified number of chars. + +Addresses: https://cygwin.com/pipermail/cygwin/2026-August/259915.html +Reported-by: Steven Doerfler +Fixes: fac73911f5a0 ("Cygwin: console: Fix typeahead input for bash") +Signed-off-by: Takashi Yano +Revewied-by: Johannes Schindelin +(cherry picked from commit 9807479c90e37913900569d3a8f3b10247bf7860) +--- + winsup/cygwin/fhandler/console.cc | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc +index 3de1bca..857485c 100644 +--- a/winsup/cygwin/fhandler/console.cc ++++ b/winsup/cygwin/fhandler/console.cc +@@ -460,7 +460,7 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp) + } + + WaitForSingleObject (p->input_mutex, mutex_timeout); +- /* Ensure accessing input recored is not disabled. */ ++ /* Ensure accessing input record is not disabled. */ + if (con.disable_master_thread) + { + ReleaseMutex (p->input_mutex); +@@ -1278,7 +1278,6 @@ fhandler_console::input_states + fhandler_console::process_input_message (size_t len) + { + char tmp[60]; +- size_t num_chars = 0; + + if (!shared_console_info[unit]) + return input_error; +@@ -1301,6 +1300,13 @@ fhandler_console::process_input_message (size_t len) + return input_error; + } + ++ /* len == 0 if called from select.cc:peek_console() */ ++ /* This code is reached only when being passed the input_ready check, ++ however, the check was done outside input_mutex. Therefore, another ++ thread may set input_ready after the check. Check it again here. */ ++ if (input_ready && (len == 0 || (get_ttyp ()->ti.c_lflag & ICANON))) ++ return input_ok; ++ + for (i = 0; i < total_read; i ++) + { + DWORD nread = 1; +@@ -1666,7 +1672,6 @@ fhandler_console::process_input_message (size_t len) + } + + num_input_events_processed = i + 1; +- num_chars += nread; + if (toadd) + { + ssize_t ret; +@@ -1685,21 +1690,17 @@ fhandler_console::process_input_message (size_t len) + } + } + /* len == 0 if called from select.cc:peek_console() */ +- if (len && num_chars >= len) ++ if (input_ready && (len == 0 || con_ra.ralen >= len)) + goto out; + } + out: +- if (len == 0) +- /* If len == 0, cancel reading from console input buffer. +- Clear readahead buffer. */ +- eat_readahead (-1); +- /* Discard processed recored. */ ++ /* Discard processed record. */ + DWORD discard_len = min (total_read, i + 1); + /* If input is signalled, do not discard input here because + discard_key_events() is already called from line_edit(). */ + if (stat == input_signalled) + discard_len = 0; +- if (discard_len && (len || stat != input_ok)) ++ if (discard_len) + discard_key_events (discard_len); + return stat; + } diff --git a/msys2-runtime/0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch b/msys2-runtime/0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch new file mode 100644 index 00000000000..361746a6905 --- /dev/null +++ b/msys2-runtime/0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch @@ -0,0 +1,121 @@ +From c892dfe93ec1f5a82d299efca8c0297ff7ade794 Mon Sep 17 00:00:00 2001 +From: Takashi Yano +Date: Thu, 3 Jul 2025 09:06:22 +0900 +Subject: [PATCH 59/N] Cygwin: console: Set console mode only if + std{in,out,err} is console + +Currently, when cygwin app is launched, the console input mode is +set to tty::cygwin, even if the stdin is not a console. However, +it is not necessary because the cygwin app does not use stdin. +This also applies to stdout and stderr. + +With this patch, the console mode is set only when std{in,out,err} +is a console for the cygwin app for better coexistence with non- +cygwin apps. + +This is a prerequisite for the experimental backport of Takashi Yano's +v15 console-mode patch to msys2-3.6.10. The release branch's later, +already-applied suspension-before-mode ordering is preserved. + +(cherry picked from commit bbd3710fc83451426e3a58e5032437ca535fa444) +Assisted-by: GPT-6 +Signed-off-by: Takashi Yano +Signed-off-by: Johannes Schindelin +--- + winsup/cygwin/dtable.cc | 6 +----- + winsup/cygwin/fhandler/console.cc | 36 ++++++++++++++++++++++--------- + 2 files changed, 27 insertions(+), 15 deletions(-) + +diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc +index 6ccc19a..e4cd3bd 100644 +--- a/winsup/cygwin/dtable.cc ++++ b/winsup/cygwin/dtable.cc +@@ -326,11 +326,7 @@ dtable::init_std_file_from_handle (int fd, HANDLE handle) + if (CTTY_IS_VALID (myself->ctty)) + dev.parse (myself->ctty); + else +- { +- dev.parse (FH_CONSOLE); +- CloseHandle (handle); +- handle = INVALID_HANDLE_VALUE; +- } ++ dev.parse (FH_CONSOLE); + } + else if (GetCommState (handle, &dcb)) + /* FIXME: Not right - assumes ttyS0 */ +diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc +index 857485c..e19e124 100644 +--- a/winsup/cygwin/fhandler/console.cc ++++ b/winsup/cygwin/fhandler/console.cc +@@ -953,8 +953,10 @@ fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p) + /* conmode can be tty::restore when non-cygwin app is + exec'ed from login shell. */ + tty::cons_mode conmode = cons_mode_on_close (p); +- set_output_mode (conmode, ti, p); +- set_input_mode (conmode, ti, p); ++ if (con.curr_output_mode != conmode) ++ set_output_mode (conmode, ti, p); ++ if (con.curr_input_mode != conmode) ++ set_input_mode (conmode, ti, p); + } + + /* Return the tty structure associated with a given tty number. If the +@@ -1947,13 +1949,24 @@ fhandler_console::open (int flags, mode_t) + setenv ("TERM", "cygwin", 1); + } + +- if (con.curr_input_mode != tty::cygwin) ++ HANDLE h_in = GetStdHandle (STD_INPUT_HANDLE); ++ HANDLE h_out = GetStdHandle (STD_OUTPUT_HANDLE); ++ HANDLE h_err = GetStdHandle (STD_ERROR_HANDLE); ++ ++ DWORD dummy; ++ bool in_is_console = GetConsoleMode (h_in, &dummy); ++ bool out_is_console = ++ GetConsoleMode (h_out, &dummy) || GetConsoleMode (h_err, &dummy); ++ if (in_is_console) ++ CloseHandle (h_in); ++ ++ if (in_is_console && con.curr_input_mode != tty::cygwin) + { + prev_input_mode_backup = con.prev_input_mode; + GetConsoleMode (get_handle (), &con.prev_input_mode); + set_input_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); + } +- if (con.curr_output_mode != tty::cygwin) ++ if (out_is_console && con.curr_output_mode != tty::cygwin) + { + prev_output_mode_backup = con.prev_output_mode; + GetConsoleMode (get_output_handle (), &con.prev_output_mode); +@@ -2069,13 +2082,14 @@ fhandler_console::close (int flag) + + acquire_output_mutex (mutex_timeout); + +- if (shared_console_info[unit] && con.curr_input_mode != tty::restore +- && (dev_t) myself->ctty == get_device () ++ if (shared_console_info[unit] && (dev_t) myself->ctty == get_device () + && cons_mode_on_close (&handle_set) == tty::restore) + { + set_disable_master_thread (true, this); +- set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set); +- set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set); ++ if (con.curr_output_mode != tty::restore) ++ set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set); ++ if (con.curr_input_mode != tty::restore) ++ set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set); + } + + if (shared_console_info[unit] && con.owner == GetCurrentProcessId ()) +@@ -2296,8 +2310,10 @@ int + fhandler_console::tcsetattr (int a, struct termios const *t) + { + get_ttyp ()->ti = *t; +- set_input_mode (tty::cygwin, t, &handle_set); +- set_output_mode (tty::cygwin, t, &handle_set); ++ if (con.curr_input_mode == tty::cygwin) ++ set_input_mode (tty::cygwin, t, &handle_set); ++ if (con.curr_output_mode == tty::cygwin) ++ set_output_mode (tty::cygwin, t, &handle_set); + return 0; + } + diff --git a/msys2-runtime/0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch b/msys2-runtime/0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch new file mode 100644 index 00000000000..51bcfed60e9 --- /dev/null +++ b/msys2-runtime/0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch @@ -0,0 +1,813 @@ +From 92c1e00a38ab093b269cff9cdede90c2a7beba23 Mon Sep 17 00:00:00 2001 +From: Takashi Yano +Date: Sat, 19 Sep 2026 09:46:59 +0900 +Subject: [PATCH 60/N] Cygwin: console: Fix undesired mode change at exit of + non-cygwin apps + +Previously, if two non-cygwin apps are started and one of them +exits first, the other one lost appropriate console mode, since +the first one restored it to tty::cygwin. This patch counts the +active console process whose pgid is pgid of the tty and if the +result is zero (means the last non-cygwin foreground process), +restore console mode. To avoid race issue between apps modifying +console mode simultaneously, this patch also introduce a mutex +named `cons_mode_mutex`. + +Known limitation: +In the case of non-overlayed spawn, there still exists a small +window in which another non-cygwin process may restore tty::cygwin +mode even though new non-cygwin app is about to start. + +In addition: +Avoid the cleanup/startup deadlock: cleanup can hold `cons_mode_mutex` +while waiting for a console master that a new owner cannot start until +acquiring that mutex. Startup must precede the mode-mutex wait without +changing the handshake or mode serialization. + +Backport the complete upstream v18 patch after the prerequisite: +https://inbox.sourceware.org/cygwin-patches/20260919004709.26360-1-takashi.yano@nifty.ne.jp/ +Adapt only the three existing msys2-3.6.10 preimage/context spellings. + +Fixes: 48285aa36c2c ("Cygwin: console: Fix handling of Ctrl-S in Win7.") +Co-authored-by: Johannes Schindelin +Reviewed-by: Johannes Schindelin +Assisted-by: GPT-6 +Signed-off-by: Takashi Yano +Signed-off-by: Johannes Schindelin +--- + winsup/cygwin/fhandler/console.cc | 245 ++++++++++++++++++++---- + winsup/cygwin/fhandler/termios.cc | 40 +++- + winsup/cygwin/local_includes/fhandler.h | 10 +- + winsup/cygwin/select.cc | 20 +- + winsup/cygwin/spawn.cc | 44 +++-- + 5 files changed, 281 insertions(+), 78 deletions(-) + +diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc +index e19e124..9e87ff5 100644 +--- a/winsup/cygwin/fhandler/console.cc ++++ b/winsup/cygwin/fhandler/console.cc +@@ -424,6 +424,12 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp) + cygwait (40); + continue; + } ++ if (con.need_win32_input_mode_fix) ++ { ++ con.master_thread_suspended = false; ++ cygwait (40); ++ continue; ++ } + + acquire_attach_mutex (mutex_timeout); + GetNumberOfConsoleInputEvents (p->input_handle, &total_read); +@@ -799,6 +805,8 @@ fhandler_console::setup () + con.num_processed = 0; + con.curr_input_mode = tty::restore; + con.curr_output_mode = tty::restore; ++ con.need_win32_input_mode_fix = false; ++ con.is_processed_input = false; + } + } + +@@ -835,6 +843,44 @@ fhandler_console::rabuflen () + static DWORD prev_input_mode_backup; + static DWORD prev_output_mode_backup; + ++/* Even under cons_mode_mutex, only need_win32_input_mode_fix can be ++ modified by another thread. This function sets or clears the ++ ENABLE_PROCESSED_INPUT flag to reflect the current value of ++ need_win32_input_mode_fix. */ ++void ++fhandler_console::fix_input_mode_if_necessary () ++{ ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); ++ if (con.curr_input_mode != tty::cygwin) ++ { ++ ReleaseMutex (cons_mode_mutex); ++ return; ++ } ++ bool need_processed_input = ++ con.master_thread_suspended || con.disable_master_thread ++ || con.need_win32_input_mode_fix; ++ if (need_processed_input == con.is_processed_input) ++ { ++ ReleaseMutex (cons_mode_mutex); ++ return; ++ } ++ WaitForSingleObject (input_mutex, mutex_timeout); ++ acquire_attach_mutex (mutex_timeout); ++ DWORD resume_pid = attach_console (con.owner); ++ DWORD flags; ++ GetConsoleMode (get_handle (), &flags); ++ if (need_processed_input) ++ flags |= ENABLE_PROCESSED_INPUT; ++ else ++ flags &= ~ENABLE_PROCESSED_INPUT; ++ con.is_processed_input = need_processed_input; ++ SetConsoleMode (get_handle (), flags); ++ detach_console (resume_pid, con.owner); ++ release_attach_mutex (); ++ ReleaseMutex (input_mutex); ++ ReleaseMutex (cons_mode_mutex); ++} ++ + /* The function set_{in,out}put_mode() should be static so that they + can be called even after the fhandler_console instance is deleted. */ + void +@@ -857,7 +903,10 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, + break; + case tty::cygwin: + flags |= ENABLE_WINDOW_INPUT; +- if (con.master_thread_suspended || con.disable_master_thread) ++ con.is_processed_input = ++ con.master_thread_suspended || con.disable_master_thread ++ || con.need_win32_input_mode_fix; ++ if (con.is_processed_input) + flags |= ENABLE_PROCESSED_INPUT; + if (wincap.has_con_24bit_colors () && !con_is_legacy) + flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; +@@ -879,6 +928,7 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, + } + con.curr_input_mode = m; + SetConsoleMode (p->input_handle, flags); ++ con.is_processed_input = (flags & ENABLE_PROCESSED_INPUT) != 0; + if (!(oflags & ENABLE_VIRTUAL_TERMINAL_INPUT) + && (flags & ENABLE_VIRTUAL_TERMINAL_INPUT) + && con.cursor_key_app_mode) +@@ -935,16 +985,92 @@ fhandler_console::setup_for_non_cygwin_app () + console mode. */ + if (get_ttyp ()->getpgid () == myself->pgid) + { ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); + set_disable_master_thread (true, this); + set_input_mode (tty::native, &tc ()->ti, get_handle_set ()); + set_output_mode (tty::native, &tc ()->ti, get_handle_set ()); ++ ReleaseMutex (cons_mode_mutex); ++ } ++} ++ ++/* Return values ++ 0: not exist ++ 1: exist ++ -1: error */ ++int ++fhandler_console::active_non_cygwin_apps_exist (pid_t pgid) ++{ ++ tmp_pathbuf tp; ++ DWORD *list = (DWORD *) tp.c_get (); ++ const DWORD buf_size = NT_MAX_PATH / sizeof (DWORD); ++ ++ DWORD buf_size1 = 1; ++ DWORD num; ++ /* The buffer of too large size does not seem to be expected by new condrv. ++ https://github.com/microsoft/terminal/issues/18264#issuecomment-2515448548 ++ Use the minimum buffer size in the loop. */ ++ while ((num = GetConsoleProcessList (list, buf_size1)) > buf_size1) ++ { ++ if (num > buf_size) ++ return -1; ++ buf_size1 = num; + } ++ if (num == 0) ++ return -1; ++ ++ /* Last one is the oldest. */ ++ /* https://github.com/microsoft/terminal/issues/95 */ ++ /* Assuming that newer processes are more likely to be non-cygwin. */ ++ for (DWORD i = 0; i < num; i++) ++ { ++ DWORD my_pid = myself->exec_dwProcessId ? : myself->dwProcessId; ++ if (list[i] == my_pid) ++ continue; ++ pid_t pid = cygwin_pid (list[i]); ++ if (pid == 0) ++ continue; ++ pinfo p (pid); ++ if (!!p && p->pgid == pgid && ISSTATE (p, PID_NOTCYGWIN)) ++ return 1; ++ } ++ return 0; + } + + void + fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p) + { + const _minor_t unit = p->unit; ++ pid_t pgid = shared_console_info[unit] ? ++ shared_console_info[unit]->tty_min_state.getpgid () : 0; ++ ++ WaitForSingleObject (p->cons_mode_mutex, mutex_timeout); ++ tty::cons_mode conmode = cons_mode_on_close (p); ++ if (con.curr_input_mode == conmode && con.curr_output_mode == conmode ++ && con.disable_master_thread == (con.owner == GetCurrentProcessId ())) ++ { ++ ReleaseMutex (p->cons_mode_mutex); ++ return; ++ } ++ switch (active_non_cygwin_apps_exist (pgid)) ++ { ++ case -1: /* Error */ ++ /* In case of an error, do not perform the cleanup, since the ++ cygwin app has a chnce to restore console mode in bg_check() ++ called from read()/write(). So give a priority to non-cygwin ++ apps here that may exist. */ ++ system_printf ("Checking for existence of non-cygwin app failed."); ++ if (con.owner == GetCurrentProcessId ()) ++ /* This process is supposed to be the last non-cygwin process */ ++ break; ++ fallthrough; ++ case 1: /* Exist */ ++ ReleaseMutex (p->cons_mode_mutex); ++ return; ++ case 0: /* Not exist */ ++ default: ++ break; ++ } ++ + termios dummy = {0, }; + termios *ti = shared_console_info[unit] ? + &(shared_console_info[unit]->tty_min_state.ti) : &dummy; +@@ -952,11 +1078,11 @@ fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p) + set_disable_master_thread (con.owner == GetCurrentProcessId ()); + /* conmode can be tty::restore when non-cygwin app is + exec'ed from login shell. */ +- tty::cons_mode conmode = cons_mode_on_close (p); + if (con.curr_output_mode != conmode) + set_output_mode (conmode, ti, p); + if (con.curr_input_mode != conmode) + set_input_mode (conmode, ti, p); ++ ReleaseMutex (p->cons_mode_mutex); + } + + /* Return the tty structure associated with a given tty number. If the +@@ -1013,6 +1139,10 @@ fhandler_console::setup_io_mutex (void) + if (res == WAIT_OBJECT_0) + release_output_mutex (); + ++ shared_name (buf, "cygcons.cons_mode.mutex", get_minor ()); ++ if (!cons_mode_mutex) ++ cons_mode_mutex = CreateMutex (&sec_none, FALSE, buf); ++ + extern HANDLE attach_mutex; + if (!attach_mutex) + attach_mutex = CreateMutex (&sec_none_nih, FALSE, NULL); +@@ -1140,13 +1270,13 @@ fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event) + || con.use_mouse >= 3)); + } + +- + bg_check_types + fhandler_console::bg_check (int sig, bool dontsignal) + { + /* Setting-up console mode for cygwin app. This is necessary if the + cygwin app and other non-cygwin apps are started simultaneously + in the same process group. */ ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); + if (sig == SIGTTIN && con.curr_input_mode != tty::cygwin) + { + set_disable_master_thread (false, this); +@@ -1154,6 +1284,7 @@ fhandler_console::bg_check (int sig, bool dontsignal) + } + if (sig == SIGTTOU && con.curr_output_mode != tty::cygwin) + set_output_mode (tty::cygwin, &tc ()->ti, get_handle_set ()); ++ ReleaseMutex (cons_mode_mutex); + + return fhandler_termios::bg_check (sig, dontsignal); + } +@@ -1197,6 +1328,7 @@ wait_retry: + case WAIT_TIMEOUT: + if (copied_chars) + { ++ fix_input_mode_if_necessary (); /* for win32_input_mode */ + buflen = copied_chars; + return; + } +@@ -1230,12 +1362,14 @@ wait_retry: + goto err; + case input_processing: + release_input_mutex (); ++ fix_input_mode_if_necessary (); /* for win32_input_mode */ + continue; + case input_ok: /* input ready */ + break; + case input_signalled: /* signalled */ + case input_winch: + release_input_mutex (); ++ fix_input_mode_if_necessary (); /* for win32_input_mode */ + if (global_sigs[get_ttyp ()->last_sig].sa_flags & SA_RESTART) + continue; + goto sig_exit; +@@ -1257,6 +1391,8 @@ wait_retry: + input_ready = false; + release_input_mutex (); + ++ fix_input_mode_if_necessary (); /* for win32_input_mode */ ++ + if (buflen > copied_chars && !(get_ttyp ()->ti.c_lflag & ICANON) + && copied_chars < get_ttyp ()->ti.c_cc[VMIN]) + goto read_more; +@@ -1960,22 +2096,8 @@ fhandler_console::open (int flags, mode_t) + if (in_is_console) + CloseHandle (h_in); + +- if (in_is_console && con.curr_input_mode != tty::cygwin) +- { +- prev_input_mode_backup = con.prev_input_mode; +- GetConsoleMode (get_handle (), &con.prev_input_mode); +- set_input_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); +- } +- if (out_is_console && con.curr_output_mode != tty::cygwin) +- { +- prev_output_mode_backup = con.prev_output_mode; +- GetConsoleMode (get_output_handle (), &con.prev_output_mode); +- set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); +- } +- +- debug_printf ("opened conin$ %p, conout$ %p", get_handle (), +- get_output_handle ()); +- ++ /* Another process may hold cons_mode_mutex while waiting for the ++ master thread to acknowledge a state change. */ + if (GetCurrentProcessId () == con.owner) + { + if (GetModuleHandle ("ConEmuHk64.dll")) +@@ -1993,6 +2115,25 @@ fhandler_console::open (int flags, mode_t) + debug_printf ("Failed to create thread_sync_event %08x", + GetLastError ()); + } ++ ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); ++ if (in_is_console && con.curr_input_mode != tty::cygwin) ++ { ++ prev_input_mode_backup = con.prev_input_mode; ++ GetConsoleMode (get_handle (), &con.prev_input_mode); ++ set_input_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); ++ } ++ if (out_is_console && con.curr_output_mode != tty::cygwin) ++ { ++ prev_output_mode_backup = con.prev_output_mode; ++ GetConsoleMode (get_output_handle (), &con.prev_output_mode); ++ set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); ++ } ++ ReleaseMutex (cons_mode_mutex); ++ ++ debug_printf ("opened conin$ %p, conout$ %p", get_handle (), ++ get_output_handle ()); ++ + return 1; + } + +@@ -2055,6 +2196,7 @@ fhandler_console::open_setup (int flags) + handle_set.output_handle = get_output_handle (); + handle_set.input_mutex = input_mutex; + handle_set.output_mutex = output_mutex; ++ handle_set.cons_mode_mutex = cons_mode_mutex; + handle_set.unit = unit; + } + return fhandler_base::open_setup (flags); +@@ -2064,6 +2206,7 @@ void + fhandler_console::post_open_setup (int fd) + { + /* Setting-up console mode for cygwin app started from non-cygwin app. */ ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); + if (fd == 0) + { + set_disable_master_thread (false, this); +@@ -2071,6 +2214,7 @@ fhandler_console::post_open_setup (int fd) + } + else if (fd == 1 || fd == 2) + set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); ++ ReleaseMutex (cons_mode_mutex); + + fhandler_base::post_open_setup (fd); + } +@@ -2080,18 +2224,20 @@ fhandler_console::close (int flag) + { + debug_printf ("closing: %p, %p", get_handle (), get_output_handle ()); + +- acquire_output_mutex (mutex_timeout); +- + if (shared_console_info[unit] && (dev_t) myself->ctty == get_device () + && cons_mode_on_close (&handle_set) == tty::restore) + { ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); + set_disable_master_thread (true, this); + if (con.curr_output_mode != tty::restore) + set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set); + if (con.curr_input_mode != tty::restore) + set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set); ++ ReleaseMutex (cons_mode_mutex); + } + ++ acquire_output_mutex (mutex_timeout); ++ + if (shared_console_info[unit] && con.owner == GetCurrentProcessId ()) + { + if (master_thread_started) +@@ -2146,6 +2292,8 @@ fhandler_console::close (int flag) + input_mutex = NULL; + CloseHandle (output_mutex); + output_mutex = NULL; ++ CloseHandle (cons_mode_mutex); ++ cons_mode_mutex = NULL; + + pcon_hand_over_proc (); + +@@ -2195,8 +2343,8 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) + release_output_mutex (); + return 0; + case TIOCSWINSZ: +- bg_check (SIGTTOU); + release_output_mutex (); ++ bg_check (SIGTTOU); + return 0; + case KDGKBMETA: + *(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX; +@@ -2310,10 +2458,12 @@ int + fhandler_console::tcsetattr (int a, struct termios const *t) + { + get_ttyp ()->ti = *t; ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); + if (con.curr_input_mode == tty::cygwin) + set_input_mode (tty::cygwin, t, &handle_set); + if (con.curr_output_mode == tty::cygwin) + set_output_mode (tty::cygwin, t, &handle_set); ++ ReleaseMutex (cons_mode_mutex); + return 0; + } + +@@ -3080,12 +3230,7 @@ fhandler_console::char_command (char c) + if (con.args[i] == 1) /* DECCKM */ + con.cursor_key_app_mode = (c == 'h'); + if (con.args[i] == 9001) /* win32-input-mode (https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md) */ +- { +- set_disable_master_thread (c == 'h', this); +- if (con.curr_input_mode == tty::cygwin) +- set_input_mode (tty::cygwin, +- &tc ()->ti, get_handle_set ()); +- } ++ con.need_win32_input_mode_fix = (c == 'h'); + } + /* Call fix_tab_position() if screen has been alternated. */ + if (need_fix_tab_position) +@@ -3934,6 +4079,18 @@ fhandler_console::write (const void *vsrc, size_t len) + + push_process_state process_state (PID_TTYOU); + ++ ssize_t ret = raw_write (vsrc, len); ++ ++ fix_input_mode_if_necessary (); /* for win32_input_mode */ ++ ++ syscall_printf ("%ld = fhandler_console::write(...)", len); ++ ++ return ret; ++} ++ ++ssize_t ++fhandler_console::raw_write (const void *vsrc, size_t len) ++{ + acquire_output_mutex (mutex_timeout); + + /* Run and check for ansi sequences */ +@@ -4267,18 +4424,13 @@ fhandler_console::write (const void *vsrc, size_t len) + } + release_output_mutex (); + +- syscall_printf ("%ld = fhandler_console::write(...)", len); +- + return len; + } + + void + fhandler_console::doecho (const void *str, DWORD len) + { +- bool stopped = get_ttyp ()->output_stopped; +- get_ttyp ()->output_stopped = false; +- write (str, len); +- get_ttyp ()->output_stopped = stopped; ++ raw_write (str, len); + } + + static const struct { +@@ -4416,10 +4568,13 @@ fhandler_console::set_console_mode_to_native () + fhandler_console *cons = (fhandler_console *) (fhandler_base *) cfd; + if (cons->get_device () == cons->tc ()->getntty ()) + { ++ const fhandler_console::handle_set_t *p = cons->get_handle_set (); ++ WaitForSingleObject (p->cons_mode_mutex, mutex_timeout); + set_disable_master_thread (true, cons); + termios *cons_ti = &cons->tc ()->ti; +- set_input_mode (tty::native, cons_ti, cons->get_handle_set ()); +- set_output_mode (tty::native, cons_ti, cons->get_handle_set ()); ++ set_input_mode (tty::native, cons_ti, p); ++ set_output_mode (tty::native, cons_ti, p); ++ ReleaseMutex (p->cons_mode_mutex); + break; + } + } +@@ -4476,8 +4631,17 @@ ContinueDebugEvent_Hooked + static FARPROC + GetProcAddress_Hooked (HMODULE h, LPCSTR n) + { +- if (strcmp(n, "RequestTermConnector") == 0) +- fhandler_console::set_disable_master_thread (true); ++ if (cygheap->ctty && strcmp (n, "RequestTermConnector") == 0) ++ { ++ char buf[MAX_PATH]; ++ const _minor_t unit = cygheap->ctty->get_minor (); ++ shared_name (buf, "cygcons.cons_mode.mutex", unit); ++ HANDLE cons_mode_mutex = CreateMutex (&sec_none, FALSE, buf); ++ WaitForSingleObject (cons_mode_mutex, mutex_timeout); ++ fhandler_console::set_disable_master_thread (true); ++ ReleaseMutex (cons_mode_mutex); ++ CloseHandle (cons_mode_mutex); ++ } + return GetProcAddress_Orig (h, n); + } + +@@ -4743,6 +4907,9 @@ fhandler_console::get_duplicated_handle_set (handle_set_t *p) + DuplicateHandle (GetCurrentProcess (), output_mutex, + GetCurrentProcess (), &p->output_mutex, + 0, FALSE, DUPLICATE_SAME_ACCESS); ++ DuplicateHandle (GetCurrentProcess (), cons_mode_mutex, ++ GetCurrentProcess (), &p->cons_mode_mutex, ++ 0, FALSE, DUPLICATE_SAME_ACCESS); + p->unit = unit; + } + +@@ -4759,6 +4926,8 @@ fhandler_console::close_handle_set (handle_set_t *p) + p->input_mutex = NULL; + CloseHandle (p->output_mutex); + p->output_mutex = NULL; ++ CloseHandle (p->cons_mode_mutex); ++ p->cons_mode_mutex = NULL; + } + + bool +diff --git a/winsup/cygwin/fhandler/termios.cc b/winsup/cygwin/fhandler/termios.cc +index e16b296..423ca9e 100644 +--- a/winsup/cygwin/fhandler/termios.cc ++++ b/winsup/cygwin/fhandler/termios.cc +@@ -830,20 +830,37 @@ void + fhandler_termios::spawn_worker::cleanup () + { + if (ptys_need_cleanup) +- fhandler_pty_slave::cleanup_for_non_cygwin_app (&ptys_handle_set, +- ptys_ttyp, stdin_is_ptys); ++ { ++ fhandler_pty_slave::cleanup_for_non_cygwin_app (&ptys_handle_set, ++ ptys_ttyp, stdin_is_ptys); ++ fhandler_pty_slave::close_handle_set (&ptys_handle_set); ++ ptys_need_cleanup = false; ++ } + if (cons_need_cleanup) +- fhandler_console::cleanup_for_non_cygwin_app (&cons_handle_set); +- close_handle_set (); ++ { ++ fhandler_console::cleanup_for_non_cygwin_app (&cons_handle_set); ++ fhandler_console::close_handle_set (&cons_handle_set); ++ cons_need_cleanup = false; ++ } ++} ++ ++bool ++fhandler_termios::spawn_worker::is_attaching (DWORD pid) ++{ ++ return !!fhandler_termios::get_console_process_id (pid, true); + } + + void +-fhandler_termios::spawn_worker::close_handle_set () ++fhandler_termios::spawn_worker::wait_for_resume_if_necessary ++ (path_conv &pc, PROCESS_INFORMATION &pi) + { +- if (ptys_need_cleanup) +- fhandler_pty_slave::close_handle_set (&ptys_handle_set); +- if (cons_need_cleanup) +- fhandler_console::close_handle_set (&cons_handle_set); ++ if (is_attaching (myself->dwProcessId) && is_console_app (pc)) ++ { ++ DWORD t0 = GetTickCount (); ++ while (GetTickCount () - t0 < 40 && !is_attaching (pi.dwProcessId) ++ && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) ++ Sleep (1); ++ } + } + + void +@@ -916,7 +933,10 @@ fhandler_termios::get_console_process_id (DWORD pid, bool match, + } + else + { +- pinfo p (cygwin_pid (list[i])); ++ pid_t cygpid = cygwin_pid (list[i]); ++ if (cygpid == 0) ++ continue; ++ pinfo p (cygpid); + if (nat && !!p && !ISSTATE(p, PID_NOTCYGWIN)) + continue; + if (!!p && p->exec_dwProcessId) +diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h +index 0a3e35d..d7edf03 100644 +--- a/winsup/cygwin/local_includes/fhandler.h ++++ b/winsup/cygwin/local_includes/fhandler.h +@@ -2022,6 +2022,7 @@ class fhandler_termios: public fhandler_base + HANDLE output_handle; + HANDLE input_mutex; + HANDLE output_mutex; ++ HANDLE cons_mode_mutex; + _minor_t unit; + }; + class spawn_worker +@@ -2041,7 +2042,8 @@ class fhandler_termios: public fhandler_base + bool nopcon, bool reset_sendsig, const WCHAR *envblock); + bool need_cleanup () { return ptys_need_cleanup || cons_need_cleanup; } + void cleanup (); +- void close_handle_set (); ++ bool is_attaching (DWORD pid); ++ void wait_for_resume_if_necessary (path_conv &, PROCESS_INFORMATION &); + }; + }; + +@@ -2156,6 +2158,8 @@ class dev_console + bool master_thread_suspended; + int num_processed; /* Number of input events in the current input buffer + already processed by cons_master_thread(). */ ++ bool need_win32_input_mode_fix; ++ bool is_processed_input; + + inline UINT get_console_cp (); + DWORD con_to_str (char *d, int dlen, WCHAR w); +@@ -2198,6 +2202,7 @@ private: + static console_state *shared_console_info[MAX_CONS_DEV + 1]; + static bool invisible_console; + HANDLE input_mutex, output_mutex; ++ HANDLE cons_mode_mutex; + handle_set_t handle_set; + _minor_t unit; + size_t num_input_events_processed; +@@ -2266,6 +2271,7 @@ private: + + void read (void *ptr, size_t& len); + ssize_t write (const void *ptr, size_t len); ++ ssize_t raw_write (const void *ptr, size_t len); + void doecho (const void *str, DWORD len); + int close (int flag = -1); + static bool exists () +@@ -2363,6 +2369,7 @@ private: + void wpbuf_send (); + int fstat (struct stat *buf); + void discard_key_events (size_t n); ++ void fix_input_mode_if_necessary (); + + class console_unit + { +@@ -2376,6 +2383,7 @@ private: + void setup_pcon_hand_over (); + static void pcon_hand_over_proc (); + static tty::cons_mode cons_mode_on_close (handle_set_t *); ++ static int active_non_cygwin_apps_exist (pid_t pgid); + + friend tty_min * tty_list::get_cttyp (); + }; +diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc +index b720834..37b28d5 100644 +--- a/winsup/cygwin/select.cc ++++ b/winsup/cygwin/select.cc +@@ -1154,34 +1154,36 @@ peek_console (select_record *me, bool) + HANDLE h; + set_handle_or_return_if_not_open (h, me); + +- fh->acquire_input_mutex (mutex_timeout); + while (!fh->input_ready && !fh->get_cons_readahead_valid ()) + { + if (fh->bg_check (SIGTTIN, true) <= bg_eof) +- { +- fh->release_input_mutex (); +- return me->read_ready = true; +- } ++ return me->read_ready = true; + else + { ++ fh->acquire_input_mutex (mutex_timeout); + acquire_attach_mutex (mutex_timeout); + DWORD resume_pid = fh->attach_console (fh->get_owner ()); + BOOL r = PeekConsoleInputW (h, &irec, 1, &events_read); + fh->detach_console (resume_pid, fh->get_owner ()); + release_attach_mutex (); + if (!r || !events_read) +- break; ++ { ++ fh->release_input_mutex (); ++ break; ++ } + } +- if (fhandler_console::input_winch == fh->process_input_message (0) ++ fhandler_console::input_states ret = fh->process_input_message (0); ++ fh->release_input_mutex (); ++ fh->fix_input_mode_if_necessary (); /* for win32_input_mode */ ++ ++ if (ret == fhandler_console::input_winch + && global_sigs[SIGWINCH].sa_handler != SIG_IGN + && global_sigs[SIGWINCH].sa_handler != SIG_DFL) + { + set_sig_errno (EINTR); +- fh->release_input_mutex (); + return -1; + } + } +- fh->release_input_mutex (); + if (fh->input_ready || fh->get_cons_readahead_valid ()) + return me->read_ready = true; + +diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc +index 9408903..7fcc33f 100644 +--- a/winsup/cygwin/spawn.cc ++++ b/winsup/cygwin/spawn.cc +@@ -607,6 +607,14 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + PROCESS_QUERY_LIMITED_INFORMATION)) + sa = &sec_none_nih; + ++ if (!real_path.iscygexec () && mode == _P_OVERLAY) ++ { ++ LONG pidflags = PID_NOTCYGWIN; ++ if (c_flags & CREATE_NEW_PROCESS_GROUP) ++ pidflags |= PID_NEW_PG; ++ InterlockedOr ((LONG *) &myself->process_state, pidflags); ++ } ++ + int fileno_stdin = in__stdin < 0 ? 0 : in__stdin; + int fileno_stdout = in__stdout < 0 ? 1 : in__stdout; + int fileno_stderr = 2; +@@ -634,14 +642,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + up on ruid. The new process will have ruid == euid. */ + ::cygheap->user.deimpersonate (); + +- if (!real_path.iscygexec () && mode == _P_OVERLAY) +- { +- LONG pidflags = PID_NOTCYGWIN; +- if (c_flags & CREATE_NEW_PROCESS_GROUP) +- pidflags |= PID_NEW_PG; +- InterlockedOr ((LONG *) &myself->process_state, pidflags); +- } +- + cygpid = (mode != _P_OVERLAY) ? create_cygwin_pid () : myself->pid; + + cygheap->lock (); +@@ -783,6 +783,18 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + /* Name the handle similarly to proc_subproc. */ + ProtectHandle1 (pi.hProcess, childhProc); + ++ /* Start the child running for non-cygwin process*/ ++ if (!iscygwin () && (c_flags & CREATE_SUSPENDED)) ++ { ++ /* Inject a non-inheritable wr_proc_pipe handle into child so that we ++ can accurately track when the child exits without keeping this ++ process waiting around for it to exit. */ ++ DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, ++ NULL, 0, false, DUPLICATE_SAME_ACCESS); ++ ResumeThread (pi.hThread); ++ term_spawn_worker.wait_for_resume_if_necessary (real_path, pi); ++ } ++ + if (mode == _P_OVERLAY) + { + myself->dwProcessId = pi.dwProcessId; +@@ -856,18 +868,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + } + } + +- /* Start the child running */ +- if (c_flags & CREATE_SUSPENDED) ++ /* Start the child running for cygwin process*/ ++ if (iscygwin () && (c_flags & CREATE_SUSPENDED)) + { +- /* Inject a non-inheritable wr_proc_pipe handle into child so that we +- can accurately track when the child exits without keeping this +- process waiting around for it to exit. */ +- if (!iscygwin ()) +- DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, NULL, +- 0, false, DUPLICATE_SAME_ACCESS); + ResumeThread (pi.hThread); +- if (iscygwin ()) +- strace.write_childpid (pi.dwProcessId); ++ strace.write_childpid (pi.dwProcessId); + } + ForceCloseHandle (pi.hThread); + +@@ -914,7 +919,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + prev_sigExeced = + InterlockedCompareExchange (&sigExeced, 0, prev_sigExeced); + term_spawn_worker.cleanup (); +- term_spawn_worker.close_handle_set (); + } + /* Make sure that ctrl_c_handler() is not on going. Calling + init_console_handler(false) locks until returning from +@@ -952,7 +956,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, + res = -1; + } + __endtry +- term_spawn_worker.close_handle_set (); ++ term_spawn_worker.cleanup (); + this->cleanup (); + if (envblock) + free (envblock); diff --git a/msys2-runtime/0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch b/msys2-runtime/0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch new file mode 100644 index 00000000000..8651d557a9a --- /dev/null +++ b/msys2-runtime/0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch @@ -0,0 +1,165 @@ +From c770e1b9fa537fff9287c1fd40ebc84ac498fcb6 Mon Sep 17 00:00:00 2001 +From: Takashi Yano +Date: Thu, 24 Sep 2026 07:32:22 +0900 +Subject: [PATCH 61/N] Cygwin: console: Abort setting disable_master_thread + when no con.owner + +With the commit 733d5a953fa9 ("Cygwin: console: Ensure the master +thread runs only when it is supposed to"), the process which calls +set_disable_master_thread() hangs if the con.owner already exited, +because set_disable_master_thread() waits for cons_master_thread +accepting the status change and reflecting the current status to +master_thread_suspended. With this patch, set_disable_master_thread() +is aborted if the owner process no longer exists to avoid this +hang. + +Backport the complete upstream v5 patch unchanged after v18: +https://inbox.sourceware.org/cygwin-patches/20260923223230.3504-1-takashi.yano@nifty.ne.jp/ + +Addresses: https://cygwin.com/pipermail/cygwin/2026-September/260037.html +Fixes: 733d5a953fa9 ("Cygwin: console: Ensure the master thread runs only when it is supposed to") +Reported-by: Jay Libove Alzina +Co-authored-by: Johannes Schindelin +Reviewed-by: Johannes Schindelin +Assisted-by: GPT-6 +Signed-off-by: Takashi Yano +Signed-off-by: Johannes Schindelin +--- + winsup/cygwin/fhandler/console.cc | 57 +++++++++++++++++++++---- + winsup/cygwin/local_includes/fhandler.h | 3 +- + 2 files changed, 51 insertions(+), 9 deletions(-) + +diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc +index 9e87ff5..77786a1 100644 +--- a/winsup/cygwin/fhandler/console.cc ++++ b/winsup/cygwin/fhandler/console.cc +@@ -858,7 +858,7 @@ fhandler_console::fix_input_mode_if_necessary () + } + bool need_processed_input = + con.master_thread_suspended || con.disable_master_thread +- || con.need_win32_input_mode_fix; ++ || con.need_win32_input_mode_fix || master_thread_unavailable (unit); + if (need_processed_input == con.is_processed_input) + { + ReleaseMutex (cons_mode_mutex); +@@ -873,8 +873,11 @@ fhandler_console::fix_input_mode_if_necessary () + flags |= ENABLE_PROCESSED_INPUT; + else + flags &= ~ENABLE_PROCESSED_INPUT; +- con.is_processed_input = need_processed_input; ++ if (need_processed_input) ++ con.is_processed_input = true; + SetConsoleMode (get_handle (), flags); ++ if (!need_processed_input) ++ con.is_processed_input = false; + detach_console (resume_pid, con.owner); + release_attach_mutex (); + ReleaseMutex (input_mutex); +@@ -889,6 +892,7 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, + { + const _minor_t unit = p->unit; + DWORD oflags; ++ bool need_processed_input = false; + WaitForSingleObject (p->input_mutex, mutex_timeout); + acquire_attach_mutex (mutex_timeout); + DWORD resume_pid = attach_console (con.owner); +@@ -903,10 +907,10 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, + break; + case tty::cygwin: + flags |= ENABLE_WINDOW_INPUT; +- con.is_processed_input = ++ need_processed_input = + con.master_thread_suspended || con.disable_master_thread +- || con.need_win32_input_mode_fix; +- if (con.is_processed_input) ++ || con.need_win32_input_mode_fix || master_thread_unavailable (unit); ++ if (need_processed_input) + flags |= ENABLE_PROCESSED_INPUT; + if (wincap.has_con_24bit_colors () && !con_is_legacy) + flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; +@@ -927,8 +931,11 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, + break; + } + con.curr_input_mode = m; ++ if (flags & ENABLE_PROCESSED_INPUT) ++ con.is_processed_input = true; + SetConsoleMode (p->input_handle, flags); +- con.is_processed_input = (flags & ENABLE_PROCESSED_INPUT) != 0; ++ if (!(flags & ENABLE_PROCESSED_INPUT)) ++ con.is_processed_input = false; + if (!(oflags & ENABLE_VIRTUAL_TERMINAL_INPUT) + && (flags & ENABLE_VIRTUAL_TERMINAL_INPUT) + && con.cursor_key_app_mode) +@@ -4933,7 +4940,37 @@ fhandler_console::close_handle_set (handle_set_t *p) + bool + fhandler_console::need_console_handler () + { +- return con.disable_master_thread || con.master_thread_suspended; ++ return con.is_processed_input; ++} ++ ++bool ++fhandler_console::master_thread_unavailable (_minor_t unit) ++{ ++ DWORD owner = con.owner; ++ if (owner == (DWORD) -1) /* About to exit, however still alive */ ++ return false; ++ if (owner == 0) ++ return true; ++ bool exited = false; ++ DWORD error = ERROR_SUCCESS; ++ HANDLE process = OpenProcess (SYNCHRONIZE, FALSE, owner); ++ if (process) ++ { ++ DWORD res = WaitForSingleObject (process, 0); ++ if (res == WAIT_FAILED) ++ error = GetLastError (); ++ exited = (res == WAIT_OBJECT_0); ++ CloseHandle (process); ++ } ++ else ++ { ++ error = GetLastError (); ++ exited = (error == ERROR_INVALID_PARAMETER); ++ } ++ if (error && !exited) ++ system_printf ("Cannot query console owner %u, error %u", owner, error); ++ ++ return (exited || error) && con.owner == owner; + } + + void +@@ -4951,7 +4988,11 @@ fhandler_console::set_disable_master_thread (bool x, fhandler_console *cons) + con.disable_master_thread = x; + cons->release_input_mutex (); + while (con.master_thread_suspended != x) +- Sleep (1); ++ { /* Wait for the responce from the cons_master_thread. */ ++ if (master_thread_unavailable (unit)) ++ return; /* Abort */ ++ Sleep (1); ++ } + } + + int +diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h +index d7edf03..f048f17 100644 +--- a/winsup/cygwin/local_includes/fhandler.h ++++ b/winsup/cygwin/local_includes/fhandler.h +@@ -2159,7 +2159,7 @@ class dev_console + int num_processed; /* Number of input events in the current input buffer + already processed by cons_master_thread(). */ + bool need_win32_input_mode_fix; +- bool is_processed_input; ++ volatile bool is_processed_input; + + inline UINT get_console_cp (); + DWORD con_to_str (char *d, int dlen, WCHAR w); +@@ -2361,6 +2361,7 @@ private: + static void cleanup_for_non_cygwin_app (handle_set_t *p); + static void set_console_mode_to_native (); + bool need_console_handler (); ++ static bool master_thread_unavailable (_minor_t unit); + static void set_disable_master_thread (bool x, fhandler_console *cons = NULL); + static DWORD attach_console (DWORD, bool *err = NULL); + static void detach_console (DWORD, DWORD); diff --git a/msys2-runtime/PKGBUILD b/msys2-runtime/PKGBUILD index 96c4bcd1fed..1ec76b9b9b2 100644 --- a/msys2-runtime/PKGBUILD +++ b/msys2-runtime/PKGBUILD @@ -4,7 +4,7 @@ pkgbase=msys2-runtime pkgname=('msys2-runtime' 'msys2-runtime-devel') pkgver=3.6.10 -pkgrel=2 +pkgrel=3 pkgdesc="Cygwin POSIX emulation engine" arch=('x86_64') url="https://www.cygwin.com/" @@ -83,9 +83,15 @@ source=('msys2-runtime'::git+https://github.com/cygwin/cygwin#tag=cygwin-${pkgve 0052-msys2-runtime-restore-fast-path-for-current-user-pri.patch 0053-Change-the-default-base-address-for-x86_64.patch 0054-dumper-allow-compiling-with-binutils-2.47.patch - 0055-Cygwin-pty-preserve-terminal-replies-after-PTY-adopt.patch) + 0055-Cygwin-pty-preserve-terminal-replies-after-PTY-adopt.patch + 0056-dumper-allow-compiling-with-binutils-2.47.patch + 0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch + 0058-Cygwin-console-Fix-regression-in-console-input.patch + 0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch + 0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch + 0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch) sha256sums=('7eac3518c0225e991a00a7ad36009090b54abead9dc802236771cde3d9332f7c' - '5ab53e63bf68dc9696c9b3c51b6ff0cd4796f4305fdebb3da25f86ce558a79aa' + 'b90913f7acbe441bfb39867710b7eff802d7f2ec374d2f88bad118dbd53ae22c' '7c83791a32dbc139159802b7349fe50f69610863feea99bddcbed7d8d449b34f' '961fdf373c882fdec852453a6db368c2817fa4c422a5f786f51e277be1a68621' '92cdf141ffdd4142fb209da4d5b3cb7264e9fc55e70d2a88df8e2971a920b1ea' @@ -140,7 +146,13 @@ sha256sums=('7eac3518c0225e991a00a7ad36009090b54abead9dc802236771cde3d9332f7c' '2c393c0dc2b2b5d62a767cccd80dd89c8d7950edbf25e44dd7d2aee89c894b6d' '8588d779158683b1df0b6170029e22939af9d7f412e654e8d601e15eff6810cb' '35ee2aef88e6e686e9cc762ca7fb1326fd607a49aef103d6e277ce0aaf261ce6' - '380b0683f91295085f6e4eb4b82c758c1c520ac0623b84cd4a7142fb287c0580') + '380b0683f91295085f6e4eb4b82c758c1c520ac0623b84cd4a7142fb287c0580' + '838216017484fa6b4f3cc8dfabcf3c3403a106c0eae24316d7b6b8a2a008dcf5' + '3439ffbaf67f03f1fd7d1df126f608a6175394789e6b6b8dfd224428c161441a' + 'd4028752d9dadf1d100818091446390bb181a74331d28cc6a59754a83cc83afc' + '96545848ba4fa26edcdfc344358ce4a9276c21a8a27424d5743e99f90d62f54d' + 'acb7d041b9a0d57df4a3d1443fd1be7fae821a4de1fb905097478945a53fb788' + '3f5e5e91a63cda166be74dbf09255569d9f3b74850dbf4a0da800754962f4fb9') # Helper macros to help make tasks easier # apply_patch_with_msg() { @@ -250,7 +262,13 @@ prepare() { 0052-msys2-runtime-restore-fast-path-for-current-user-pri.patch \ 0053-Change-the-default-base-address-for-x86_64.patch \ 0054-dumper-allow-compiling-with-binutils-2.47.patch \ - 0055-Cygwin-pty-preserve-terminal-replies-after-PTY-adopt.patch + 0055-Cygwin-pty-preserve-terminal-replies-after-PTY-adopt.patch \ + 0056-dumper-allow-compiling-with-binutils-2.47.patch \ + 0057-Cygwin-open-Unlock-fdtab-before-open_with_arch.patch \ + 0058-Cygwin-console-Fix-regression-in-console-input.patch \ + 0059-Cygwin-console-Set-console-mode-only-if-std-in-out-e.patch \ + 0060-Cygwin-console-Fix-undesired-mode-change-at-exit-of-.patch \ + 0061-Cygwin-console-Abort-setting-disable_master_thread-w.patch } build() { diff --git a/msys2-runtime/msys2-runtime.commit b/msys2-runtime/msys2-runtime.commit index ab2f92f7403..d8825720b3d 100644 --- a/msys2-runtime/msys2-runtime.commit +++ b/msys2-runtime/msys2-runtime.commit @@ -1 +1 @@ -1c2fdcd892d856aa8d1502eb6035602790e1ec59 +3c7d3b1b45bdec14ea31371ee6216a79fd7f8e25