* cygheap.cc (init_cygheap::init_tls_list): Accommodate threadlist

having a new type threadlist_t *.  Convert commented out code into an
	#if 0.  Create thread mutex.  Explain why.
	(init_cygheap::remove_tls): Drop timeout value.  Always wait infinitely
	for tls_sentry.  Return mutex HANDLE of just deleted threadlist entry.
	(init_cygheap::find_tls): New implementation taking tls pointer as
	search parameter.  Return threadlist_t *.
	(init_cygheap::find_tls): Return threadlist_t *.  Define ix as auto
	variable.  Drop exception handling since crash must be made impossible
	due to correct synchronization.  Return with locked mutex.
	* cygheap.h (struct threadlist_t): Define.
	(struct init_cygheap): Convert threadlist to threadlist_t type.
	(init_cygheap::remove_tls): Align declaration to above change.
	(init_cygheap::find_tls): Ditto.
	(init_cygheap::unlock_tls): Define.
	* cygtls.cc (_cygtls::remove): Unlock and close mutex when finishing.
	* exceptions.cc (sigpacket::process): Lock _cygtls area of thread before
	accessing it.
	* fhandler_termios.cc (fhandler_termios::bg_check): Ditto.
	* sigproc.cc (sig_send): Ditto.
	* thread.cc (pthread::exit): Ditto.  Add comment.
	(pthread::cancel): Ditto.
This commit is contained in:
Corinna Vinschen 2014-11-28 20:46:13 +00:00
parent c2f50c4099
commit 26158dc3e9
8 changed files with 200 additions and 67 deletions

View File

@ -1,3 +1,28 @@
2014-11-28 Corinna Vinschen <corinna@vinschen.de>
* cygheap.cc (init_cygheap::init_tls_list): Accommodate threadlist
having a new type threadlist_t *. Convert commented out code into an
#if 0. Create thread mutex. Explain why.
(init_cygheap::remove_tls): Drop timeout value. Always wait infinitely
for tls_sentry. Return mutex HANDLE of just deleted threadlist entry.
(init_cygheap::find_tls): New implementation taking tls pointer as
search parameter. Return threadlist_t *.
(init_cygheap::find_tls): Return threadlist_t *. Define ix as auto
variable. Drop exception handling since crash must be made impossible
due to correct synchronization. Return with locked mutex.
* cygheap.h (struct threadlist_t): Define.
(struct init_cygheap): Convert threadlist to threadlist_t type.
(init_cygheap::remove_tls): Align declaration to above change.
(init_cygheap::find_tls): Ditto.
(init_cygheap::unlock_tls): Define.
* cygtls.cc (_cygtls::remove): Unlock and close mutex when finishing.
* exceptions.cc (sigpacket::process): Lock _cygtls area of thread before
accessing it.
* fhandler_termios.cc (fhandler_termios::bg_check): Ditto.
* sigproc.cc (sig_send): Ditto.
* thread.cc (pthread::exit): Ditto. Add comment.
(pthread::cancel): Ditto.
2014-11-28 Corinna Vinschen <corinna@vinschen.de> 2014-11-28 Corinna Vinschen <corinna@vinschen.de>
* cygheap.cc (init_cygheap::find_tls): Add comment. * cygheap.cc (init_cygheap::find_tls): Add comment.

View File

