[PATCH v15] Cygwin: console: Fix undesired mode change at exit of non-cygwin apps
Takashi Yano
takashi.yano@nifty.ne.jp
Tue Sep 15 07:49:16 GMT 2026
Hi Johannes,
I think I've fixed the all points you mentioned.
Could you please check v16?
On Sun, 13 Sep 2026 12:38:32 +0200 (CEST)
Johannes Schindelin wrote:
> Hi Takashi,
>
> thank you for your tenacity! You are going well above and beyond what any
> open source maintainer could expect of any contributor, going to 15
> iterations of a patch.
>
> So I feel quite embarrassed to point out a couple more things I would like
> to see changed... 😊 I hope you forgive me!
>
> Also please note that I am still trying my best to understand the
> console/pty code with its implied contracts, and I feel comfortable in
> saying that I am still too uncomfortable to claim that I really understand
> it. _You_ are clearly the domain expert here, and I have to rely heavily
> on AI when performing these reviews, which means that I can easily miss
> crucial design decisions.
>
> On Sun, 13 Sep 2026, Takashi Yano wrote:
>
> > Previously, if two non-cygwin apps are started and one of them
> > exits first, the other one loosed appropriate console mode, since
>
> Did you mean "the other application lost the console mode it needed" here?
> ("loosed" could be either "loosened", i.e. the opposite of tightened, or
> "lost", which I think you meant in this instance).
>
> > 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`.
>
> Well explained, thanks!
>
> >
> > 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.
>
> Thank you for calling this out, and I also don't see any elegant way
> around this limitation. It *could* be done via some sort of "pending"
> state, but the difficult part is lifetime management: failed launches,
> cancellation, early child exit, parent death, and simultaneous starts must
> not leave stale pending state that would suppress cleanup forever.
>
> >
> > Fixes: 48285aa36c2c ("Cygwin: console: Fix handling of Ctrl-S in Win7.")
> > Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> > Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> > ---
> > v2: Stop counting up/down the counter by itself.
> > Use num_active_non_cygwin_apps() instead.
> > v3: Guard setup_for_non_cygwin_app() by cons_mode_mutex as well.
> > v4: Guard all mode changes in console by cons_mode_mutex.
> > v5: Fix the issue of mutex acquisition order.
> > Fix the race window around the process creation.
> > Improve latency of checking existence of non-cygwin apps.
> > Handle errors in checking existence of non-cygwin apps.
> > v6: Match the conditions for incrementing and decrementing the counter.
> > v7: Decrement the counter only if it was incremented by myself.
> > v8: Symlify the conditions for incrementing and decrementing the counter
> > a bit.
> > v9: Minimize the argument of set_non_cygwin_app_setup_ongoing().
> > v10: Set process_state before calling spawn_worker::setup() rather than
> > using the counter. In addition, resume non-cygwin app before
> > modifying process table. These make things much simpler.
> > Narrowing the period of acquiring input_mutex in peek_console()
> > in select.cc.
> > v11: Release output_mutex before calling bg_check() in ioctl().
> > Suppress unecessary console-mode change attempts.
> > v12: Change handling of disabling master thread in the case of win32
> > input mode.
> > Wait for console attaching only when the spawned app is a console
> > app. In addition, a timeout is introduced to this wait loop for
> > safety.
> > v13: Do not perform cleanup for non-cygwin apps on the error in
> > active_non_cygwin_apps_exist().
> > Handle WAIT_ABANDONED in CSI?9001h/l handling.
> > Fix ABBA deadlock about echo code path.
> > Set need_win32_input_mode_fix before set_disable_master_thread()
> > in CSI?9001h/l handling.
> > Setting and checking for need_win32_input_mode_fix inside the
> > input_mutex.
> > Fix the problem that the flag need_win32_input_mode_fix is set,
> > but disable_master_thread does not set if another thread holds
> > cons_mode_mutex.
> > v14: Re-design echo code path. (Introduce raw_write() for doecho())
> > Re-design the handling of need_win32_input_mode_fix from scratch.
> > v15: Fix possible deadlock with input_mutex regarding doecho()
> >
> > winsup/cygwin/fhandler/console.cc | 200 +++++++++++++++++++++---
> > winsup/cygwin/fhandler/termios.cc | 40 +++--
> > winsup/cygwin/local_includes/fhandler.h | 10 +-
> > winsup/cygwin/select.cc | 16 +-
> > winsup/cygwin/spawn.cc | 44 +++---
> > 5 files changed, 250 insertions(+), 60 deletions(-)
> >
> > diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc
> > index ba35ca44c..b036b7ac5 100644
> > --- a/winsup/cygwin/fhandler/console.cc
> > +++ b/winsup/cygwin/fhandler/console.cc
> > @@ -443,6 +443,11 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp)
> > cygwait (40);
> > continue;
> > }
> > + if (con.need_win32_input_mode_fix)
> > + {
> > + cygwait (40);
> > + continue;
> > + }
>
> `disable_master_thread` is the pause/resume request;
> `master_thread_suspended` is its acknowledgement. While
> `need_win32_input_mode_fix` remains true, v14's worker sleeps without
> clearing an old true acknowledgement. A non-owner Cygwin process can
> launch a native child, pause the worker, then request resume during
> cleanup and wait indefinitely while holding `cons_mode_mutex`. The
> worker's owner is alive, so the owner-exit patch cannot help. Suppressing
> input processing must not break the request/acknowledgement protocol.
The code here with the patch is:
while (con.owner == GetCurrentProcessId ())
{
DWORD total_read, n, i;
DWORD mode;
bool need_strip = false;
if (con.disable_master_thread)
{
con.master_thread_suspended = true;
cygwait (40);
continue;
}
if (con.need_win32_input_mode_fix)
{
cygwait (40);
continue;
}
Even if need_win32_input_mode_fix is true, disable_master_thread
-> master_thread_suspended works within 40msec.
> >
> > acquire_attach_mutex (mutex_timeout);
> > GetNumberOfConsoleInputEvents (p->input_handle, &total_read);
> > @@ -841,6 +846,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;
> > }
> > }
> >
> > @@ -877,6 +884,39 @@ 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);
> > + 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);
> > +}
>
> This method also runs while `con.curr_input_mode` is native. Switching to
> native mode leaves `is_processed_input` unchanged, so that cache can still
> be false after the worker has been paused. If the native application
> disables `ENABLE_PROCESSED_INPUT` to receive Ctrl+C as input, a Cygwin
> sibling's write can enable it again simply because the worker is paused.
> No Win32-input-mode request is needed. Reconciliation must respect native
> settings, not treat worker suspension as permission to rewrite them.
Fixed. Thanks!
>
> > +
> > /* 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
> > @@ -899,7 +939,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;
> > @@ -977,16 +1020,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;
> > @@ -994,11 +1113,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
> > @@ -1055,6 +1174,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);
> > @@ -1182,13 +1305,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);
> > @@ -1196,6 +1319,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);
> > }
> > @@ -1314,6 +1438,8 @@ wait_retry:
> >
> > #undef buf
> >
> > + fix_input_mode_if_necessary (); /* for win32_input_mode */
> > +
>
> This new call in `read()` is only on its normal finishing path. For
> example, consider a noncanonical 16-byte read with echo enabled,
> `VMIN=16`, and `VTIME=1`. If only the eight bytes `ESC[?9001h` arrive,
> echo changes `need_win32_input_mode_fix`; the read copies those bytes,
> retries, then returns eight from the partial-timeout branch. Please note
> that in this instance, it does *not* reach the new fix-up call. Another
> console operation may subsequently mask the missing update.
Fixed.
> Once again, please do double-check my claims. While I have a somewhat
> rudimentary understanding of the architecture of the code (and now finally
> even understand why that "master" thread exists, thanks to your patient
> explanations), without any doubt there could be gaping holes in my
> reasoning due to my lack of expertise in this area.
>
> And before I forget, again: Thank you so much for working on this code. It
> is tricky, it is finicky, and without your continued dedication there
> would be no chance to ever resolve these bugs.
>
> Ciao,
> Johannes
>
> > buflen = copied_chars;
> > return;
> >
> > @@ -2011,6 +2137,7 @@ fhandler_console::open (int flags, mode_t)
> > if (in_is_console)
> > CloseHandle (h_in);
> >
> > + WaitForSingleObject (cons_mode_mutex, mutex_timeout);
> > if (in_is_console && con.curr_input_mode != tty::cygwin)
> > {
> > prev_input_mode_backup = con.prev_input_mode;
> > @@ -2023,6 +2150,7 @@ fhandler_console::open (int flags, mode_t)
> > 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 ());
> > @@ -2106,6 +2234,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);
> > @@ -2115,6 +2244,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);
> > @@ -2122,6 +2252,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);
> > }
> > @@ -2131,18 +2262,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)
> > @@ -2197,6 +2330,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 ();
> >
> > @@ -2246,8 +2381,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;
> > @@ -2370,10 +2505,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;
> > }
> >
> > @@ -3140,12 +3277,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)
> > @@ -3994,6 +4126,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 */
> > @@ -4327,18 +4471,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)
> > {
> > - int stopped = get_ttyp ()->output_stopped;
> > - get_ttyp ()->output_stopped = 0;
> > - write (str, len);
> > - get_ttyp ()->output_stopped = stopped;
> > + raw_write (str, len);
> > }
> >
> > static const struct {
> > @@ -4476,10 +4615,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;
> > }
> > }
> > @@ -4536,8 +4678,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);
> > }
> >
> > @@ -4818,6 +4969,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;
> > }
> >
> > @@ -4834,6 +4988,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 ee576a0a8..0134ec8b8 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 d11b3ec4f..e00212f57 100644
> > --- a/winsup/cygwin/local_includes/fhandler.h
> > +++ b/winsup/cygwin/local_includes/fhandler.h
> > @@ -2023,6 +2023,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
> > @@ -2042,7 +2043,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 &);
> > };
> > };
> >
> > @@ -2157,6 +2159,8 @@ class dev_console
> > volatile 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);
> > @@ -2199,6 +2203,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;
> > @@ -2268,6 +2273,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 ()
> > @@ -2366,6 +2372,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
> > {
> > @@ -2379,6 +2386,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 b72083447..33417169b 100644
> > --- a/winsup/cygwin/select.cc
> > +++ b/winsup/cygwin/select.cc
> > @@ -1154,23 +1154,23 @@ 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)
> > && global_sigs[SIGWINCH].sa_handler != SIG_IGN
> > @@ -1180,8 +1180,10 @@ peek_console (select_record *me, bool)
> > fh->release_input_mutex ();
> > return -1;
> > }
> > + fh->release_input_mutex ();
> > +
> > + fh->fix_input_mode_if_necessary (); /* for win32_input_mode */
> > }
> > - 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 8f976b9a0..abaa34d32 100644
> > --- a/winsup/cygwin/spawn.cc
> > +++ b/winsup/cygwin/spawn.cc
> > @@ -559,6 +559,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;
> > @@ -586,14 +594,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 ();
> > @@ -737,6 +737,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;
> > @@ -810,18 +822,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);
> >
> > @@ -868,7 +873,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
> > @@ -906,7 +910,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);
> > --
> > 2.51.0
> >
> >
--
Takashi Yano <takashi.yano@nifty.ne.jp>
More information about the Cygwin-patches
mailing list