summaryrefslogtreecommitdiff
path: root/boehm-gc/win32_threads.c
diff options
context:
space:
mode:
authorjsturm <jsturm@138bc75d-0d04-0410-961f-82ee72b054a4>2003-07-28 04:18:23 +0000
committerjsturm <jsturm@138bc75d-0d04-0410-961f-82ee72b054a4>2003-07-28 04:18:23 +0000
commit341281013a7334da0896c950d9c254e42692c479 (patch)
tree827bf34dcb0c2786d314e5fa136528e5a4feb6f7 /boehm-gc/win32_threads.c
parent975f1a7b2a8b2194905616d3fb05653329a2afce (diff)
downloadgcc-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.c400
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 */