@ -617,8 +617,9 @@ init_cygheap::init_tls_list ()
else else
{ {
sthreads = THREADLIST_CHUNK; sthreads = THREADLIST_CHUNK;
threadlist = (_cygtls **) ccalloc_abort (HEAP_TLS, cygheap->sthreads, threadlist = (threadlist_t *)
sizeof (cygheap->threadlist[0])); ccalloc_abort (HEAP_TLS, cygheap->sthreads,
sizeof (cygheap->threadlist[0]));
} }
tls_sentry::lock.init ("thread_tls_sentry"); tls_sentry::lock.init ("thread_tls_sentry");
} }
@ -630,72 +631,116 @@ init_cygheap::add_tls (_cygtls *t)
tls_sentry here (INFINITE); tls_sentry here (INFINITE);
if (nthreads >= cygheap->sthreads) if (nthreads >= cygheap->sthreads)
{ {
threadlist = (_cygtls **) threadlist = (threadlist_t *)
crealloc_abort (threadlist, (sthreads += THREADLIST_CHUNK) crealloc_abort (threadlist, (sthreads += THREADLIST_CHUNK)
* sizeof (threadlist[0])); * sizeof (threadlist[0]));
// memset (threadlist + nthreads, 0, THREADLIST_CHUNK * sizeof (threadlist[0])); #if 0
memset (threadlist + nthreads, 0,
THREADLIST_CHUNK * sizeof (threadlist[0]));
#endif
} }
threadlist[nthreads++] = t; /* Create a mutex to lock the thread's _cygtls area. This is required for
the following reason: The thread's _cygtls area is on the thread's
own stack. Thus, when the thread exits, its _cygtls area is automatically
destroyed by the OS. Thus, when this happens while the signal thread
still utilizes the thread's _cygtls area, things go awry.
The following methods take this into account:
- The thread mutex is generally only locked under tls_sentry locking.
- remove_tls, called from _cygtls::remove, locks the mutex before
removing the threadlist entry and _cygtls::remove then unlocks and
destroyes the mutex.
- find_tls, called from several places but especially from the signal
thread, will lock the mutex on exit and the caller can access the
_cygtls area locked. Always make sure to unlock the mutex when the
_cygtls area isn't needed anymore. */
threadlist[nthreads].thread = t;
threadlist[nthreads].mutex = CreateMutexW (&sec_none_nih, FALSE, NULL);
if (!threadlist[nthreads].mutex)
api_fatal ("Can't create per-thread mutex, %E");
++nthreads;
} }
void HANDLE __reg3
init_cygheap::remove_tls (_cygtls *t, DWORD wait) init_cygheap::remove_tls (_cygtls *t)
{ {
tls_sentry here (wait); HANDLE mutex = NULL;
tls_sentry here (INFINITE);
if (here.acquired ()) if (here.acquired ())
{ {
for (uint32_t i = 0; i < nthreads; i++) for (uint32_t i = 0; i < nthreads; i++)
if (t == threadlist[i]) if (t == threadlist[i].thread)
{ {
mutex = threadlist[i].mutex;
WaitForSingleObject (mutex, INFINITE);
if (i < --nthreads) if (i < --nthreads)
threadlist[i] = threadlist[nthreads]; threadlist[i] = threadlist[nthreads];
debug_only_printf ("removed %p element %u", this, i); debug_only_printf ("removed %p element %u", this, i);
break; break;
} }
} }
/* Leave with locked mutex. The calling function is responsible for
unlocking the mutex. */
return mutex;
} }
_cygtls __reg3 * threadlist_t __reg2 *
init_cygheap::find_tls (_cygtls *tls)
{
tls_sentry here (INFINITE);
threadlist_t *t = NULL;
int ix = -1;
while (++ix < (int) nthreads)
{
if (!threadlist[ix].thread->tid
|| !threadlist[ix].thread->initialized)
;
if (threadlist[ix].thread == tls)
{
t = &threadlist[ix];
break;
}
}
/* Leave with locked mutex. The calling function is responsible for
unlocking the mutex. */
if (t)
WaitForSingleObject (t->mutex, INFINITE);
return t;
}
threadlist_t __reg3 *
init_cygheap::find_tls (int sig, bool& issig_wait) init_cygheap::find_tls (int sig, bool& issig_wait)
{ {
debug_printf ("sig %d\n", sig); debug_printf ("sig %d\n", sig);
tls_sentry here (INFINITE); tls_sentry here (INFINITE);
static int NO_COPY ix; threadlist_t *t = NULL;
_cygtls *t = NULL;
issig_wait = false; issig_wait = false;
ix = -1; int ix = -1;
/* Scan thread list looking for valid signal-delivery candidates */ /* Scan thread list looking for valid signal-delivery candidates */
while (++ix < (int) nthreads) while (++ix < (int) nthreads)
{ {
__try /* Only pthreads have tid set to non-0. */
if (!threadlist[ix].thread->tid
|| !threadlist[ix].thread->initialized)
;
else if (sigismember (&(threadlist[ix].thread->sigwait_mask), sig))
{ {
/* Only pthreads have tid set to non-0. */ t = &cygheap->threadlist[ix];
if (!threadlist[ix]->tid) issig_wait = true;
continue; break;
else if (sigismember (&(threadlist[ix]->sigwait_mask), sig))
{
t = cygheap->threadlist[ix];
issig_wait = true;
__leave;
}
else if (!t && !sigismember (&(threadlist[ix]->sigmask), sig))
t = cygheap->threadlist[ix];
} }
__except (NO_ERROR) else if (!t && !sigismember (&(threadlist[ix].thread->sigmask), sig))
{ t = &cygheap->threadlist[ix];
/* We're here because threadlist[ix] became NULL or invalid. In
theory this should be impossible due to correct synchronization.
But *if* it happens, just remove threadlist[ix] from threadlist.
TODO: This should be totally unnecessary. */
debug_printf ("cygtls synchronization is leaking...");
remove_tls (threadlist[ix], 0);
--ix;
}
__endtry
} }
/* Leave with locked mutex. The calling function is responsible for
unlocking the mutex. */
if (t)
WaitForSingleObject (t->mutex, INFINITE);
return t; return t;
} }

