[PATCH v3 0/3] Three fixes for signal handling
Takashi Yano (3): Cygwin: signal: Fix deadlock on SIGCONT Cygwin: signal: Fix a race issue on modifying _pinfo::process_state Cygwin: signal: Fix a problem that process hangs on exit [<= revised] winsup/cygwin/exceptions.cc | 31 winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 +- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 2 +- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 37 insertions(+), 28 deletions(-) -- 2.45.1
[PATCH v3 3/3] Cygwin: signal: Fix a problem that process hangs on exit
The process that receives many SIGSTOP/SIGCONT signals sometimes hangs on exit in sig_dispatch_pending(). This patch skips processing signals in call_signal_handler() when exit_state > ES_EXIT_STARTING to avoid that situation. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: d243e51ef1d3 ("Cygwin: signal: Fix deadlock between main thread and sig thread") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 45c71cbdf..759f89dca 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1675,7 +1675,7 @@ _cygtls::call_signal_handler () while (1) { lock (); - if (!current_sig) + if (!current_sig || exit_state > ES_EXIT_STARTING) { unlock (); break; -- 2.45.1
[PATCH v3 1/3] Cygwin: signal: Fix deadlock on SIGCONT
If SIGCONT starts processing while __SIGFLUSHFAST is ongoing, _main_tls->current_sig will never be cleared because the signal processing is stopped while waiting for the wake-up event in the main thread. This leads to a deadlock in the while loop waiting for current_sig to be cleared. With this patch, the function returns to wait_sig() if current_sig is set, rather than waiting for it in the while loop. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 9d2155089e87 ("(sigpacket::process): Call handle_SIGCONT early to deal with SIGCONT.") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 25 +++-- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index f576c5ff2..c6e82b6c5 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1425,23 +1425,18 @@ _cygtls::handle_SIGCONT () if (NOTSTATE (myself, PID_STOPPED)) return; - myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; - /* Carefully tell sig_handle_tty_stop to wake up. - Make sure that any pending signal is handled before trying to - send a new one. Then make sure that SIGCONT has been recognized - before exiting the loop. */ - while (current_sig) /* Assume that it's ok to just test sig outside of a */ -yield (); /* lock since setup_handler does it this way. */ - lock (); current_sig = SIGCONT; set_signal_arrived (); /* alert sig_handle_tty_stop */ unlock (); + /* Make sure that SIGCONT has been recognized before exiting the loop. */ while (current_sig == SIGCONT) yield (); + myself->stopsig = 0; + myself->process_state &= ~PID_STOPPED; + /* Clear pending stop signals */ sig_clear (SIGSTOP, false); sig_clear (SIGTSTP, false); @@ -1473,7 +1468,17 @@ sigpacket::process () myself->rusage_self.ru_nsignals++; if (si.si_signo == SIGCONT) -_main_tls->handle_SIGCONT (); +{ + /* Carefully tell sig_handle_tty_stop to wake up. +Make sure that any pending signal is handled before trying to +send a new one. */ + if (_main_tls->current_sig) + { + rc = -1; + goto done; + } + _main_tls->handle_SIGCONT (); +} /* SIGKILL is special. It always goes through. */ if (si.si_signo == SIGKILL) -- 2.45.1
[PATCH v3 2/3] Cygwin: signal: Fix a race issue on modifying _pinfo::process_state
The PID_STOPPED flag in _ponfo::process_state is sometimes accidentally cleared due to a race condition when modifying it with the "|=" or "&=" operators. This patch uses InterlockedOr/And() instead to avoid the race condition. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 1fd5e000ace55 ("import winsup-2000-02-17 snapshot") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 6 +++--- winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 ++- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 2 +- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index c6e82b6c5..45c71cbdf 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -882,7 +882,7 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *) /* Silently ignore attempts to suspend if there is no accommodating cygwin parent to deal with this behavior. */ if (!myself->cygstarted) -myself->process_state &= ~PID_STOPPED; +InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); else { _my_tls.incyg = 1; @@ -948,7 +948,7 @@ _cygtls::interrupt_setup (siginfo_t& si, void *handler, struct sigaction& siga) if (handler == sig_handle_tty_stop) { myself->stopsig = 0; - myself->process_state |= PID_STOPPED; + InterlockedOr ((LONG *) &myself->process_state, PID_STOPPED); } infodata = si; @@ -1435,7 +1435,7 @@ _cygtls::handle_SIGCONT () yield (); myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; + InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); /* Clear pending stop signals */ sig_clear (SIGSTOP, false); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 41a533705..783971b76 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -678,8 +678,9 @@ dofork (void **proc, bool *with_forkables) if (ischild) { - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); } else if (res < 0) { diff --git a/winsup/cygwin/local_includes/pinfo.h b/winsup/cygwin/local_includes/pinfo.h index 4de0f80dd..3086f9941 100644 --- a/winsup/cygwin/local_includes/pinfo.h +++ b/winsup/cygwin/local_includes/pinfo.h @@ -271,9 +271,9 @@ public: push_process_state (int add_flag) { flag = add_flag; -myself->process_state |= flag; +InterlockedOr ((LONG *) &myself->process_state, flag); } - void pop () { myself->process_state &= ~(flag); } + void pop () { InterlockedAnd ((LONG *) &myself->process_state, ~(flag)); } ~push_process_state () { pop (); } }; diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 1f26a3ccd..c69213f9a 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -69,7 +69,7 @@ pinfo::thisproc (HANDLE h) child_info_spawn::handle_spawn. */ init (cygheap->pid, flags, h); - procinfo->process_state |= PID_IN_USE; + InterlockedOr ((LONG *) &procinfo->process_state, PID_IN_USE); procinfo->dwProcessId = myself_initial.dwProcessId; procinfo->sendsig = myself_initial.sendsig; wcscpy (procinfo->progname, myself_initial.progname); @@ -89,7 +89,7 @@ pinfo_init (char **envp, int envc) { environ_init (envp, envc); /* spawn has already set up a pid structure for us so we'll use that */ - myself->process_state |= PID_CYGPARENT; + InterlockedOr ((LONG *) &myself->process_state, PID_CYGPARENT); } else { @@ -108,10 +108,11 @@ pinfo_init (char **envp, int envc) debug_printf ("Set nice to %d", myself->nice); } - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); if (being_debugged ()) -myself->process_state |= PID_DEBUGGED; +InterlockedOr ((LONG *) &myself->process_state, PID_DEBUGGED); myself.preserve (); debug_printf ("pid %d, pgid %d, process_state %y", myself->pid, myself->pgid, myself->process_state); diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index 0bd64963f..f8ba67e75 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -456,9 +456,11 @@ sigaction_worker (int sig, const struct sigaction *newact, sig_clear (sig, true); if (sig == SIGCHL
[PATCH v2 2/3] Cygwin: signal: Fix a race issue on modifying _pinfo::process_state
The PID_STOPPED flag in _ponfo::process_state is sometimes accidentally cleared due to a race condition when modifying it with the "|=" or "&=" operators. This patch uses InterlockedOr/And() instead to avoid the race condition. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 1fd5e000ace55 ("import winsup-2000-02-17 snapshot") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 6 +++--- winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 ++- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 2 +- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index c6e82b6c5..45c71cbdf 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -882,7 +882,7 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *) /* Silently ignore attempts to suspend if there is no accommodating cygwin parent to deal with this behavior. */ if (!myself->cygstarted) -myself->process_state &= ~PID_STOPPED; +InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); else { _my_tls.incyg = 1; @@ -948,7 +948,7 @@ _cygtls::interrupt_setup (siginfo_t& si, void *handler, struct sigaction& siga) if (handler == sig_handle_tty_stop) { myself->stopsig = 0; - myself->process_state |= PID_STOPPED; + InterlockedOr ((LONG *) &myself->process_state, PID_STOPPED); } infodata = si; @@ -1435,7 +1435,7 @@ _cygtls::handle_SIGCONT () yield (); myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; + InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); /* Clear pending stop signals */ sig_clear (SIGSTOP, false); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 41a533705..783971b76 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -678,8 +678,9 @@ dofork (void **proc, bool *with_forkables) if (ischild) { - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); } else if (res < 0) { diff --git a/winsup/cygwin/local_includes/pinfo.h b/winsup/cygwin/local_includes/pinfo.h index 4de0f80dd..3086f9941 100644 --- a/winsup/cygwin/local_includes/pinfo.h +++ b/winsup/cygwin/local_includes/pinfo.h @@ -271,9 +271,9 @@ public: push_process_state (int add_flag) { flag = add_flag; -myself->process_state |= flag; +InterlockedOr ((LONG *) &myself->process_state, flag); } - void pop () { myself->process_state &= ~(flag); } + void pop () { InterlockedAnd ((LONG *) &myself->process_state, ~(flag)); } ~push_process_state () { pop (); } }; diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 1f26a3ccd..c69213f9a 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -69,7 +69,7 @@ pinfo::thisproc (HANDLE h) child_info_spawn::handle_spawn. */ init (cygheap->pid, flags, h); - procinfo->process_state |= PID_IN_USE; + InterlockedOr ((LONG *) &procinfo->process_state, PID_IN_USE); procinfo->dwProcessId = myself_initial.dwProcessId; procinfo->sendsig = myself_initial.sendsig; wcscpy (procinfo->progname, myself_initial.progname); @@ -89,7 +89,7 @@ pinfo_init (char **envp, int envc) { environ_init (envp, envc); /* spawn has already set up a pid structure for us so we'll use that */ - myself->process_state |= PID_CYGPARENT; + InterlockedOr ((LONG *) &myself->process_state, PID_CYGPARENT); } else { @@ -108,10 +108,11 @@ pinfo_init (char **envp, int envc) debug_printf ("Set nice to %d", myself->nice); } - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); if (being_debugged ()) -myself->process_state |= PID_DEBUGGED; +InterlockedOr ((LONG *) &myself->process_state, PID_DEBUGGED); myself.preserve (); debug_printf ("pid %d, pgid %d, process_state %y", myself->pid, myself->pgid, myself->process_state); diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index 0bd64963f..f8ba67e75 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -456,9 +456,11 @@ sigaction_worker (int sig, const struct sigaction *newact, sig_clear (sig, true); if (sig == SIGCHL
[PATCH v2 0/3] Three fixes for signal handling
Takashi Yano (3): Cygwin: signal: Fix deadlock on SIGCONT Cygwin: signal: Fix a race issue on modifying _pinfo::process_state Cygwin: signal: Fix a problem that process hangs on exit [<= revised] winsup/cygwin/exceptions.cc | 29 winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 ++- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 8 ++-- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 41 insertions(+), 28 deletions(-) -- 2.45.1
[PATCH v2 1/3] Cygwin: signal: Fix deadlock on SIGCONT
If SIGCONT starts processing while __SIGFLUSHFAST is ongoing, _main_tls->current_sig will never be cleared because the signal processing is stopped while waiting for the wake-up event in the main thread. This leads to a deadlock in the while loop waiting for current_sig to be cleared. With this patch, the function returns to wait_sig() if current_sig is set, rather than waiting for it in the while loop. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 9d2155089e87 ("(sigpacket::process): Call handle_SIGCONT early to deal with SIGCONT.") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 25 +++-- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index f576c5ff2..c6e82b6c5 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1425,23 +1425,18 @@ _cygtls::handle_SIGCONT () if (NOTSTATE (myself, PID_STOPPED)) return; - myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; - /* Carefully tell sig_handle_tty_stop to wake up. - Make sure that any pending signal is handled before trying to - send a new one. Then make sure that SIGCONT has been recognized - before exiting the loop. */ - while (current_sig) /* Assume that it's ok to just test sig outside of a */ -yield (); /* lock since setup_handler does it this way. */ - lock (); current_sig = SIGCONT; set_signal_arrived (); /* alert sig_handle_tty_stop */ unlock (); + /* Make sure that SIGCONT has been recognized before exiting the loop. */ while (current_sig == SIGCONT) yield (); + myself->stopsig = 0; + myself->process_state &= ~PID_STOPPED; + /* Clear pending stop signals */ sig_clear (SIGSTOP, false); sig_clear (SIGTSTP, false); @@ -1473,7 +1468,17 @@ sigpacket::process () myself->rusage_self.ru_nsignals++; if (si.si_signo == SIGCONT) -_main_tls->handle_SIGCONT (); +{ + /* Carefully tell sig_handle_tty_stop to wake up. +Make sure that any pending signal is handled before trying to +send a new one. */ + if (_main_tls->current_sig) + { + rc = -1; + goto done; + } + _main_tls->handle_SIGCONT (); +} /* SIGKILL is special. It always goes through. */ if (si.si_signo == SIGKILL) -- 2.45.1
[PATCH v2 3/3] Cygwin: signal: Fix a problem that process hangs on exit
The process that receives many SIGSTOP/SIGCONT signals sometimes hangs on exit in sig_dispatch_pending(). This patch skips processing signals in wait_sig() when exit_state > ES_EXIT_STARTING to avoid that situation. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: d243e51ef1d3 ("Cygwin: signal: Fix deadlock between main thread and sig thread") Reported-by: Christian Franke Reviewed-by: Signed-off-by: Takashi Yano --- winsup/cygwin/sigproc.cc | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 8739f18f5..b519866d2 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -1374,7 +1374,11 @@ wait_sig (VOID *) sigq.retry = false; /* Don't process signals when we start exiting */ - if (exit_state > ES_EXIT_STARTING && pack.si.si_signo > 0) + if (exit_state > ES_EXIT_STARTING + && (pack.si.si_signo > 0 + || pack.si.si_signo == __SIGNOHOLD + || pack.si.si_signo == __SIGFLUSH + || pack.si.si_signo == __SIGFLUSHFAST)) goto skip_process_signal; sigset_t dummy_mask; -- 2.45.1
[PATCH 0/3] A few fixes for signal handling
Takashi Yano (3): Cygwin: signal: Fix deadlock on SIGCONT Cygwin: signal: Fix a race issue on modifying _pinfo::process_state Cygwin: signal: Fix a problem that process hangs on exit winsup/cygwin/exceptions.cc | 29 winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 ++- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 5 +++-- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 38 insertions(+), 28 deletions(-) -- 2.45.1
[PATCH 1/3] Cygwin: signal: Fix deadlock on SIGCONT
If SIGCONT starts processing while __SIGFLUSHFAST is ongoing, _main_tls->current_sig will never be cleared because the signal processing is stopped while waiting for the wake-up event in the main thread. This leads to a deadlock in the while loop waiting for current_sig to be cleared. With this patch, the function returns to wait_sig() if current_sig is set, rather than waiting for it in the while loop. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 9d2155089e87 ("(sigpacket::process): Call handle_SIGCONT early to deal with SIGCONT.") Reported-by: Christian Franke Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 25 +++-- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index f576c5ff2..c6e82b6c5 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1425,23 +1425,18 @@ _cygtls::handle_SIGCONT () if (NOTSTATE (myself, PID_STOPPED)) return; - myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; - /* Carefully tell sig_handle_tty_stop to wake up. - Make sure that any pending signal is handled before trying to - send a new one. Then make sure that SIGCONT has been recognized - before exiting the loop. */ - while (current_sig) /* Assume that it's ok to just test sig outside of a */ -yield (); /* lock since setup_handler does it this way. */ - lock (); current_sig = SIGCONT; set_signal_arrived (); /* alert sig_handle_tty_stop */ unlock (); + /* Make sure that SIGCONT has been recognized before exiting the loop. */ while (current_sig == SIGCONT) yield (); + myself->stopsig = 0; + myself->process_state &= ~PID_STOPPED; + /* Clear pending stop signals */ sig_clear (SIGSTOP, false); sig_clear (SIGTSTP, false); @@ -1473,7 +1468,17 @@ sigpacket::process () myself->rusage_self.ru_nsignals++; if (si.si_signo == SIGCONT) -_main_tls->handle_SIGCONT (); +{ + /* Carefully tell sig_handle_tty_stop to wake up. +Make sure that any pending signal is handled before trying to +send a new one. */ + if (_main_tls->current_sig) + { + rc = -1; + goto done; + } + _main_tls->handle_SIGCONT (); +} /* SIGKILL is special. It always goes through. */ if (si.si_signo == SIGKILL) -- 2.45.1
[PATCH 2/3] Cygwin: signal: Fix a race issue on modifying _pinfo::process_state
The PID_STOPPED flag in _ponfo::process_state is sometimes accidentally cleared due to a race condition when modifying it with the "|=" or "&=" operators. This patch uses InterlockedOr/And() instead to avoid the race condition. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: 1fd5e000ace55 ("import winsup-2000-02-17 snapshot") Reported-by: Christian Franke Signed-off-by: Takashi Yano --- winsup/cygwin/exceptions.cc | 6 +++--- winsup/cygwin/fork.cc| 5 +++-- winsup/cygwin/local_includes/pinfo.h | 4 ++-- winsup/cygwin/pinfo.cc | 11 ++- winsup/cygwin/signal.cc | 6 -- winsup/cygwin/sigproc.cc | 2 +- winsup/cygwin/spawn.cc | 6 +++--- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index c6e82b6c5..45c71cbdf 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -882,7 +882,7 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *) /* Silently ignore attempts to suspend if there is no accommodating cygwin parent to deal with this behavior. */ if (!myself->cygstarted) -myself->process_state &= ~PID_STOPPED; +InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); else { _my_tls.incyg = 1; @@ -948,7 +948,7 @@ _cygtls::interrupt_setup (siginfo_t& si, void *handler, struct sigaction& siga) if (handler == sig_handle_tty_stop) { myself->stopsig = 0; - myself->process_state |= PID_STOPPED; + InterlockedOr ((LONG *) &myself->process_state, PID_STOPPED); } infodata = si; @@ -1435,7 +1435,7 @@ _cygtls::handle_SIGCONT () yield (); myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; + InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED); /* Clear pending stop signals */ sig_clear (SIGSTOP, false); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 41a533705..783971b76 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -678,8 +678,9 @@ dofork (void **proc, bool *with_forkables) if (ischild) { - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); } else if (res < 0) { diff --git a/winsup/cygwin/local_includes/pinfo.h b/winsup/cygwin/local_includes/pinfo.h index 4de0f80dd..3086f9941 100644 --- a/winsup/cygwin/local_includes/pinfo.h +++ b/winsup/cygwin/local_includes/pinfo.h @@ -271,9 +271,9 @@ public: push_process_state (int add_flag) { flag = add_flag; -myself->process_state |= flag; +InterlockedOr ((LONG *) &myself->process_state, flag); } - void pop () { myself->process_state &= ~(flag); } + void pop () { InterlockedAnd ((LONG *) &myself->process_state, ~(flag)); } ~push_process_state () { pop (); } }; diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 1f26a3ccd..c69213f9a 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -69,7 +69,7 @@ pinfo::thisproc (HANDLE h) child_info_spawn::handle_spawn. */ init (cygheap->pid, flags, h); - procinfo->process_state |= PID_IN_USE; + InterlockedOr ((LONG *) &procinfo->process_state, PID_IN_USE); procinfo->dwProcessId = myself_initial.dwProcessId; procinfo->sendsig = myself_initial.sendsig; wcscpy (procinfo->progname, myself_initial.progname); @@ -89,7 +89,7 @@ pinfo_init (char **envp, int envc) { environ_init (envp, envc); /* spawn has already set up a pid structure for us so we'll use that */ - myself->process_state |= PID_CYGPARENT; + InterlockedOr ((LONG *) &myself->process_state, PID_CYGPARENT); } else { @@ -108,10 +108,11 @@ pinfo_init (char **envp, int envc) debug_printf ("Set nice to %d", myself->nice); } - myself->process_state |= PID_ACTIVE; - myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED); + InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE); + InterlockedAnd ((LONG *) &myself->process_state, + ~(PID_INITIALIZING | PID_EXITED | PID_REAPED)); if (being_debugged ()) -myself->process_state |= PID_DEBUGGED; +InterlockedOr ((LONG *) &myself->process_state, PID_DEBUGGED); myself.preserve (); debug_printf ("pid %d, pgid %d, process_state %y", myself->pid, myself->pgid, myself->process_state); diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index 0bd64963f..f8ba67e75 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -456,9 +456,11 @@ sigaction_worker (int sig, const struct sigaction *newact, sig_clear (sig, true); if (sig == SIGCHLD)
[PATCH 3/3] Cygwin: signal: Fix a problem that process hangs on exit
The process that receives many SIGSTOP/SIGCONT signals sometimes hangs on exit in sig_dispatch_pending(). This patch avoids processing signals in sig_dispatch_pending() as if __SIGFLASHFAST is used. Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html Fixes: d243e51ef1d3 ("Cygwin: signal: Fix deadlock between main thread and sig thread") Reported-by: Christian Franke Signed-off-by: Takashi Yano --- winsup/cygwin/sigproc.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 8739f18f5..7cfa37d8f 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -743,7 +743,8 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) } unsigned cw_mask; - cw_mask = pack.si.si_signo == __SIGFLUSHFAST ? 0 : cw_sig_restart; + cw_mask = +(pack.si.si_signo == __SIGFLUSHFAST || exit_state) ? 0 : cw_sig_restart; DWORD nb; BOOL res; -- 2.45.1