summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/exceptions.cc
diff options
context:
space:
mode:
authorChristopher Faylor <me@cgf.cx>2013-04-09 01:01:19 +0000
committerChristopher Faylor <me@cgf.cx>2013-04-09 01:01:19 +0000
commit9d2155089e8709977ef012cfdfd88d511b92d154 (patch)
treeb2d6845c34da33952996e16a1e637f61268ae351 /winsup/cygwin/exceptions.cc
parent037ad80a525e5be3088bcb3b34546498695e746f (diff)
downloadcygnal-9d2155089e8709977ef012cfdfd88d511b92d154.tar.gz
cygnal-9d2155089e8709977ef012cfdfd88d511b92d154.tar.bz2
cygnal-9d2155089e8709977ef012cfdfd88d511b92d154.zip
* cygtls.h (_cygtls::reset_signal_arrived): Actually reset the signal_arrived
event. (_cygtls::handle_SIGCONT): Declare ew function. * cygwait.cc (is_cw_sig_handle): Delete. (is_cw_sig_cont): New convenience define. (cygwait): Clear signal if is_cw_sig_cont and we got a SIGCONT. * cygwait.h (cw_wait_mask): Add cw_sig_cont. * exceptions.cc (sig_handle_tty_stop): Tighten "incyg" region. Use cw_sig_cont param for cygwait. Don't zero signal here outside of lock. (sigpacket::setup_handler): Don't check for in_forkee since we will now never get here in that state. (_cygtls::handle_SIGCONT): Define new function. (sigpacket::process): Call handle_SIGCONT early to deal with SIGCONT. Nuke continue_now handling. Allow SIGKILL to kill a suspended process. Delete a couple of now-unneeded labels. (_cygtls::call_signal_handler): Reorganize setting of incyg within lock. * sigproc.cc (pending_signals): Simplify. (pending_signals::clear): New method. (_cygtls::remove_wq): Reorganize to always close wq.thread_ev if it exists to avoid handle leaks. (sig_clear): Simplify by just calling sigq.clear(). (sig_dispatch_pending): Always call sigq.pending even in signal thread to force another loop in wait_sig. (sig_send): Remove a "goto out" just before out: label. (pending_signals::add): Simplify. (pending_signals::del): Delete. (pending_signals::next): Delete. (wait_sig): Define variable q to be the start of the signal queue. Just iterate through sigq queue, deleting processed or zeroed signals. Only set clearwait when the current signal is SIGCHLD. * sigproc.h: Add a comment about an unused enum.
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r--winsup/cygwin/exceptions.cc102
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;