View File

@ -534,6 +534,14 @@ struct mini_cygheap
#define NBUCKETS 40 #define NBUCKETS 40
struct threadlist_t
{
struct _cygtls *thread;
HANDLE mutex; /* Used to avoid accessing tls area of
deleted thread. See comment in
cygheap::remove_tls for a description. */
};
struct init_cygheap: public mini_cygheap struct init_cygheap: public mini_cygheap
{ {
_cmalloc_entry *chain; _cmalloc_entry *chain;
@ -561,7 +569,7 @@ struct init_cygheap: public mini_cygheap
struct sigaction *sigs; struct sigaction *sigs;
fhandler_termios *ctty; /* Current tty */ fhandler_termios *ctty; /* Current tty */
struct _cygtls **threadlist; threadlist_t *threadlist;
uint32_t sthreads; uint32_t sthreads;
pid_t pid; /* my pid */ pid_t pid; /* my pid */
struct { /* Equivalent to using LIST_HEAD. */ struct { /* Equivalent to using LIST_HEAD. */
@ -572,8 +580,10 @@ struct init_cygheap: public mini_cygheap
void init_installation_root (); void init_installation_root ();
void __reg1 init_tls_list ();; void __reg1 init_tls_list ();;
void __reg2 add_tls (_cygtls *); void __reg2 add_tls (_cygtls *);
void __reg3 remove_tls (_cygtls *, DWORD); HANDLE __reg3 remove_tls (_cygtls *);
_cygtls __reg3 *find_tls (int, bool&); threadlist_t __reg2 *find_tls (_cygtls *);
threadlist_t __reg3 *find_tls (int, bool&);
void unlock_tls (threadlist_t *t) { if (t) ReleaseMutex (t->mutex); }
}; };

View File

@ -180,7 +180,7 @@ _cygtls::remove (DWORD wait)
debug_printf ("wait %u", wait); debug_printf ("wait %u", wait);
cygheap->remove_tls (this, INFINITE); HANDLE mutex = cygheap->remove_tls (this);
remove_wq (wait); remove_wq (wait);
/* FIXME: Need some sort of atthreadexit function to allow things like /* FIXME: Need some sort of atthreadexit function to allow things like
@ -211,6 +211,11 @@ _cygtls::remove (DWORD wait)
/* Close timer handle. */ /* Close timer handle. */
if (locals.cw_timer) if (locals.cw_timer)
NtClose (locals.cw_timer); NtClose (locals.cw_timer);
if (mutex)
{
ReleaseMutex (mutex);
CloseHandle (mutex);
}
} }
#ifdef __x86_64__ #ifdef __x86_64__

View File

