* DevNotes: Add entry cgf-000017.

* _cygtls.cc (_cygtls::call2): Use new exit_thread function in place of
ExitThread.
* miscfuncs.cc (thread_wrapper): Ditto.
* thread.cc (pthread::exit): Ditto.
(pthread_mutex::unlock): Set tid to NULL rather than 0.
(pthread_spinlock::unlock): Ditto.
* pinfo.cc (commune_process): Actually call lock_process constructor.
* sigproc.cc (exit_thread): New function.
(wait_sig): Handle __SIGTHREADEXIT case.  Don't just block rather than
returning from this function.
* sigproc.h (__SIGTHREADEXIT): New enum.
(exit_thread): Declare.
* sync.cc (muto::release): Accept a tls command-line argument.
* sync.h (muto::release): Accept a tls command-line parameter.  Default to
&_my_tls.
This commit is contained in:
Christopher Faylor 2012-12-21 18:52:00 +00:00
parent dfbb1d0383
commit 614aff88a0
10 changed files with 106 additions and 13 deletions

View File

@ -1,3 +1,22 @@
2012-12-21 Christopher Faylor <me.cygwin2012@cgf.cx>
* DevNotes: Add entry cgf-000017.
* _cygtls.cc (_cygtls::call2): Use new exit_thread function in place of
ExitThread.
* miscfuncs.cc (thread_wrapper): Ditto.
* thread.cc (pthread::exit): Ditto.
(pthread_mutex::unlock): Set tid to NULL rather than 0.
(pthread_spinlock::unlock): Ditto.
* pinfo.cc (commune_process): Actually call lock_process constructor.
* sigproc.cc (exit_thread): New function.
(wait_sig): Handle __SIGTHREADEXIT case. Don't just block rather than
returning from this function.
* sigproc.h (__SIGTHREADEXIT): New enum.
(exit_thread): Declare.
* sync.cc (muto::release): Accept a tls command-line argument.
* sync.h (muto::release): Accept a tls command-line parameter. Default
to &_my_tls.
2012-12-20 Corinna Vinschen <corinna@vinschen.de>
* dcrt0.cc (build_argv): Allow quoted filenames in @ expression.

View File

@ -1,3 +1,27 @@
2012-12-21 cgf-000017
The changes in this set are to work around the issue noted here:
http://cygwin.com/ml/cygwin/2012-12/threads.html#00140
The problem is, apparently, that the return value of an ExitThread()
will take precedence over the return value of TerminateProcess/ExitProcess
if the thread is the last one exiting. That's rather amazing...
For the fix, I replaced all calls to ExitThread with exit_thread(). The
exit_thread function, creates a handle to the current thread and sends
it to a packet via sig_send(__SIGTHREADEXIT). Then it acquires the
process lock and calls ExitThread.
wait_sig will then wait for the handle, indicating that the thread has
exited, and, when that has happened, remove the process lock on behalf
of the now-defunct thread. wait_sig will now also avoid actually
exiting since it could trigger the same problem.
Holding process_lock should prevent threads from exiting while a Cygwin
process is shutting down. They will just block forever in that case -
just like wait_sig.
2012-08-17 cgf-000016
While debugging another problem I finally noticed that

View File

@ -102,7 +102,7 @@ _cygtls::call2 (DWORD (*func) (void *, void *), void *arg, void *buf)
dynamically loaded. */
if ((void *) func != (void *) dll_crt0_1
&& (void *) func != (void *) dll_dllcrt0_1)
ExitThread (res);
exit_thread (res);
}
void

View File

@ -27,6 +27,7 @@ details. */
#include "dtable.h"
#include "cygheap.h"
#include "pinfo.h"
#include "sigproc.h"
#include "exception.h"
long tls_ix = -1;
@ -546,7 +547,7 @@ thread_wrapper (VOID *arg)
: : [WRAPPER_ARG] "r" (&wrapper_arg),
[CYGTLS] "i" (CYGTLS_PADSIZE));
/* Never return from here. */
ExitThread (0);
exit_thread (0);
}
HANDLE WINAPI

View File

@ -515,7 +515,7 @@ commune_process (void *arg)
if (process_sync) // FIXME: this test shouldn't be necessary
ProtectHandle (process_sync);
lock_process now ();
lock_process now;
if (si._si_commune._si_code & PICOM_EXTRASTR)
si._si_commune._si_str = (char *) (&si + 1);

View File

