[PATCH v3 0/3] Three fixes for signal handling

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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

2025-02-28 Thread Takashi Yano
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