2d1d1eb1e4
compiler bug. * autoload.cc (TryEnterCriticalSection): Remove. * dcrt0.cc (dll_crt0_0): Delete inappropriate setting of _my_tls.stackptr to NULL since it has really bad consequences. Make 'si' an automatic variable. * cygtls.cc (_threadinfo::init_thread): Correct thinko which caused thread list to be allocated every time. * cygtls.h (CYGTLS_PADSIZE): Define as const int. * sync.h: Make multiple inclusion safe. (muto::next): Eliminate. (muto::exiting_thread): New variable. (muto::set_exiting_thread): New function. (new_muto): Change to use different section for mutos since c++ give inexplicable warning in some cases otherwise. (new_muto1): Ditto. * dcrt0.cc (do_exit): Call muto::set_exiting_thread here. * sync.cc (muto_start): Eliminate. (muto::acquire): Always give exiting thread a lock. Never give thread a lock if exiting. (muto::release): Ditto for releasing. * dtable.cc (dtable::init_lock): Unline function and define here. * dtable.h (lock_cs): Define as a muto since critical sections seem to work oddly on Windows Me. (lock): Accommodate switch to muto. (unlock): Ditto. * exceptions.cc (setup_handler): Don't worry about acquiring mutos since that hasn't mattered for a long time. (signal_exit): Ditto: muto stuff will be handled automatically on exit now. * Makefile.in (DLL_IMPORTS): Link advapi32 to ensure proper DLL initialization. * autoload.cc (RegCloseKey): Arbitrarily choose this function as a "seed" to pull the advapi32 link library in. So, comment out the autoloading. * cygtls.cc (_threadinfo::init_thread): Just clear CYGTLS_PADSIZE. (_threadinfo::remove): Add debugging. (_threadinfo::find_tls): Ditto. * cygtls.h (_threadinfo::padding): Make zero length (for now?). * dcrt0.cc (dll_crt0_0): Move more initialization here from dll_crt0_1. (dll_crt0_1): See above. * dtable.h (dtable::lock): Remove commented out critical section locking. * dtable.h (dtable::init_lock): Remove commented out critical section locking. * dtable.h (dtable::unlock): Remove commented out critical section locking. * exceptions.cc (interruptible): bool'ize. * init.cc (threadfunc_fe): Revert to storing threadfunc at stack bottom. (munge_threadfunc): Ditto. Avoid adding overhead to calibration_thread. (prime_threads): Don't initialize tls stuff. (dll_entry): Make minor change to initialization order. * tlsoffsets.h: Regenerate. * sigproc.cc (wait_sig): Add sanity check for end of process thread exit. * select.h: Make minor formatting change. * Makefile.in: Add still more -fomit-frame-pointer functions. * dtable.h (dtable::lock): New function. (dtable::unlock): New function. (dtable::init_lock): New function. * cygheap.h (HEAP_TLS): Declare new enum value. (init_cygheap::threadlist): Declare new array. (init_cygheap::sthreads): Declare new variable. (cygheap_fdmanip::~cygheap_fdmanip): Use new dtable lock/unlock functions. (cygheap_fdnew::cygheap_fdnew): Ditto. (cygheap_fdget::cygheap_fdget): Ditto. * dtable.cc (dtable_init): Initialize fdtab critical section. (dtable::fixup_after_fork): Ditto. (dtable::fixup_after_exec): Ditto. (dtable::dup2): Use lock/unlock calls to protect access to fdtab. (dtable::find_fifo): Ditto. (dtable::fixup_before_fork): Ditto. (dtable::fixup_before_exec): Ditto. (dtable::set_file_pointers_for_exec): Ditto. (dtable::vfork_child_dup): Ditto. (dtable::vfork_parent_restore): Ditto. * syscalls.cc (close_all_files): Ditto. * sync.h (muto::acquired): Declare new function. (new_muto1): Declare new macro used to specify name of muto storage. * sync.cc (muto::acquired): Define new function. * cygthread.cc (cygthread::stub): Remove signal chain removal call since it is handled during initialization now. * cygthread.cc (cygthread::simplestub): Remove signal chain removal call since it is handled during initialization now. * cygtls.cc (sentry): New class used for locking. Use throughout. (_threadinfo::reset_exception): Don't pop stack. (_threadinfo::find_tls): Move from exceptions.cc. (_threadinfo::init_thread): Initialize array of threads rather than linked list. Take second argument indicating thread function for this thread. (_threadinfo::remove): Search thread array rather than linked list. Use sentry to lock. Only unlock if we got the lock. (_threadinfo::find_tls): Ditto for first two. (handle_threadlist_exception): Handle exceptions when manipulating the thread list in case of premature thread termination. (_threadinfo::init_threadlist_exceptions): Ditto. * cygtls.h (TLS_STACK_SIZE): Decrease size. (_threadinfo::padding): Add element to avoid overwriting lower part of stack. (_threadinfo::remove): Add a "wait" argument to control how long we wait for a lock before removing. * exceptions.cc (init_exception_handler): Make global. Take argument to control exception handler being set. (ctrl_c_handler): Wait forever when removing self from signal chain. (_threadinfo::find_tls): Move to cygtls.cc. (sig_handle): Reorganize detection for thread-specific signals. * heap.cc (heap_init): Rework slightly. Make fatal error more verbose. Remove malloc initialization since it can't happen during dll attach. * init.cc (search_for): Move address to search for on stack here. (threadfunc_ix): Ditto for stack offset. Make shared so that stack walk potentially only has to be done once when cygwin processes are running. (threadfunc_fe): Use standard tls to store thread function (may change back later). (calibration_thread): New function. Potentially called to find threadfunc_ix. (munge_threadfunc): Search for "search_for" value on stack. Output warning when thread func not found on stack. Use standard tls to store thread function. (prime_threads): New function. Called to prime thread front end. (dll_entry): Call dll_crt0_0 here when DLL_PROCESS_ATTACH. Call prime_threads here. Try to remove thread from signal list here. * sigproc.cc (wait_sig): Initialize threadlist exception stuff here. * thread.cc (pthread::exit): Pass argument to signal list remove function. * thread.h: Remove obsolete *ResourceLock defines. * tlsoffsets.h: Regenerate. * winsup.h (spf): Define temporary debug macro to be deleted later. * dcrt0.cc (dll_crt0_0): New function, called during DLL initialization. Mainly consists of code pulled from dll_crt0_1. (dll_crt0_1): See above. (_dll_crt0): Wait for initial calibration thread to complete, if appropriate. Move some stuff to dll_crt0_0. (initialize_main_tls): Accommodate argument change to _thread_info::init_thread. * fork.cc (fork_child): Ditto. (sync_with_child): Fix debug message. * external.cc (cygwin_internal): Remove special considerations for uninitialized dll since initialization happens during dll attach now. * dlfcn.cc (dlopen): Remove obsolete *ResourceLock calls. (dlclose): Ditto. * cygheap.h (init_cygheap::close_ctty): Declare new function. * cygheap.cc (init_cygheap::close_ctty): Define new function. * syscalls.cc (close_all_files): Use close_ctty. (setsid): Ditto. * cygthread.cc (cygthread::stub): Remove exception initialization. * cygthread.cc (cygthread::stub): Remove exception initialization. (cygthread::simplestub): Ditto. * thread.cc (pthread::thread_init_wrapper): Ditto. * cygtls.cc (_last_thread): Make static. (_threadinfo::call2): Initialize exception handler here. (_threadinfo::find_tls): Move here. * exceptions.cc (_threadinfo::find_tls): Move. * dcrt0.cc (__api_fatal): Add prefix info to message here rather than including it in every call to function. * winsup.h (api_fatal): Accommodate above change. * debug.cc (add_handle): Don't do anything if cygheap not around. (mark_closed): Ditto. * dll_init.cc (dll_list::detach): Fix debug output. * fork.cc (sync_with_child): Ditto. (vfork): Improve debug output. * heap.cc (heap_init): Ditto. * exceptions.cc (try_to_debug): Clarify message when debugger attaches.
326 lines
7.0 KiB
C++
326 lines
7.0 KiB
C++
/* cygthread.cc
|
|
|
|
Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#include "winsup.h"
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include "exceptions.h"
|
|
#include "security.h"
|
|
#include "cygthread.h"
|
|
#include "sync.h"
|
|
#include "cygerrno.h"
|
|
#include "sigproc.h"
|
|
#include "thread.h"
|
|
#include "cygtls.h"
|
|
|
|
#undef CloseHandle
|
|
|
|
static cygthread NO_COPY threads[18];
|
|
#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
|
|
|
|
DWORD NO_COPY cygthread::main_thread_id;
|
|
bool NO_COPY cygthread::exiting;
|
|
|
|
/* Initial stub called by cygthread constructor. Performs initial
|
|
per-thread initialization and loops waiting for new thread functions
|
|
to execute. */
|
|
DWORD WINAPI
|
|
cygthread::stub (VOID *arg)
|
|
{
|
|
cygthread *info = (cygthread *) arg;
|
|
if (info->arg == cygself)
|
|
{
|
|
if (info->ev)
|
|
{
|
|
CloseHandle (info->ev);
|
|
CloseHandle (info->thread_sync);
|
|
}
|
|
info->ev = info->thread_sync = info->stack_ptr = NULL;
|
|
}
|
|
else
|
|
{
|
|
info->stack_ptr = &arg;
|
|
if (!info->ev)
|
|
{
|
|
info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
|
|
info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (!info->__name)
|
|
system_printf ("erroneous thread activation");
|
|
else
|
|
{
|
|
if (!info->func || exiting)
|
|
return 0;
|
|
|
|
/* Cygwin threads should not call ExitThread directly */
|
|
info->func (info->arg == cygself ? info : info->arg);
|
|
/* ...so the above should always return */
|
|
|
|
#ifdef DEBUGGING
|
|
info->func = NULL; // catch erroneous activation
|
|
#endif
|
|
info->__name = NULL;
|
|
SetEvent (info->ev);
|
|
}
|
|
switch (WaitForSingleObject (info->thread_sync, INFINITE))
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
continue;
|
|
default:
|
|
api_fatal ("WFSO failed, %E");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Overflow stub called by cygthread constructor. Calls specified function
|
|
and then exits the thread. */
|
|
DWORD WINAPI
|
|
cygthread::simplestub (VOID *arg)
|
|
{
|
|
cygthread *info = (cygthread *) arg;
|
|
info->stack_ptr = &arg;
|
|
info->ev = info->h;
|
|
info->func (info->arg == cygself ? info : info->arg);
|
|
return 0;
|
|
}
|
|
|
|
/* Start things going. Called from dll_crt0_1. */
|
|
void
|
|
cygthread::init ()
|
|
{
|
|
main_thread_id = GetCurrentThreadId ();
|
|
}
|
|
|
|
bool
|
|
cygthread::is ()
|
|
{
|
|
DWORD tid = GetCurrentThreadId ();
|
|
|
|
for (DWORD i = 0; i < NTHREADS; i++)
|
|
if (threads[i].id == tid)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
cygthread *
|
|
cygthread::freerange ()
|
|
{
|
|
cygthread *self = (cygthread *) calloc (1, sizeof (*self));
|
|
self->is_freerange = true;
|
|
self->inuse = 1;
|
|
return self;
|
|
}
|
|
|
|
void * cygthread::operator
|
|
new (size_t)
|
|
{
|
|
cygthread *info;
|
|
|
|
/* Search the threads array for an empty slot to use */
|
|
for (info = threads; info < threads + NTHREADS; info++)
|
|
if (!InterlockedExchange (&info->inuse, 1))
|
|
{
|
|
/* available */
|
|
#ifdef DEBUGGING
|
|
if (info->__name)
|
|
api_fatal ("name not NULL? id %p, i %d", info->id, info - threads);
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
#ifdef DEBUGGING
|
|
char buf[1024];
|
|
if (!GetEnvironmentVariable ("CYGWIN_FREERANGE_NOCHECK", buf, sizeof (buf)))
|
|
api_fatal ("Overflowed cygwin thread pool");
|
|
else
|
|
thread_printf ("Overflowed cygwin thread pool");
|
|
#endif
|
|
|
|
info = freerange (); /* exhausted thread pool */
|
|
|
|
out:
|
|
return info;
|
|
}
|
|
|
|
cygthread::cygthread (LPTHREAD_START_ROUTINE start, LPVOID param,
|
|
const char *name): __name (name),
|
|
func (start), arg (param)
|
|
{
|
|
thread_printf ("name %s, id %p", name, id);
|
|
if (h)
|
|
{
|
|
while (!thread_sync)
|
|
low_priority_sleep (0);
|
|
SetEvent (thread_sync);
|
|
thread_printf ("activated thread_sync %p", thread_sync);
|
|
}
|
|
else
|
|
{
|
|
stack_ptr = NULL;
|
|
h = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
|
|
this, 0, &id);
|
|
if (!h)
|
|
api_fatal ("thread handle not set - %p<%p>, %E", h, id);
|
|
thread_printf ("created thread %p", h);
|
|
}
|
|
}
|
|
|
|
/* Return the symbolic name of the current thread for debugging.
|
|
*/
|
|
const char *
|
|
cygthread::name (DWORD tid)
|
|
{
|
|
const char *res = NULL;
|
|
if (!tid)
|
|
tid = GetCurrentThreadId ();
|
|
|
|
if (tid == main_thread_id)
|
|
return "main";
|
|
|
|
for (DWORD i = 0; i < NTHREADS; i++)
|
|
if (threads[i].id == tid)
|
|
{
|
|
res = threads[i].__name ?: "exiting thread";
|
|
break;
|
|
}
|
|
|
|
if (!res)
|
|
{
|
|
static char buf[30] NO_COPY = {0};
|
|
__small_sprintf (buf, "unknown (%p)", tid);
|
|
res = buf;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
cygthread::operator
|
|
HANDLE ()
|
|
{
|
|
while (!ev)
|
|
low_priority_sleep (0);
|
|
return ev;
|
|
}
|
|
|
|
/* Should only be called when the process is exiting since it
|
|
leaves an open thread slot. */
|
|
void
|
|
cygthread::exit_thread ()
|
|
{
|
|
if (!is_freerange)
|
|
SetEvent (*this);
|
|
ExitThread (0);
|
|
}
|
|
|
|
/* Forcibly terminate a thread. */
|
|
void
|
|
cygthread::terminate_thread ()
|
|
{
|
|
if (!is_freerange)
|
|
{
|
|
ResetEvent (*this);
|
|
ResetEvent (thread_sync);
|
|
}
|
|
(void) TerminateThread (h, 0);
|
|
(void) WaitForSingleObject (h, INFINITE);
|
|
CloseHandle (h);
|
|
|
|
while (!stack_ptr)
|
|
low_priority_sleep (0);
|
|
|
|
MEMORY_BASIC_INFORMATION m;
|
|
memset (&m, 0, sizeof (m));
|
|
(void) VirtualQuery (stack_ptr, &m, sizeof m);
|
|
|
|
if (!m.RegionSize)
|
|
system_printf ("m.RegionSize 0? stack_ptr %p", stack_ptr);
|
|
else if (!VirtualFree (m.AllocationBase, 0, MEM_RELEASE))
|
|
debug_printf ("VirtualFree of allocation base %p<%p> failed, %E",
|
|
stack_ptr, m.AllocationBase);
|
|
|
|
if (is_freerange)
|
|
free (this);
|
|
else
|
|
{
|
|
h = NULL;
|
|
__name = NULL;
|
|
stack_ptr = NULL;
|
|
(void) InterlockedExchange (&inuse, 0); /* No longer in use */
|
|
}
|
|
}
|
|
|
|
/* Detach the cygthread from the current thread. Note that the
|
|
theory is that cygthreads are only associated with one thread.
|
|
So, there should be never be multiple threads doing waits
|
|
on the same cygthread. */
|
|
bool
|
|
cygthread::detach (HANDLE sigwait)
|
|
{
|
|
bool signalled = false;
|
|
if (!inuse)
|
|
system_printf ("called detach but inuse %d, thread %p?", inuse, id);
|
|
else
|
|
{
|
|
DWORD res;
|
|
|
|
if (!sigwait)
|
|
res = WaitForSingleObject (*this, INFINITE);
|
|
else
|
|
{
|
|
HANDLE w4[2];
|
|
w4[0] = *this;
|
|
w4[1] = signal_arrived;
|
|
res = WaitForSingleObject (sigwait, INFINITE);
|
|
if (res != WAIT_OBJECT_0)
|
|
system_printf ("WFSO sigwait %p failed, res %u, %E", sigwait, res);
|
|
res = WaitForMultipleObjects (2, w4, FALSE, INFINITE);
|
|
if (res == WAIT_OBJECT_0)
|
|
/* nothing */;
|
|
else if (WaitForSingleObject (sigwait, 0) == WAIT_OBJECT_0)
|
|
res = WaitForSingleObject (*this, INFINITE);
|
|
else if ((res = WaitForSingleObject (*this, 0)) != WAIT_OBJECT_0)
|
|
{
|
|
signalled = true;
|
|
terminate_thread ();
|
|
set_sig_errno (EINTR); /* caller should be dealing with return
|
|
values. */
|
|
}
|
|
}
|
|
|
|
thread_printf ("%s returns %d, id %p", sigwait ? "WFMO" : "WFSO",
|
|
res, id);
|
|
|
|
if (signalled)
|
|
/* already handled */;
|
|
else if (is_freerange)
|
|
{
|
|
CloseHandle (h);
|
|
free (this);
|
|
}
|
|
else
|
|
{
|
|
ResetEvent (*this);
|
|
/* Mark the thread as available by setting inuse to zero */
|
|
(void) InterlockedExchange (&inuse, 0);
|
|
}
|
|
}
|
|
return signalled;
|
|
}
|
|
|
|
void
|
|
cygthread::terminate ()
|
|
{
|
|
exiting = 1;
|
|
}
|