diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index e83a81597..8fbe9a821 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,38 @@ +2013-04-08 Christopher Faylor + + * 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. + 2013-04-08 Corinna Vinschen * fhandler_socket.cc (get_inet_addr): Handle abstract AF_LOCAL socket. diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h index 5ddb9e199..5463ed85c 100644 --- a/winsup/cygwin/cygtls.h +++ b/winsup/cygwin/cygtls.h @@ -253,7 +253,13 @@ public: will_wait_for_signal = true; } } - void reset_signal_arrived () { will_wait_for_signal = false; } + void reset_signal_arrived () + { + if (signal_arrived) + ResetEvent (signal_arrived); + will_wait_for_signal = false; + } + void handle_SIGCONT (); private: void __reg3 call2 (DWORD (*) (void *, void *), void *, void *); /*gentls_offsets*/ diff --git a/winsup/cygwin/cygwait.cc b/winsup/cygwin/cygwait.cc index b0476660c..fcf31b39b 100644 --- a/winsup/cygwin/cygwait.cc +++ b/winsup/cygwin/cygwait.cc @@ -1,6 +1,6 @@ /* cygwait.h - Copyright 2011, 2012 Red Hat, Inc. + Copyright 2011, 2012, 2013 Red Hat, Inc. This file is part of Cygwin. @@ -17,9 +17,9 @@ #define is_cw_cancel_self (mask & cw_cancel_self) #define is_cw_sig (mask & cw_sig) #define is_cw_sig_eintr (mask & cw_sig_eintr) -#define is_cw_sig_return (mask & cw_sig_return) +#define is_cw_sig_cont (mask & cw_sig_cont) -#define is_cw_sig_handle (mask & (is_cw_sig | is_cw_sig_eintr)) +#define is_cw_sig_handle (mask & (cw_sig | cw_sig_eintr | cw_sig_cont)) LARGE_INTEGER cw_nowait_storage; @@ -83,10 +83,12 @@ cygwait (HANDLE object, PLARGE_INTEGER timeout, unsigned mask) { _my_tls.lock (); int sig = _my_tls.sig; + if (is_cw_sig_cont && sig == SIGCONT) + _my_tls.sig = 0; _my_tls.unlock (); if (!sig) continue; - if (is_cw_sig_eintr) + if (is_cw_sig_eintr || (is_cw_sig_cont && sig == SIGCONT)) res = WAIT_SIGNALED; /* caller will deal with signals */ else if (_my_tls.call_signal_handler ()) continue; diff --git a/winsup/cygwin/cygwait.h b/winsup/cygwin/cygwait.h index 707976786..496817bcf 100644 --- a/winsup/cygwin/cygwait.h +++ b/winsup/cygwin/cygwait.h @@ -1,6 +1,6 @@ /* cygwait.h - Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. This file is part of Cygwin. @@ -19,7 +19,8 @@ enum cw_wait_mask cw_cancel = 0x0001, cw_cancel_self = 0x0002, cw_sig = 0x0004, - cw_sig_eintr = 0x0008 + cw_sig_eintr = 0x0008, + cw_sig_cont = 0x0010 }; extern LARGE_INTEGER cw_nowait_storage; 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; diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 4bf6f5630..e585877b9 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -73,20 +73,15 @@ class pending_signals { sigpacket sigs[NSIG + 1]; sigpacket start; - sigpacket *end; - sigpacket *prev; - sigpacket *curr; bool retry; + public: - void reset () {curr = &start; prev = &start;} void add (sigpacket&); - void del (); bool pending () {retry = true; return !!start.next;} - sigpacket *next (); - sigpacket *save () const {return curr;} - void restore (sigpacket *saved) {curr = saved;} + void clear (int sig) {sigs[sig].si.si_signo = 0;} friend void __reg1 sig_dispatch_pending (bool);; friend void WINAPI wait_sig (VOID *arg); + friend void sigproc_init (); }; Static pending_signals sigq; @@ -338,18 +333,22 @@ out1: void _cygtls::remove_wq (DWORD wait) { - if (exit_state < ES_FINAL && waitq_head.next && sync_proc_subproc - && sync_proc_subproc.acquire (wait)) + if (wq.thread_ev) { - for (waitq *w = &waitq_head; w->next != NULL; w = w->next) - if (w->next == &wq) - { - ForceCloseHandle1 (wq.thread_ev, wq_ev); - w->next = wq.next; - break; - } - sync_proc_subproc.release (); + if (exit_state < ES_FINAL && waitq_head.next && sync_proc_subproc + && sync_proc_subproc.acquire (wait)) + { + for (waitq *w = &waitq_head; w->next != NULL; w = w->next) + if (w->next == &wq) + { + w->next = wq.next; + break; + } + sync_proc_subproc.release (); + } + ForceCloseHandle1 (wq.thread_ev, wq_ev); } + } /* Terminate the wait_subproc thread. @@ -392,23 +391,9 @@ proc_terminate () /* Clear pending signal */ void __reg1 -sig_clear (int target_sig) +sig_clear (int sig) { - if (&_my_tls != _sig_tls) - sig_send (myself, -target_sig); - else - { - sigpacket *q; - sigpacket *save = sigq.save (); - sigq.reset (); - while ((q = sigq.next ())) - if (q->si.si_signo == target_sig) - { - q->si.si_signo = __SIGDELETE; - break; - } - sigq.restore (save); - } + sigq.clear (sig); } extern "C" int @@ -425,21 +410,10 @@ sigpending (sigset_t *mask) void __reg1 sig_dispatch_pending (bool fast) { - if (&_my_tls == _sig_tls) - { -#ifdef DEBUGGING - sigproc_printf ("exit_state %d, cur thread id %p, _sig_tls %p, sigq.start.next %p", - exit_state, GetCurrentThreadId (), _sig_tls, sigq.start.next); -#endif - return; - } - /* Non-atomically test for any signals pending and wake up wait_sig if any are found. It's ok if there's a race here since the next call to this function - should catch it. - FIXME: Eventually, wait_sig should wake up on its own to deal with pending - signals. */ - if (sigq.pending ()) + should catch it. */ + if (sigq.pending () && &_my_tls != _sig_tls) sig_send (myself, fast ? __SIGFLUSHFAST : __SIGFLUSH); } @@ -728,7 +702,6 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) if (wait_for_completion && si.si_signo != __SIGFLUSHFAST) _my_tls.call_signal_handler (); - goto out; out: if (communing && rc) @@ -1232,46 +1205,19 @@ talktome (siginfo_t *si) new cygthread (commune_process, size, si, "commune"); } +/* Add a packet to the beginning of the queue. + Should only be called from signal thread. */ void pending_signals::add (sigpacket& pack) { sigpacket *se; + se = sigs + pack.si.si_signo; if (se->si.si_signo) return; *se = pack; se->next = NULL; - if (end) - end->next = se; - end = se; - if (!start.next) - start.next = se; -} - -void -pending_signals::del () -{ - sigpacket *next = curr->next; - prev->next = next; - curr->si.si_signo = 0; -#ifdef DEBUGGING - curr->next = NULL; -#endif - if (end == curr) - end = prev; - curr = next; -} - -sigpacket * -pending_signals::next () -{ - sigpacket *res; - prev = curr; - if (!curr || !(curr = curr->next)) - res = NULL; - else - res = curr; - return res; + start.next = se; } /* Process signals by waiting for signal data to arrive in a pipe. @@ -1311,7 +1257,7 @@ wait_sig (VOID *) pack.mask = &dummy_mask; } - sigpacket *q; + sigpacket *q = &sigq.start; bool clearwait = false; switch (pack.si.si_signo) { @@ -1324,8 +1270,7 @@ wait_sig (VOID *) case __SIGPENDING: *pack.mask = 0; unsigned bit; - sigq.reset (); - while ((q = sigq.next ())) + while ((q = q->next)) if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo))) *pack.mask |= bit; break; @@ -1340,15 +1285,23 @@ wait_sig (VOID *) case __SIGNOHOLD: case __SIGFLUSH: case __SIGFLUSHFAST: - sigq.reset (); - while ((q = sigq.next ())) - { - int sig = q->si.si_signo; - if (sig == __SIGDELETE || q->process () > 0) - sigq.del (); - if (sig == SIGCHLD) - clearwait = true; - } + { + sigpacket *qnext; + /* Check the queue for signals. There will always be at least one + thing on the queue if this was a valid signal. */ + while ((qnext = q->next)) + { + if (qnext->si.si_signo && qnext->process () <= 0) + q = q->next; + else + { + q->next = qnext->next; + qnext->si.si_signo = 0; + } + } + if (pack.si.si_signo == SIGCHLD) + clearwait = true; + } break; case __SIGSETPGRP: init_console_handler (true); diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h index 9c70c2c2d..c4313f056 100644 --- a/winsup/cygwin/sigproc.h +++ b/winsup/cygwin/sigproc.h @@ -20,7 +20,7 @@ enum __SIGSTRACE = -(NSIG + 2), __SIGCOMMUNE = -(NSIG + 3), __SIGPENDING = -(NSIG + 4), - __SIGDELETE = -(NSIG + 5), + __SIGDELETE = -(NSIG + 5), /* Not currently used */ __SIGFLUSHFAST = -(NSIG + 6), __SIGHOLD = -(NSIG + 7), __SIGNOHOLD = -(NSIG + 8),