[PATCH v2] Cygwin: console: Abort setting disable_master_thread when no con.owner
Johannes Schindelin
Johannes.Schindelin@gmx.de
Sun Sep 13 12:12:16 GMT 2026
Hi Takashi,
On Sat, 12 Sep 2026, Takashi Yano wrote:
> 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.
>
> 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 <libove@felines.org>
> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
> Reviewed-by:
> ---
> v2:
> Fix the race
>
> winsup/cygwin/fhandler/console.cc | 15 ++++++++++++++-
> 1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc
> index 5f78d0c62..4b1ba60ca 100644
> --- a/winsup/cygwin/fhandler/console.cc
> +++ b/winsup/cygwin/fhandler/console.cc
> @@ -5009,11 +5009,24 @@ fhandler_console::set_disable_master_thread (bool x, fhandler_console *cons)
> return;
> }
> const _minor_t unit = cons->get_minor ();
> + if (con.owner == 0)
> + /* The process that runs cons_master_thread no longer exists. */
> + return;
This discards a request that a replacement worker must inherit.
A reachable retired state is `con.owner=0`, `disable_master_thread=false`,
`master_thread_suspended=true`: the worker yielded to a reader, then its
hosting process closed a console descriptor that was not its controlling
terminal, without requesting suspension. A surviving foreground Cygwin
process subsequently requests suspension before starting a native
application, but the initial guard returns without recording the true
request. A later Cygwin process with redirected stdin and console stdout
can adopt that console and start a replacement worker without resetting
the false request. It therefore runs and can consume flow-control
characters intended for the native reader. V2's later normalization cannot
help: the initial return bypasses it.
Before this patch, the true request was recorded, and the already-true
acknowledgement let the call finish immediately. Thus this is a regression
in a previously completing case, not simply a different outcome for an
already-hanging case. The defect comes from v1's guard and remains in v2.
The basic idea is: a missing worker lets us stop waiting, but not discard
the request its replacement needs. We need to preserve or normalize the
shared request consistently under input-state synchronization on both
no-owner exits, without removing the dead-owner escape.
Thanks,
Johannes
> cons->acquire_input_mutex (mutex_timeout);
> con.disable_master_thread = x;
> cons->release_input_mutex ();
> while (con.master_thread_suspended != x)
> - Sleep (1);
> + {
> + Sleep (1);
> + if (con.owner == 0)
> + {
> + cons->acquire_input_mutex (mutex_timeout);
> + con.disable_master_thread = true;
> + con.master_thread_suspended = true;
> + cons->release_input_mutex ();
> + return;
> + }
> + }
> }
>
> int
> --
> 2.51.0
>
>
More information about the Cygwin-patches
mailing list