diff options
author | jsturm <jsturm@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-07-28 04:18:23 +0000 |
---|---|---|
committer | jsturm <jsturm@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-07-28 04:18:23 +0000 |
commit | 341281013a7334da0896c950d9c254e42692c479 (patch) | |
tree | 827bf34dcb0c2786d314e5fa136528e5a4feb6f7 /boehm-gc/win32_threads.c | |
parent | 975f1a7b2a8b2194905616d3fb05653329a2afce (diff) | |
download | gcc-341281013a7334da0896c950d9c254e42692c479.tar.gz |
Import GC 6.3alpha1.
* BCC_MAKEFILE: Merge with GC 6.3alpha1 release.
* ChangeLog: Likewise.
* Makefile.am: Likewise.
* Makefile.direct: Likewise.
* Makefile.dj: Likewise.
* allchblk.c: Likewise.
* alloc.c: Likewise.
* backgraph.c: Likewise.
* configure.host: Likewise.
* configure.in: Likewise.
* dbg_mlc.c: Likewise.
* dyn_load.c: Likewise.
* finalize.c: Likewise.
* gc_cpp.cc: Likewise.
* gc_dlopen.c: Likewise.
* gcj_mlc.c: Likewise.
* if_mach.c: Likewise.
* mach_dep.c: Likewise.
* malloc.c: Likewise.
* mallocx.c: Likewise.
* mark.c: Likewise.
* mark_rts.c: Likewise.
* misc.c: Likewise.
* os_dep.c: Likewise.
* ptr_chck.c: Likewise.
* reclaim.c: Likewise.
* solaris_pthreads.c: Likewise.
* solaris_threads.c: Likewise.
* sparc_mach_dep.S: Likewise.
* threadlibs.c: Likewise.
* typd_mlc.c: Likewise.
* version.h: Likewise.
* win32_threads.c: Likewise.
* Mac_files/MacOS_Test_config.h: Likewise.
* Mac_files/MacOS_config.h: Likewise.
* cord/cordbscs.c: Likewise.
* cord/cordprnt.c: Likewise.
* cord/de_win.c: Likewise.
* doc/README: Likewise.
* doc/README.MacOSX: Likewise.
* doc/README.changes: Likewise.
* doc/README.environment: Likewise.
* doc/README.ews4800: Likewise.
* doc/README.linux: Likewise.
* doc/README.macros: Likewise.
* doc/README.win32: Likewise.
* doc/debugging.html: Likewise.
* doc/gcdescr.html: Likewise.
* doc/tree.html: Likewise.
* include/Makefile.in: Likewise.
* include/gc.h: Likewise.
* include/gc_cpp.h: Likewise.
* include/gc_local_alloc.h: Likewise.
* include/gc_mark.h: Likewise.
* include/gc_pthread_redirects.h: Likewise.
* include/gc_typed.h: Likewise.
* include/new_gc_alloc.h: Likewise.
* include/private/dbg_mlc.h: Likewise.
* include/private/gc_hdrs.h: Likewise.
* include/private/gc_locks.h: Likewise.
* include/private/gc_pmark.h: Likewise.
* include/private/gc_priv.h: Likewise.
* include/private/gcconfig.h: Likewise.
* include/private/solaris_threads.h: Likewise.
* include/private/specific.h: Likewise.
* tests/test.c: Likewise.
* tests/test_cpp.cc: Likewise.
* configure: Rebuild.
* Makefile.in: Rebuild.
* mips_sgi_mach_dep.s: Add.
* alpha_mach_dep.s: Remove.
* irix_threads.c: Remove.
* linux_threads.c: Remove.
* mips_sgi_mach_dep.S: Remove.
* missing: Remove.
* powerpc_macosx_mach_dep.s: Remove.
* doc/Makefile.am: Remove.
* doc/Makefile.in: Remove.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@69880 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'boehm-gc/win32_threads.c')
-rw-r--r-- | boehm-gc/win32_threads.c | 400 |
1 files changed, 317 insertions, 83 deletions
diff --git a/boehm-gc/win32_threads.c b/boehm-gc/win32_threads.c index 954b18d7018..ff1d06625f8 100644 --- a/boehm-gc/win32_threads.c +++ b/boehm-gc/win32_threads.c @@ -1,13 +1,28 @@ -#if defined(GC_WIN32_THREADS) +#if defined(GC_WIN32_THREADS) #include "private/gc_priv.h" - -#if 0 -#define STRICT #include <windows.h> + +#ifdef CYGWIN32 +# include <errno.h> + + /* Cygwin-specific forward decls */ +# undef pthread_create +# undef pthread_sigmask +# undef pthread_join +# undef dlopen + +# define DEBUG_CYGWIN_THREADS 0 + + GC_bool GC_thr_initialized = FALSE; + void * GC_start_routine(void * arg); + void GC_thread_exit_proc(void *arg); + #endif -#define MAX_THREADS 64 +#ifndef MAX_THREADS +# define MAX_THREADS 64 +#endif struct thread_entry { LONG in_use; @@ -18,6 +33,12 @@ struct thread_entry { /* !in_use ==> stack == 0 */ CONTEXT context; GC_bool suspended; + +# ifdef CYGWIN32 + void *status; /* hold exit value until join in case it's a pointer */ + pthread_t pthread_id; +# endif + }; volatile GC_bool GC_please_stop = FALSE; @@ -29,6 +50,12 @@ void GC_push_thread_structures GC_PROTO((void)) /* Unlike the other threads implementations, the thread table here */ /* contains no pointers to the collectable heap. Thus we have */ /* no private structures we need to preserve. */ +# ifdef CYGWIN32 + { int i; /* pthreads may keep a pointer in the thread exit value */ + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1)); + } +# endif } void GC_stop_world() @@ -36,6 +63,10 @@ void GC_stop_world() DWORD thread_id = GetCurrentThreadId(); int i; +#ifdef CYGWIN32 + if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); +#endif + GC_please_stop = TRUE; for (i = 0; i < MAX_THREADS; i++) if (thread_table[i].stack != 0 @@ -53,11 +84,15 @@ void GC_stop_world() DWORD exitCode; if (GetExitCodeThread(thread_table[i].handle,&exitCode) && exitCode != STILL_ACTIVE) { - thread_table[i].stack = 0; + thread_table[i].stack = 0; /* prevent stack from being pushed */ +# ifndef CYGWIN32 + /* this breaks pthread_join on Cygwin, which is guaranteed to */ + /* only see user pthreads */ thread_table[i].in_use = FALSE; CloseHandle(thread_table[i].handle); BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT)); - continue; +# endif + continue; } if (SuspendThread(thread_table[i].handle) == (DWORD)-1) ABORT("SuspendThread failed"); @@ -335,9 +370,13 @@ void GC_get_next_stack(char *start, char **lo, char **hi) if (*lo < start) *lo = start; } -#if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) +#if !defined(CYGWIN32) -HANDLE WINAPI GC_CreateThread( +#if !defined(MSWINCE) && defined(GC_DLL) + +/* We register threads from DllMain */ + +GC_API HANDLE GC_CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) @@ -346,7 +385,10 @@ HANDLE WINAPI GC_CreateThread( lpParameter, dwCreationFlags, lpThreadId); } -#else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#else /* defined(MSWINCE) || !defined(GC_DLL)) */ + +/* We have no DllMain to take care of new threads. Thus we */ +/* must properly intercept thread creation. */ typedef struct { HANDLE child_ready_h, parent_ready_h; @@ -400,7 +442,17 @@ HANDLE WINAPI GC_CreateThread( /* fill in ID and handle; tell child this is done */ thread_table[i].id = *lpThreadId; - thread_table[i].handle = thread_h; + if (!DuplicateHandle(GetCurrentProcess(), + thread_h, + GetCurrentProcess(), + &thread_table[i].handle, + 0, + 0, + DUPLICATE_SAME_ACCESS)) { + DWORD last_error = GetLastError(); + GC_printf1("Last error code: %lx\n", last_error); + ABORT("DuplicateHandle failed"); + } SetEvent (parent_ready_h); /* wait for child to fill in stack and copy args */ @@ -470,7 +522,9 @@ static DWORD WINAPI thread_start(LPVOID arg) return ret; } -#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ + +#endif /* !CYGWIN32 */ #ifdef MSWINCE @@ -527,13 +581,255 @@ DWORD WINAPI main_thread_start(LPVOID arg) LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); -#ifdef GC_DLL +/* threadAttach/threadDetach routines used by both CYGWIN and DLL + * implementation, since both recieve explicit notification on thread + * creation/destruction. + */ +static void threadAttach() { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preeemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* inititalization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + + /* The following should be a noop according to the win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# if defined(MPROTECT_VDB) + if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); +# endif + /* cast away volatile qualifier */ + for (i = 0; InterlockedExchange((LONG*)&thread_table[i].in_use,1) != 0; i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + if (i == MAX_THREADS - 1) + ABORT("too many threads"); + } + thread_table[i].id = GetCurrentThreadId(); +# ifdef CYGWIN32 + thread_table[i].pthread_id = pthread_self(); +# endif + if (!DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + (HANDLE*)&thread_table[i].handle, + 0, + 0, + DUPLICATE_SAME_ACCESS)) { + DWORD last_error = GetLastError(); + GC_printf1("Last error code: %lx\n", last_error); + ABORT("DuplicateHandle failed"); + } + thread_table[i].stack = GC_get_stack_base(); + if (thread_table[i].stack == NULL) + ABORT("Failed to find stack base in threadAttach"); + /* If this thread is being created while we are trying to stop */ + /* the world, wait here. Hopefully this can't happen on any */ + /* systems that don't allow us to block here. */ + while (GC_please_stop) Sleep(20); +} + +static void threadDetach(DWORD thread_id) { + int i; + + LOCK(); + for (i = 0; + i < MAX_THREADS && + (!thread_table[i].in_use || thread_table[i].id != thread_id); + i++) {} + if (i >= MAX_THREADS ) { + WARN("thread %ld not found on detach", (GC_word)thread_id); + } else { + thread_table[i].stack = 0; + thread_table[i].in_use = FALSE; + CloseHandle(thread_table[i].handle); + /* cast away volatile qualifier */ + BZERO((void *)&thread_table[i].context, sizeof(CONTEXT)); + } + UNLOCK(); +} + +#ifdef CYGWIN32 + +/* Called by GC_init() - we hold the allocation lock. */ +void GC_thr_init() { + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + +#if 0 + /* this might already be handled in GC_init... */ + InitializeCriticalSection(&GC_allocate_ml); +#endif + + /* Add the initial thread, so we can stop it. */ + threadAttach(); +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; +}; + +int GC_pthread_join(pthread_t pthread_id, void **retval) { + int result; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(), + GetCurrentThreadId(), (int)pthread_id); +# endif + + /* Can't do any table lookups here, because thread being joined + might not have registered itself yet */ + + result = pthread_join(pthread_id, retval); + + LOCK(); + for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id; + i++) { + if (i == MAX_THREADS - 1) { + GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id); + ABORT("thread not found on detach"); + } + } + UNLOCK(); + threadDetach(thread_table[i].id); + +# if DEBUG_CYGWIN_THREADS + GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n", + (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id); +# endif + + return result; +} + +/* Cygwin-pthreads calls CreateThread internally, but it's not + * easily interceptible by us.. + * so intercept pthread_create instead + */ +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) { + int result; + struct start_info * si; + + if (!GC_is_initialized) GC_init(); + /* make sure GC is initialized (i.e. main thread is attached) */ + + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + si = GC_malloc_uncollectable(sizeof(struct start_info)); + if (0 == si) return(EAGAIN); + + si -> start_routine = start_routine; + si -> arg = arg; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(), + GetCurrentThreadId); +# endif + result = pthread_create(new_thread, attr, GC_start_routine, si); + + if (result) { /* failure */ + GC_free(si); + } + + return(result); +} + +void * GC_start_routine(void * arg) +{ + struct start_info * si = arg; + void * result; + void *(*start)(void *); + void *start_arg; + pthread_t pthread_id; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(), + GetCurrentThreadId()); +# endif + + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + threadAttach(); + UNLOCK(); + + start = si -> start_routine; + start_arg = si -> arg; + pthread_id = pthread_self(); + + GC_free(si); /* was allocated uncollectable */ + + pthread_cleanup_push(GC_thread_exit_proc, pthread_id); + result = (*start)(start_arg); + pthread_cleanup_pop(0); + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) returned from start routine.\n", + (int)pthread_self(),GetCurrentThreadId()); +# endif + + LOCK(); + for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { + if (i == MAX_THREADS - 1) + ABORT("thread not found on exit"); + } + thread_table[i].status = result; + UNLOCK(); + + return(result); +} + +void GC_thread_exit_proc(void *arg) +{ + pthread_t pthread_id = (pthread_t)arg; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n", + (int)pthread_self(),GetCurrentThreadId()); +# endif + + LOCK(); + for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { + if (i == MAX_THREADS - 1) + ABORT("thread not found on exit"); + } + UNLOCK(); + +#if 0 + /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */ + thread_table[i].status = ??? +#endif +} + +/* nothing required here... */ +int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { + return pthread_sigmask(how, set, oset); +} +int GC_pthread_detach(pthread_t thread) { + return pthread_detach(thread); +} +#else /* !CYGWIN32 */ /* - * This isn't generally safe, since DllMain is not premptible. - * If another thread holds the lock while this runs we're in trouble. + * We avoid acquiring locks here, since this doesn't seem to be preemptable. * Pontus Rydin suggests wrapping the thread start routine instead. */ +#ifdef GC_DLL BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) { switch (reason) { @@ -542,75 +838,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) GC_init(); /* Force initialization before thread attach. */ /* fall through */ case DLL_THREAD_ATTACH: - { - int i; - /* It appears to be unsafe to acquire a lock here, since this */ - /* code is apparently not preeemptible on some systems. */ - /* (This is based on complaints, not on Microsoft's official */ - /* documentation, which says this should perform "only simple */ - /* inititalization tasks".) */ - /* Hence we make do with nonblocking synchronization. */ - - /* The following should be a noop according to the win32 */ - /* documentation. There is empirical evidence that it */ - /* isn't. - HB */ -# ifdef MPROTECT_VDB - if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); -# endif - - for (i = 0; - /* cast away volatile qualifier */ - InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0; - i++) { - /* Compare-and-swap would make this cleaner, but that's not */ - /* supported before Windows 98 and NT 4.0. In Windows 2000, */ - /* InterlockedExchange is supposed to be replaced by */ - /* InterlockedExchangePointer, but that's not really what I */ - /* want here. */ - if (i == MAX_THREADS - 1) - ABORT("too many threads"); - } - thread_table[i].id = GetCurrentThreadId(); - if (!DuplicateHandle(GetCurrentProcess(), - GetCurrentThread(), - GetCurrentProcess(), - /* cast away volatile qualifier */ - (HANDLE *) &thread_table[i].handle, - 0, - 0, - DUPLICATE_SAME_ACCESS)) { - DWORD last_error = GetLastError(); - GC_printf1("Last error code: %lx\n", last_error); - ABORT("DuplicateHandle failed"); - } - thread_table[i].stack = GC_get_stack_base(); - /* If this thread is being created while we are trying to stop */ - /* the world, wait here. Hopefully this can't happen on any */ - /* systems that don't allow us to block here. */ - while (GC_please_stop) Sleep(20); - } + threadAttach(); break; + case DLL_THREAD_DETACH: - { - int i; - DWORD thread_id = GetCurrentThreadId(); - LOCK(); - for (i = 0; - i < MAX_THREADS && - (thread_table[i].stack == 0 || thread_table[i].id != thread_id); - i++) {} - if (i >= MAX_THREADS) { - WARN("thread %ld not found on detach", (GC_word)thread_id); - } else { - thread_table[i].stack = 0; - thread_table[i].in_use = FALSE; - CloseHandle(thread_table[i].handle); - /* cast away volatile qualifier */ - BZERO((void *) &thread_table[i].context, sizeof(CONTEXT)); - } - UNLOCK(); - } + threadDetach(GetCurrentThreadId()); break; + case DLL_PROCESS_DETACH: { int i; @@ -636,8 +870,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) } return TRUE; } - -# endif /* GC_DLL */ +#endif /* GC_DLL */ +#endif /* !CYGWIN32 */ # endif /* !MSWINCE */ |