It seems that current _cygtls::handle_SIGCONT() code sometimes falls
into a deadlock due to frequent TLS lock/unlock operation in the
yield() loop. With this patch, the yield() in the wait loop is placed
outside the TLS lock to avoid frequent TLS lock/unlock.
Fixes: 9ae51bcc51a7 ("Cygwin: signal: Fix another deadlock between main and sig
thread")
Reviewed-by:
Signed-off-by: Takashi Yano <[email protected]>
---
winsup/cygwin/exceptions.cc | 36 ++++++++++-----------------
winsup/cygwin/local_includes/cygtls.h | 4 +--
2 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 4dc4be278..f576c5ff2 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -1420,7 +1420,7 @@ api_fatal_debug ()
/* Attempt to carefully handle SIGCONT when we are stopped. */
void
-_cygtls::handle_SIGCONT (threadlist_t * &tl_entry)
+_cygtls::handle_SIGCONT ()
{
if (NOTSTATE (myself, PID_STOPPED))
return;
@@ -1431,23 +1431,17 @@ _cygtls::handle_SIGCONT (threadlist_t * &tl_entry)
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. */
- bool sigsent = false;
- while (1)
- if (current_sig) /* Assume that it's ok to just test sig outside of a
- lock since setup_handler does it this way. */
- {
- cygheap->unlock_tls (tl_entry);
- yield (); /* Attempt to schedule another thread. */
- tl_entry = cygheap->find_tls (_main_tls);
- }
- else if (sigsent)
- break; /* SIGCONT has been recognized by other thread */
- else
- {
- current_sig = SIGCONT;
- set_signal_arrived (); /* alert sig_handle_tty_stop */
- sigsent = true;
- }
+ 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 ();
+
+ while (current_sig == SIGCONT)
+ yield ();
+
/* Clear pending stop signals */
sig_clear (SIGSTOP, false);
sig_clear (SIGTSTP, false);
@@ -1479,11 +1473,7 @@ sigpacket::process ()
myself->rusage_self.ru_nsignals++;
if (si.si_signo == SIGCONT)
- {
- tl_entry = cygheap->find_tls (_main_tls);
- _main_tls->handle_SIGCONT (tl_entry);
- cygheap->unlock_tls (tl_entry);
- }
+ _main_tls->handle_SIGCONT ();
/* SIGKILL is special. It always goes through. */
if (si.si_signo == SIGKILL)
diff --git a/winsup/cygwin/local_includes/cygtls.h
b/winsup/cygwin/local_includes/cygtls.h
index 2d490646a..e0de712f4 100644
--- a/winsup/cygwin/local_includes/cygtls.h
+++ b/winsup/cygwin/local_includes/cygtls.h
@@ -194,7 +194,7 @@ public: /* Do NOT remove this public: line, it's a marker
for gentls_offsets. */
class cygthread *_ctinfo;
class san *andreas;
waitq wq;
- int current_sig;
+ volatile int current_sig;
unsigned incyg;
volatile unsigned stacklock;
__tlsstack_t *stackptr;
@@ -274,7 +274,7 @@ public: /* Do NOT remove this public: line, it's a marker
for gentls_offsets. */
{
will_wait_for_signal = false;
}
- void handle_SIGCONT (threadlist_t * &);
+ void handle_SIGCONT ();
static void cleanup_early(struct _reent *);
private:
void call2 (DWORD (*) (void *, void *), void *, void *);
--
2.45.1