@ -1299,6 +1299,9 @@ sigpacket::process ()
struct sigaction& thissig = global_sigs[si.si_signo]; struct sigaction& thissig = global_sigs[si.si_signo];
void *handler = have_execed ? NULL : (void *) thissig.sa_handler; void *handler = have_execed ? NULL : (void *) thissig.sa_handler;
threadlist_t *tl_entry = NULL;
_cygtls *tls = NULL;
/* Don't try to send signals if we're just starting up since signal masks /* Don't try to send signals if we're just starting up since signal masks
may not be available. */ may not be available. */
if (!cygwin_finished_initializing) if (!cygwin_finished_initializing)
@ -1311,12 +1314,19 @@ sigpacket::process ()
myself->rusage_self.ru_nsignals++; myself->rusage_self.ru_nsignals++;
_cygtls *tls;
if (si.si_signo == SIGCONT) if (si.si_signo == SIGCONT)
_main_tls->handle_SIGCONT (); {
tl_entry = cygheap->find_tls (_main_tls);
_main_tls->handle_SIGCONT ();
cygheap->unlock_tls (tl_entry);
}
/* SIGKILL is special. It always goes through. */
if (si.si_signo == SIGKILL) if (si.si_signo == SIGKILL)
tls = _main_tls; /* SIGKILL is special. It always goes through. */ {
tl_entry = cygheap->find_tls (_main_tls);
tls = _main_tls;
}
else if (ISSTATE (myself, PID_STOPPED)) else if (ISSTATE (myself, PID_STOPPED))
{ {
rc = -1; /* Don't send signals when stopped */ rc = -1; /* Don't send signals when stopped */
@ -1324,18 +1334,29 @@ sigpacket::process ()
} }
else if (!sigtls) else if (!sigtls)
{ {
tls = cygheap->find_tls (si.si_signo, issig_wait); tl_entry = cygheap->find_tls (si.si_signo, issig_wait);
sigproc_printf ("using tls %p", tls); if (tl_entry)
{
tls = tl_entry->thread;
sigproc_printf ("using tls %p", tls);
}
} }
else else
{ {
tls = sigtls; tl_entry = cygheap->find_tls (sigtls);
if (sigismember (&tls->sigwait_mask, si.si_signo)) if (tl_entry)
issig_wait = true; {
else if (!sigismember (&tls->sigmask, si.si_signo)) tls = tl_entry->thread;
issig_wait = false; if (sigismember (&tls->sigwait_mask, si.si_signo))
else issig_wait = true;
tls = NULL; else if (!sigismember (&tls->sigmask, si.si_signo))
issig_wait = false;
else
{
cygheap->unlock_tls (tl_entry);
tls = NULL;
}
}
} }
/* !tls means no threads available to catch a signal. */ /* !tls means no threads available to catch a signal. */
@ -1371,19 +1392,22 @@ sigpacket::process ()
} }
/* Clear pending SIGCONT on stop signals */ /* Clear pending SIGCONT on stop signals */
if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU) if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
|| si.si_signo == SIGTTOU)
sig_clear (SIGCONT); sig_clear (SIGCONT);
if (handler == (void *) SIG_DFL) if (handler == (void *) SIG_DFL)
{ {
if (si.si_signo == SIGCHLD || si.si_signo == SIGIO || si.si_signo == SIGCONT || si.si_signo == SIGWINCH if (si.si_signo == SIGCHLD || si.si_signo == SIGIO
|| si.si_signo == SIGCONT || si.si_signo == SIGWINCH
|| si.si_signo == SIGURG) || si.si_signo == SIGURG)
{ {
sigproc_printf ("signal %d default is currently ignore", si.si_signo); sigproc_printf ("signal %d default is currently ignore", si.si_signo);
goto done; goto done;
} }
if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU) if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
|| si.si_signo == SIGTTOU)
goto stop; goto stop;
goto exit_sig; goto exit_sig;
@ -1395,7 +1419,12 @@ sigpacket::process ()
goto dosig; goto dosig;
stop: stop:
tls = _main_tls; if (tls != _main_tls)
{
cygheap->unlock_tls (tl_entry);
tl_entry = cygheap->find_tls (_main_tls);
tls = _main_tls;
}
handler = (void *) sig_handle_tty_stop; handler = (void *) sig_handle_tty_stop;
thissig = global_sigs[SIGSTOP]; thissig = global_sigs[SIGSTOP];
goto dosig; goto dosig;
@ -1415,6 +1444,7 @@ dosig:
rc = setup_handler (handler, thissig, tls); rc = setup_handler (handler, thissig, tls);
done: done:
cygheap->unlock_tls (tl_entry);
sigproc_printf ("returning %d", rc); sigproc_printf ("returning %d", rc);
return rc; return rc;

View File

