diff options
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 102 |
1 files changed, 60 insertions, 42 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 9815be280..d86635bae 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -686,23 +686,24 @@ extern "C" { static void sig_handle_tty_stop (int sig, siginfo_t *, void *) { - _my_tls.incyg = 1; /* 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; else { + _my_tls.incyg = 1; myself->stopsig = sig; myself->alert_parent (sig); sigproc_printf ("process %d stopped by signal %d", myself->pid, sig); /* FIXME! This does nothing to suspend anything other than the main thread. */ - DWORD res = cygwait (NULL, cw_infinite, cw_sig_eintr); + /* Use special cygwait parameter to handle SIGCONT. _main_tls.sig will + be cleared under lock when SIGCONT is detected. */ + DWORD res = cygwait (NULL, cw_infinite, cw_sig_cont); switch (res) { case WAIT_SIGNALED: - _my_tls.sig = 0; myself->stopsig = SIGCONT; myself->alert_parent (SIGCONT); break; @@ -710,8 +711,8 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *) api_fatal ("WaitSingleObject returned %d", res); break; } + _my_tls.incyg = 0; } - _my_tls.incyg = 0; } } /* end extern "C" */ @@ -785,10 +786,6 @@ sigpacket::setup_handler (void *handler, struct sigaction& siga, _cygtls *tls) goto out; } - while (in_forkee) - yield (); /* Won't be able to send signals until we're finished - processing fork(). */ - for (int n = 0; n < CALL_HANDLER_RETRY_OUTER; n++) { for (int i = 0; i < CALL_HANDLER_RETRY_INNER; i++) @@ -1121,31 +1118,56 @@ signal_exit (int sig, siginfo_t *si) } } /* extern "C" */ +/* Attempt to carefully handle SIGCONT when we are stopped. */ +void +_cygtls::handle_SIGCONT () +{ + if (ISSTATE (myself, PID_STOPPED)) + { + myself->stopsig = 0; + myself->process_state &= ~PID_STOPPED; + int state = 0; + /* Carefully tell sig_handle_tty_stop to wake up. */ + while (state < 2) + { + lock (); + if (sig) + yield (); /* state <= 1 */ + else if (state) + state++; /* state == 2 */ + else + { + sig = SIGCONT; + SetEvent (signal_arrived); + state++; /* state == 1 */ + } + unlock (); + } + /* Tell wait_sig to handle any queued signals now that we're alive + again. */ + sig_dispatch_pending (false); + } + /* Clear pending stop signals */ + sig_clear (SIGSTOP); + sig_clear (SIGTSTP); + sig_clear (SIGTTIN); + sig_clear (SIGTTOU); +} + int __stdcall sigpacket::process () { int rc = 1; bool issig_wait = false; - bool continue_now = false; struct sigaction& thissig = global_sigs[si.si_signo]; void *handler = have_execed ? NULL : (void *) thissig.sa_handler; + /* Don't try to send signals if we're just starting up since signal masks + may not be available. */ if (!cygwin_finished_initializing) { rc = -1; - goto really_done; - } - - if (si.si_signo == SIGCONT) - { - continue_now = ISSTATE (myself, PID_STOPPED); - myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; - /* Clear pending stop signals */ - sig_clear (SIGSTOP); - sig_clear (SIGTSTP); - sig_clear (SIGTTIN); - sig_clear (SIGTTOU); + goto done; } sigproc_printf ("signal %d processing", si.si_signo); @@ -1153,7 +1175,17 @@ sigpacket::process () myself->rusage_self.ru_nsignals++; _cygtls *tls; - if (!sigtls) + if (si.si_signo == SIGCONT) + _main_tls->handle_SIGCONT (); + + if (si.si_signo == SIGKILL) + tls = _main_tls; /* SIGKILL is special. It always goes through. */ + else if (ISSTATE (myself, PID_STOPPED)) + { + rc = -1; /* Don't send signals when stopped */ + goto done; + } + else if (!sigtls) { tls = cygheap->find_tls (si.si_signo, issig_wait); sigproc_printf ("using tls %p", tls); @@ -1169,7 +1201,8 @@ sigpacket::process () tls = NULL; } - if (!tls || ISSTATE (myself, PID_STOPPED)) + /* !tls means no threads available to catch a signal. */ + if (!tls) { sigproc_printf ("signal %d blocked", si.si_signo); rc = -1; @@ -1225,6 +1258,7 @@ sigpacket::process () goto dosig; stop: + tls = _main_tls; handler = (void *) sig_handle_tty_stop; thissig = global_sigs[SIGSTOP]; goto dosig; @@ -1232,17 +1266,8 @@ stop: exit_sig: handler = (void *) signal_exit; thissig.sa_flags |= SA_SIGINFO; - if (si.si_signo == SIGKILL) - goto dispatch_sig; dosig: - if (ISSTATE (myself, PID_STOPPED) && !continue_now) - { - rc = -1; /* No signals delivered if stopped */ - goto done; - } - -dispatch_sig: if (have_execed) { sigproc_printf ("terminating captive process"); @@ -1251,15 +1276,8 @@ dispatch_sig: /* Dispatch to the appropriate function. */ sigproc_printf ("signal %d, signal handler %p", si.si_signo, handler); rc = setup_handler (handler, thissig, tls); - continue_now = false; done: - if (continue_now) - { - (tls ?: _main_tls)->sig = SIGCONT; - SetEvent (tls->signal_arrived); - } -really_done: sigproc_printf ("returning %d", rc); return rc; @@ -1293,11 +1311,11 @@ _cygtls::call_signal_handler () sigset_t this_oldmask = set_process_mask_delta (); int this_errno = saved_errno; - sig = 0; /* Flag that we can accept another signal */ reset_signal_arrived (); + incyg = false; + sig = 0; /* Flag that we can accept another signal */ unlock (); /* unlock signal stack */ - incyg = false; /* no ucontext_t information provided yet, so third arg is NULL */ thisfunc (thissig, &thissi, NULL); incyg = true; |