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 <takashi.y...@nifty.ne.jp> --- 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