@ -193,9 +193,12 @@ fhandler_termios::bg_check (int sig)
return bg_eof; return bg_eof;
} }
threadlist_t *tl_entry;
tl_entry = cygheap->find_tls (_main_tls);
int sigs_ignored = int sigs_ignored =
((void *) global_sigs[sig].sa_handler == (void *) SIG_IGN) || ((void *) global_sigs[sig].sa_handler == (void *) SIG_IGN) ||
(_main_tls->sigmask & SIGTOMASK (sig)); (_main_tls->sigmask & SIGTOMASK (sig));
cygheap->unlock_tls (tl_entry);
/* If the process is ignoring SIGTT*, then background IO is OK. If /* If the process is ignoring SIGTT*, then background IO is OK. If
the process is not ignoring SIGTT*, then the sig is to be sent to the process is not ignoring SIGTT*, then the sig is to be sent to

View File

@ -608,7 +608,11 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
else if (si.si_signo == __SIGPENDING) else if (si.si_signo == __SIGPENDING)
pack.mask = &pending; pack.mask = &pending;
else if (si.si_signo == __SIGFLUSH || si.si_signo > 0) else if (si.si_signo == __SIGFLUSH || si.si_signo > 0)
pack.mask = tls ? &tls->sigmask : &_main_tls->sigmask; {
threadlist_t *tl_entry = cygheap->find_tls (tls ? tls : _main_tls);
pack.mask = tls ? &tls->sigmask : &_main_tls->sigmask;
cygheap->unlock_tls (tl_entry);
}
else else
pack.mask = NULL; pack.mask = NULL;
@ -1259,9 +1263,12 @@ wait_sig (VOID *)
continue; continue;
sigset_t dummy_mask; sigset_t dummy_mask;
threadlist_t *tl_entry;
if (!pack.mask) if (!pack.mask)
{ {
tl_entry = cygheap->find_tls (_main_tls);
dummy_mask = _main_tls->sigmask; dummy_mask = _main_tls->sigmask;
cygheap->unlock_tls (tl_entry);
pack.mask = &dummy_mask; pack.mask = &dummy_mask;
} }
@ -1276,11 +1283,16 @@ wait_sig (VOID *)
strace.activate (false); strace.activate (false);
break; break;
case __SIGPENDING: case __SIGPENDING:
*pack.mask = 0; {
unsigned bit; unsigned bit;
while ((q = q->next))
if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo))) *pack.mask = 0;
*pack.mask |= bit; tl_entry = cygheap->find_tls (pack.sigtls);
while ((q = q->next))
if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo)))
*pack.mask |= bit;
cygheap->unlock_tls (tl_entry);
}
break; break;
case __SIGHOLD: case __SIGHOLD:
sig_held = true; sig_held = true;

View File

@ -515,7 +515,7 @@ void
pthread::exit (void *value_ptr) pthread::exit (void *value_ptr)
{ {
class pthread *thread = this; class pthread *thread = this;
bool is_main_tls = (cygtls == _main_tls); // Check cygtls before deleting this _cygtls *tls = cygtls; /* Save cygtls before deleting this. */
// run cleanup handlers // run cleanup handlers
pop_all_cleanup_handlers (); pop_all_cleanup_handlers ();
@ -541,15 +541,16 @@ pthread::exit (void *value_ptr)
::exit (0); ::exit (0);
else else
{ {
if (is_main_tls) if (tls == _main_tls)
{ {
/* FIXME: Needs locking. */ cygheap->find_tls (tls); /* Lock _main_tls mutex. */
_cygtls *dummy = (_cygtls *) malloc (sizeof (_cygtls)); _cygtls *dummy = (_cygtls *) malloc (sizeof (_cygtls));
*dummy = *_main_tls; *dummy = *_main_tls;
_main_tls = dummy; _main_tls = dummy;
_main_tls->initialized = 0; _main_tls->initialized = 0;
} }
cygtls->remove (INFINITE); /* This also unlocks and closes the _main_tls mutex. */
tls->remove (INFINITE);
ExitThread (0); ExitThread (0);
} }
} }
@ -595,6 +596,7 @@ pthread::cancel ()
and tends to hang infinitely if we change the instruction pointer. and tends to hang infinitely if we change the instruction pointer.
So just don't cancel asynchronously if the thread is currently So just don't cancel asynchronously if the thread is currently
executing Windows code. Rely on deferred cancellation in this case. */ executing Windows code. Rely on deferred cancellation in this case. */
threadlist_t *tl_entry = cygheap->find_tls (cygtls);
if (!cygtls->inside_kernel (&context)) if (!cygtls->inside_kernel (&context))
{ {
#ifdef __x86_64__ #ifdef __x86_64__
@ -604,6 +606,7 @@ pthread::cancel ()
#endif #endif
SetThreadContext (win32_obj_id, &context); SetThreadContext (win32_obj_id, &context);
} }
cygheap->unlock_tls (tl_entry);
} }
mutex.unlock (); mutex.unlock ();
/* See above. For instance, a thread which waits for a semaphore in sem_wait /* See above. For instance, a thread which waits for a semaphore in sem_wait