* DevNotes: Add entry cgf-000019.
* dcrt0.cc (do_exit): Just set exit_state to ES_EVENTS_TERMINATE and nuke call to events_terminate which just set a superfluous flag. * sigproc.cc (signal_exit_code): New variable. (setup_signal_exit): Define new function. (_cygtls::signal_exit): Remove accommodations for closing the signal pipe handle. (exit_thread): Just sleep if we're exiting. (wait_sig): If signal_exit_code is set, just handle bookkeeping signals and exit ReadFile loop if there is nothing more to process. Call signal_exit at end if signal_exit_code is non-zero. * sigproc.h (setup_signal_exit): Declare new function. * exceptions.cc (sigpacket::process): Use setup_signal_exit to control exiting due to a signal. (exception::handle): Ditto. Query exit_state rather than defunct exit_already to determine if we are exiting. * globals.cc (ES_SIGNAL_EXIT): New enum. * sync.h (lock_process::release): New function for explicitly unlocking muto. (lock_process::~lock_process): Use release method.
This commit is contained in:
parent
81f1868336
commit
871d0724fa
|
@ -1,3 +1,26 @@
|
||||||
|
2012-12-28 Christopher Faylor <me.cygwin2012@cgf.cx>
|
||||||
|
|
||||||
|
* DevNotes: Add entry cgf-000019.
|
||||||
|
* dcrt0.cc (do_exit): Just set exit_state to ES_EVENTS_TERMINATE and
|
||||||
|
nuke call to events_terminate which just set a superfluous flag.
|
||||||
|
* sigproc.cc (signal_exit_code): New variable.
|
||||||
|
(setup_signal_exit): Define new function.
|
||||||
|
(_cygtls::signal_exit): Remove accommodations for closing the signal
|
||||||
|
pipe handle.
|
||||||
|
(exit_thread): Just sleep if we're exiting.
|
||||||
|
(wait_sig): If signal_exit_code is set, just handle bookkeeping signals
|
||||||
|
and exit ReadFile loop if there is nothing more to process. Call
|
||||||
|
signal_exit at end if signal_exit_code is non-zero.
|
||||||
|
* sigproc.h (setup_signal_exit): Declare new function.
|
||||||
|
* exceptions.cc (sigpacket::process): Use setup_signal_exit to control
|
||||||
|
exiting due to a signal.
|
||||||
|
(exception::handle): Ditto. Query exit_state rather than defunct
|
||||||
|
exit_already to determine if we are exiting.
|
||||||
|
* globals.cc (ES_SIGNAL_EXIT): New enum.
|
||||||
|
* sync.h (lock_process::release): New function for explicitly unlocking
|
||||||
|
muto.
|
||||||
|
(lock_process::~lock_process): Use release method.
|
||||||
|
|
||||||
2012-12-27 Christopher Faylor <me.cygwin2012@cgf.cx>
|
2012-12-27 Christopher Faylor <me.cygwin2012@cgf.cx>
|
||||||
|
|
||||||
* fork.cc (child_info::prefork): Fix error message formatting.
|
* fork.cc (child_info::prefork): Fix error message formatting.
|
||||||
|
|
|
@ -1,3 +1,32 @@
|
||||||
|
2012-12-28 cgf-000019
|
||||||
|
|
||||||
|
(I forgot to mention that cgf-000018 was reverted. Although I never saw
|
||||||
|
a hang from this, I couldn't convince myself that one wasn't possible.)
|
||||||
|
|
||||||
|
This fix attempts to correct a deadlock where, when a true Windows
|
||||||
|
signal arrives, Windows creates a thread which "does stuff" and attempts
|
||||||
|
to exit. In the process of exiting Cygwin grabs the process lock. If
|
||||||
|
the signal thread has seen the signal and wants to exit, it can't
|
||||||
|
because the newly-created thread now holds it. But, since the new
|
||||||
|
thread is relying on the signal thread to release its process lock,
|
||||||
|
it exits and the process lock is never released.
|
||||||
|
|
||||||
|
To fix this, I removed calls to _cygtls::signal_exit in favor of
|
||||||
|
flagging that we were exiting by setting signal_exit_code (almost forgot
|
||||||
|
to mark that NO_COPY: that would have been fun). The new function
|
||||||
|
setup_signal_exit() now handles setting things up so that ReadFile loop
|
||||||
|
in wait_sig will do the right thing when it terminates. This function
|
||||||
|
may just Sleep indefinitely if a signal is being sent from a thread
|
||||||
|
other than the signal thread. wait_sig() was changed so that it will
|
||||||
|
essentially drop into asychronous-read-mode when a signal which exits
|
||||||
|
has been detected. The ReadFile loop is exited when we know that the
|
||||||
|
process is supposed to be exiting and there is nothing else in the
|
||||||
|
signal queue.
|
||||||
|
|
||||||
|
Although I never actually saw this happen, exit_thread() was also
|
||||||
|
changed to release the process lock and just sleep indefintely if it is
|
||||||
|
detected that we are exiting.
|
||||||
|
|
||||||
2012-12-21 cgf-000018
|
2012-12-21 cgf-000018
|
||||||
|
|
||||||
Re: cgf-000017
|
Re: cgf-000017
|
||||||
|
|
|
@ -1098,10 +1098,7 @@ do_exit (int status)
|
||||||
lock_process until_exit (true);
|
lock_process until_exit (true);
|
||||||
|
|
||||||
if (exit_state < ES_EVENTS_TERMINATE)
|
if (exit_state < ES_EVENTS_TERMINATE)
|
||||||
{
|
exit_state = ES_EVENTS_TERMINATE;
|
||||||
exit_state = ES_EVENTS_TERMINATE;
|
|
||||||
events_terminate ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exit_state < ES_SIGNAL)
|
if (exit_state < ES_SIGNAL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,8 +41,6 @@ static BOOL WINAPI ctrl_c_handler (DWORD);
|
||||||
|
|
||||||
/* This is set to indicate that we have already exited. */
|
/* This is set to indicate that we have already exited. */
|
||||||
|
|
||||||
static NO_COPY int exit_already = 0;
|
|
||||||
|
|
||||||
NO_COPY static struct
|
NO_COPY static struct
|
||||||
{
|
{
|
||||||
unsigned int code;
|
unsigned int code;
|
||||||
|
@ -481,9 +479,9 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we've already exited, don't do anything here. Returning 1
|
/* If we're exiting, don't do anything here. Returning 1
|
||||||
tells Windows to keep looking for an exception handler. */
|
tells Windows to keep looking for an exception handler. */
|
||||||
if (exit_already || e->ExceptionFlags)
|
if (exit_state || e->ExceptionFlags)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
siginfo_t si = {0};
|
siginfo_t si = {0};
|
||||||
|
@ -673,8 +671,7 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void
|
||||||
error_code);
|
error_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flag signal + core dump */
|
setup_signal_exit ((cygheap->rlim_core > 0UL ? 0x80 : 0) | si.si_signo);
|
||||||
me.signal_exit ((cygheap->rlim_core > 0UL ? 0x80 : 0) | si.si_signo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS
|
si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS
|
||||||
|
@ -1249,13 +1246,9 @@ done:
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
exit_sig:
|
exit_sig:
|
||||||
tls->signal_exit (si.si_signo); /* never returns */
|
sigproc_printf ("setting up for exit with signal %d", si.si_signo);
|
||||||
}
|
setup_signal_exit (si.si_signo);
|
||||||
|
return rc;
|
||||||
void
|
|
||||||
events_terminate ()
|
|
||||||
{
|
|
||||||
exit_already = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -34,6 +34,7 @@ UINT system_wow64_directory_length;
|
||||||
enum exit_states
|
enum exit_states
|
||||||
{
|
{
|
||||||
ES_NOT_EXITING = 0,
|
ES_NOT_EXITING = 0,
|
||||||
|
ES_SIGNAL_EXIT,
|
||||||
ES_EXIT_STARTING,
|
ES_EXIT_STARTING,
|
||||||
ES_PROCESS_LOCKED,
|
ES_PROCESS_LOCKED,
|
||||||
ES_EVENTS_TERMINATE,
|
ES_EVENTS_TERMINATE,
|
||||||
|
|
|
@ -61,6 +61,7 @@ _cygtls NO_COPY *_sig_tls;
|
||||||
|
|
||||||
Static HANDLE my_sendsig;
|
Static HANDLE my_sendsig;
|
||||||
Static HANDLE my_readsig;
|
Static HANDLE my_readsig;
|
||||||
|
Static int signal_exit_code;
|
||||||
|
|
||||||
/* Function declarations */
|
/* Function declarations */
|
||||||
static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1)));
|
static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1)));
|
||||||
|
@ -361,32 +362,12 @@ close_my_readsig ()
|
||||||
ForceCloseHandle1 (h, my_readsig);
|
ForceCloseHandle1 (h, my_readsig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cover function to `do_exit' to handle exiting even in presence of more
|
/* Exit due to a signal, even in presence of more exceptions. We used to just
|
||||||
exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
|
call exit, but a SIGSEGV shouldn't cause atexit routines to run.
|
||||||
routines to run. */
|
Should only be called from the signal thread. */
|
||||||
void
|
void
|
||||||
_cygtls::signal_exit (int rc)
|
_cygtls::signal_exit (int rc)
|
||||||
{
|
{
|
||||||
HANDLE myss = my_sendsig;
|
|
||||||
my_sendsig = NULL; /* Make no_signals_allowed return true */
|
|
||||||
|
|
||||||
/* This code used to try to always close my_readsig but it ended up
|
|
||||||
blocking for reasons that people in google think make sense.
|
|
||||||
It's possible that it was blocking because ReadFile was still active
|
|
||||||
but it isn't clear why this only caused random hangs rather than
|
|
||||||
consistent hangs. So, for now at least, avoid closing my_readsig
|
|
||||||
unless this is the signal thread. */
|
|
||||||
if (&_my_tls == _sig_tls)
|
|
||||||
close_my_readsig (); /* Stop any currently executing sig_sends */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sigpacket sp = {};
|
|
||||||
sp.si.si_signo = __SIGEXIT;
|
|
||||||
DWORD len;
|
|
||||||
/* Write a packet to the wait_sig thread which tells it to exit and
|
|
||||||
close my_readsig. */
|
|
||||||
WriteFile (myss, &sp, sizeof (sp), &len, NULL);
|
|
||||||
}
|
|
||||||
signal_debugger (rc & 0x7f);
|
signal_debugger (rc & 0x7f);
|
||||||
|
|
||||||
if (rc == SIGQUIT || rc == SIGABRT)
|
if (rc == SIGQUIT || rc == SIGABRT)
|
||||||
|
@ -553,6 +534,27 @@ sigproc_terminate (exit_states es)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set up stuff so that the signal thread will know that we are
|
||||||
|
exiting due to a signal. */
|
||||||
|
void
|
||||||
|
setup_signal_exit (int sig)
|
||||||
|
{
|
||||||
|
signal_exit_code = sig; /* Tell wait_sig() that we are exiting. */
|
||||||
|
exit_state = ES_SIGNAL_EXIT; /* Tell the rest of the world that we are exiting. */
|
||||||
|
|
||||||
|
if (&_my_tls != _sig_tls)
|
||||||
|
{
|
||||||
|
sigpacket sp = {};
|
||||||
|
sp.si.si_signo = __SIGEXIT;
|
||||||
|
DWORD len;
|
||||||
|
/* Write a packet to the wait_sig thread. It will eventuall cause
|
||||||
|
the process to exit too. So just wait for that to happen after
|
||||||
|
sending the packet. */
|
||||||
|
WriteFile (my_sendsig, &sp, sizeof (sp), &len, NULL);
|
||||||
|
Sleep (INFINITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Exit the current thread very carefully.
|
/* Exit the current thread very carefully.
|
||||||
See cgf-000017 in DevNotes for more details on why this is
|
See cgf-000017 in DevNotes for more details on why this is
|
||||||
necessary. */
|
necessary. */
|
||||||
|
@ -576,10 +578,16 @@ exit_thread (DWORD res)
|
||||||
siginfo_t si = {__SIGTHREADEXIT, SI_KERNEL};
|
siginfo_t si = {__SIGTHREADEXIT, SI_KERNEL};
|
||||||
si.si_value.sival_ptr = h;
|
si.si_value.sival_ptr = h;
|
||||||
lock_process for_now; /* May block indefinitely if we're exiting. */
|
lock_process for_now; /* May block indefinitely if we're exiting. */
|
||||||
|
if (exit_state)
|
||||||
|
{
|
||||||
|
for_now.release ();
|
||||||
|
Sleep (INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
/* Tell wait_sig to wait for this thread to exit. It can then release
|
/* Tell wait_sig to wait for this thread to exit. It can then release
|
||||||
the lock below and close the above-opened handle. */
|
the lock below and close the above-opened handle. */
|
||||||
sig_send (myself_nowait, si, &_my_tls);
|
sig_send (myself_nowait, si, &_my_tls);
|
||||||
ExitThread (0); /* Should never hit this */
|
ExitThread (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __stdcall
|
int __stdcall
|
||||||
|
@ -1368,6 +1376,7 @@ pending_signals::next ()
|
||||||
static void WINAPI
|
static void WINAPI
|
||||||
wait_sig (VOID *)
|
wait_sig (VOID *)
|
||||||
{
|
{
|
||||||
|
extern int signal_exit_code;
|
||||||
_sig_tls = &_my_tls;
|
_sig_tls = &_my_tls;
|
||||||
sig_hold = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
|
sig_hold = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
|
@ -1380,7 +1389,14 @@ wait_sig (VOID *)
|
||||||
{
|
{
|
||||||
if (pack.si.si_signo == __SIGHOLD)
|
if (pack.si.si_signo == __SIGHOLD)
|
||||||
WaitForSingleObject (sig_hold, INFINITE);
|
WaitForSingleObject (sig_hold, INFINITE);
|
||||||
|
|
||||||
DWORD nb;
|
DWORD nb;
|
||||||
|
/* If signal_exit_code is set then we are shutting down due to a signal.
|
||||||
|
We'll exit this loop iff there is nothing more in the signal queue. */
|
||||||
|
if (signal_exit_code
|
||||||
|
&& (!PeekNamedPipe (my_readsig, NULL, 0, NULL, &nb, NULL) || !nb))
|
||||||
|
break;
|
||||||
|
|
||||||
pack.sigtls = NULL;
|
pack.sigtls = NULL;
|
||||||
if (!ReadFile (my_readsig, &pack, sizeof (pack), &nb, NULL))
|
if (!ReadFile (my_readsig, &pack, sizeof (pack), &nb, NULL))
|
||||||
break;
|
break;
|
||||||
|
@ -1400,6 +1416,9 @@ wait_sig (VOID *)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal_exit_code && pack.si.si_signo > 0)
|
||||||
|
continue; /* No more real signals allowed */
|
||||||
|
|
||||||
sigset_t dummy_mask;
|
sigset_t dummy_mask;
|
||||||
if (!pack.mask)
|
if (!pack.mask)
|
||||||
{
|
{
|
||||||
|
@ -1505,8 +1524,14 @@ wait_sig (VOID *)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
close_my_readsig ();
|
|
||||||
sigproc_printf ("signal thread exiting");
|
sigproc_printf ("signal thread exiting");
|
||||||
|
|
||||||
|
my_sendsig = NULL; /* Make no_signals_allowed return true */
|
||||||
|
close_my_readsig (); /* Cause any sig_send's to stop */
|
||||||
|
|
||||||
|
if (signal_exit_code)
|
||||||
|
_my_tls.signal_exit (signal_exit_code);
|
||||||
|
|
||||||
/* Just wait for the process to go away. Otherwise, this thread's
|
/* Just wait for the process to go away. Otherwise, this thread's
|
||||||
exit value could be interpreted as the process exit value.
|
exit value could be interpreted as the process exit value.
|
||||||
See cgf-000017 in DevNotes for more details. */
|
See cgf-000017 in DevNotes for more details. */
|
||||||
|
|
|
@ -89,6 +89,7 @@ void __stdcall sigalloc ();
|
||||||
int kill_pgrp (pid_t, siginfo_t&);
|
int kill_pgrp (pid_t, siginfo_t&);
|
||||||
int killsys (pid_t, int);
|
int killsys (pid_t, int);
|
||||||
void exit_thread (DWORD) __attribute__ ((regparm (1), noreturn));
|
void exit_thread (DWORD) __attribute__ ((regparm (1), noreturn));
|
||||||
|
void setup_signal_exit (int) __attribute__ ((regparm (1)));
|
||||||
|
|
||||||
extern "C" void sigdelayed ();
|
extern "C" void sigdelayed ();
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,15 @@ public:
|
||||||
if (exiting && exit_state < ES_PROCESS_LOCKED)
|
if (exiting && exit_state < ES_PROCESS_LOCKED)
|
||||||
exit_state = ES_PROCESS_LOCKED;
|
exit_state = ES_PROCESS_LOCKED;
|
||||||
}
|
}
|
||||||
|
void release ()
|
||||||
|
{
|
||||||
|
locker.release ();
|
||||||
|
skip_unlock = true;
|
||||||
|
}
|
||||||
~lock_process ()
|
~lock_process ()
|
||||||
{
|
{
|
||||||
if (!skip_unlock)
|
if (!skip_unlock)
|
||||||
locker.release ();
|
release ();
|
||||||
}
|
}
|
||||||
static void force_release (_cygtls *tid) {locker.release (tid);}
|
static void force_release (_cygtls *tid) {locker.release (tid);}
|
||||||
friend class dtable;
|
friend class dtable;
|
||||||
|
|
Loading…
Reference in New Issue