=================================================================== RCS file: /home/cvs/OpenXM_contrib2/asir2000/gc/win32_threads.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -p -r1.4 -r1.5 --- OpenXM_contrib2/asir2000/gc/win32_threads.c 2002/07/24 07:46:25 1.4 +++ OpenXM_contrib2/asir2000/gc/win32_threads.c 2002/07/24 08:00:12 1.5 @@ -1,7 +1,25 @@ -#if defined(GC_WIN32_THREADS) || defined(WIN32_THREADS) +#if defined(GC_WIN32_THREADS) #include "private/gc_priv.h" +#ifdef CYGWIN32 +# include + + /* 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 + + #if 0 #define STRICT #include @@ -18,6 +36,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 +53,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 +66,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,10 +87,13 @@ 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)); +#endif continue; } if (SuspendThread(thread_table[i].handle) == (DWORD)-1) @@ -134,9 +171,6 @@ void GC_push_all_stacks() (LPCONTEXT)&thread_table[i].context)) ABORT("GetThreadContext failed"); # ifdef I386 - if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack - || thread_table[i].context.Esp < (DWORD)bottom) - ABORT("Thread stack pointer out of range"); GC_push_one ((word) thread_table[i].context.Edi); GC_push_one ((word) thread_table[i].context.Esi); GC_push_one ((word) thread_table[i].context.Ebp); @@ -144,8 +178,15 @@ void GC_push_all_stacks() GC_push_one ((word) thread_table[i].context.Edx); GC_push_one ((word) thread_table[i].context.Ecx); GC_push_one ((word) thread_table[i].context.Eax); - GC_push_all_stack((char *) thread_table[i].context.Esp, - thread_table[i].stack); + if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack + || thread_table[i].context.Esp < (DWORD)bottom) { + WARN("Thread stack pointer 0x%lx out of range, pushing everything", + thread_table[i].context.Esp); + GC_push_all_stack((char *) bottom, thread_table[i].stack); + } else { + GC_push_all_stack((char *) thread_table[i].context.Esp, + thread_table[i].stack); + } # else # ifdef ARM32 if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack @@ -331,9 +372,19 @@ void GC_get_next_stack(char *start, char **lo, char ** if (*lo < start) *lo = start; } +#if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) -# ifdef MSWINCE +HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) +{ + return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, + lpParameter, dwCreationFlags, lpThreadId); +} +#else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ + typedef struct { HANDLE child_ready_h, parent_ready_h; volatile struct thread_entry * entry; @@ -437,20 +488,29 @@ static DWORD WINAPI thread_start(LPVOID arg) /* Clear the thread entry even if we exit with an exception. */ /* This is probably pointless, since an uncaught exception is */ /* supposed to result in the process being killed. */ +#ifndef __GNUC__ __try { +#endif /* __GNUC__ */ ret = args.start (args.param); +#ifndef __GNUC__ } __finally { +#endif /* __GNUC__ */ LOCK(); args.entry->stack = 0; args.entry->in_use = FALSE; /* cast away volatile qualifier */ BZERO((void *) &args.entry->context, sizeof(CONTEXT)); UNLOCK(); +#ifndef __GNUC__ } +#endif /* __GNUC__ */ return ret; } +#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#ifdef MSWINCE + typedef struct { HINSTANCE hInstance; HINSTANCE hPrevInstance; @@ -504,6 +564,249 @@ DWORD WINAPI main_thread_start(LPVOID arg) LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); +/* threadAttach/threadDetach routines used by both CYGWIN and DLL implementation, + since both recieve explicit notification on thread creation/destruction + */ +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); +} + +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 + /* * This isn't generally safe, since DllMain is not premptible. * If another thread holds the lock while this runs we're in trouble. @@ -517,75 +820,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO 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; @@ -611,7 +852,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVO } return TRUE; } +#endif /* CYGWIN32 */ # endif /* !MSWINCE */ -#endif /* WIN32_THREADS */ +#endif /* GC_WIN32_THREADS */