@ -553,6 +553,33 @@ sigproc_terminate (exit_states es)
}
}
/* Exit the current thread very carefully.
See cgf-000017 in DevNotes for more details on why this is
necessary. */
void
exit_thread (DWORD res)
{
HANDLE h;
if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
GetCurrentProcess (), &h,
0, FALSE, DUPLICATE_SAME_ACCESS))
{
#ifdef DEBUGGING
system_printf ("couldn't duplicate the current thread, %E");
#endif
ExitThread (res);
}
ProtectHandle1 (h, exit_thread);
siginfo_t si = {__SIGTHREADEXIT, SI_KERNEL};
si.si_value.sival_ptr = h;
/* Tell wait_sig to wait for this thread to exit. It can then release
the lock below and close the above-opened handle. */
sig_send (myself_nowait, si, &_my_tls);
lock_process for_now;
ExitThread (0); /* Should never hit this */
}
int __stdcall
sig_send (_pinfo *p, int sig, _cygtls *tid)
{
@ -1419,6 +1446,23 @@ wait_sig (VOID *)
case __SIGSETPGRP:
init_console_handler (true);
break;
case __SIGTHREADEXIT:
{
/* Serialize thread exit as the thread exit code can be interpreted
as the process exit code in some cases when racing with
ExitProcess/TerminateProcess.
So, wait for the thread which sent this signal to exit, then
release the process lock which it held and close it's handle.
See cgf-000017 in DevNotes for more details.
*/
HANDLE h = (HANDLE) pack.si.si_value.sival_ptr;
DWORD res = WaitForSingleObject (h, 5000);
lock_process::force_release (pack.sigtls);
ForceCloseHandle1 (h, exit_thread);
if (res != WAIT_OBJECT_0)
system_printf ("WaitForSingleObject(%p) for thread exit returned %u", h, res);
}
break;
default:
if (pack.si.si_signo < 0)
sig_clear (-pack.si.si_signo);
@ -1461,5 +1505,8 @@ wait_sig (VOID *)
close_my_readsig ();
sigproc_printf ("signal thread exiting");
ExitThread (0);
/* Just wait for the process to go away. Otherwise, this thread's
exit value could be interpreted as the process exit value.
See cgf-000017 in DevNotes for more details. */
Sleep (INFINITE);
}

View File

@ -25,7 +25,8 @@ enum
__SIGHOLD = -(NSIG + 7),
__SIGNOHOLD = -(NSIG + 8),
__SIGEXIT = -(NSIG + 9),
__SIGSETPGRP = -(NSIG + 10)
__SIGSETPGRP = -(NSIG + 10),
__SIGTHREADEXIT = -(NSIG + 11)
};
#endif
@ -87,6 +88,7 @@ void __stdcall sigalloc ();
int kill_pgrp (pid_t, siginfo_t&);
int killsys (pid_t, int);
void exit_thread (DWORD) __attribute__ ((regparm(1), noreturn));
extern "C" void sigdelayed ();

View File

@ -4,7 +4,8 @@
which is intended to operate similarly to a mutex but attempts to
avoid making expensive calls to the kernel.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010 Red Hat, Inc.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010, 2011, 2012
Red Hat, Inc.
This file is part of Cygwin.
@ -109,10 +110,8 @@ muto::acquired ()
/* Return the muto lock. Needs to be called once per every acquire. */
int
muto::release ()
muto::release (_cygtls *this_tls)
{
void *this_tls = &_my_tls;
if (tls != this_tls || !visits)
{
SetLastError (ERROR_NOT_OWNER); /* Didn't have the lock. */

View File

@ -33,7 +33,7 @@ public:
~muto ()
#endif
int acquire (DWORD ms = INFINITE) __attribute__ ((regparm (2))); /* Acquire the lock. */
int release () __attribute__ ((regparm (1))); /* Release the lock. */
int release (_cygtls * = &_my_tls) __attribute__ ((regparm (2))); /* Release the lock. */
bool acquired () __attribute__ ((regparm (1)));
void upforgrabs () {tls = this;} // just set to an invalid address
@ -60,6 +60,7 @@ public:
if (!skip_unlock)
locker.release ();
}
static void force_release (_cygtls *tid) {locker.release (tid);}
friend class dtable;
friend class fhandler_fifo;
};

View File

@ -532,7 +532,7 @@ pthread::exit (void *value_ptr)
_main_tls = dummy;
_main_tls->initialized = false;
}
ExitThread (0);
exit_thread (0);
}
}
@ -1778,7 +1778,7 @@ pthread_mutex::unlock ()
{
owner = (pthread_t) _unlocked_mutex;
#ifdef DEBUGGING
tid = 0;
tid = NULL;
#endif
if (InterlockedDecrement ((long *) &lock_counter))
::SetEvent (win32_obj_id); // Another thread is waiting
@ -1905,7 +1905,7 @@ pthread_spinlock::unlock ()
{
owner = (pthread_t) _unlocked_mutex;
#ifdef DEBUGGING
tid = 0;
tid = NULL;
#endif
InterlockedExchange ((long *) &lock_counter, 0);
::SetEvent (win32_obj_id);