+#if !defined(CYGWIN32)
+
+#if !defined(MSWINCE) && defined(GC_DLL)
+
+/* We register threads from DllMain */
+
+GC_API 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(GC_DLL)) */
+
+/* We have no DllMain to take care of new threads. Thus we */
+/* must properly intercept thread creation. */
+
+typedef struct {
+ LPTHREAD_START_ROUTINE start;
+ LPVOID param;
+} thread_args;
+
+static DWORD WINAPI thread_start(LPVOID arg);
+
+GC_API HANDLE WINAPI GC_CreateThread(
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
+{
+ HANDLE thread_h = NULL;
+
+ thread_args *args;
+
+ if (!GC_is_initialized) GC_init();
+ /* make sure GC is initialized (i.e. main thread is attached) */
+
+ args = GC_malloc_uncollectable(sizeof(thread_args));
+ /* Handed off to and deallocated by child thread. */
+ if (0 == args) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+
+ /* set up thread arguments */
+ args -> start = lpStartAddress;
+ args -> param = lpParameter;
+
+ thread_h = CreateThread(lpThreadAttributes,
+ dwStackSize, thread_start,
+ args, dwCreationFlags,
+ lpThreadId);
+
+ return thread_h;
+}
+
+static DWORD WINAPI thread_start(LPVOID arg)
+{
+ DWORD ret = 0;
+ thread_args *args = (thread_args *)arg;
+
+ GC_new_thread();
+
+ /* 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__ */
+ GC_free(args);
+ GC_delete_thread(GetCurrentThreadId());
+#ifndef __GNUC__
+ }
+#endif /* __GNUC__ */
+
+ return ret;
+}
+#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
+
+#endif /* !CYGWIN32 */
+
+#ifdef MSWINCE
+
+typedef struct {
+ HINSTANCE hInstance;
+ HINSTANCE hPrevInstance;
+ LPWSTR lpCmdLine;
+ int nShowCmd;
+} main_thread_args;
+
+DWORD WINAPI main_thread_start(LPVOID arg);
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPWSTR lpCmdLine, int nShowCmd)
+{
+ DWORD exit_code = 1;
+
+ main_thread_args args = {
+ hInstance, hPrevInstance, lpCmdLine, nShowCmd
+ };
+ HANDLE thread_h;
+ DWORD thread_id;
+
+ /* initialize everything */
+ GC_init();
+
+ /* start the main thread */
+ thread_h = GC_CreateThread(
+ NULL, 0, main_thread_start, &args, 0, &thread_id);
+
+ if (thread_h != NULL)
+ {
+ WaitForSingleObject (thread_h, INFINITE);
+ GetExitCodeThread (thread_h, &exit_code);
+ CloseHandle (thread_h);
+ }
+
+ GC_deinit();
+ DeleteCriticalSection(&GC_allocate_ml);
+
+ return (int) exit_code;
+}
+
+DWORD WINAPI main_thread_start(LPVOID arg)
+{
+ main_thread_args * args = (main_thread_args *) arg;
+
+ return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
+ args->lpCmdLine, args->nShowCmd);
+}
+
+# else /* !MSWINCE */
+
+/* Called by GC_init() - we hold the allocation lock. */
+void GC_thr_init() {
+ if (GC_thr_initialized) return;
+ GC_main_thread = GetCurrentThreadId();
+ GC_thr_initialized = TRUE;
+
+ /* Add the initial thread, so we can stop it. */
+ GC_new_thread();
+}
+
+#ifdef CYGWIN32
+
+struct start_info {
+ void *(*start_routine)(void *);
+ void *arg;
+ GC_bool detached;
+};
+
+int GC_pthread_join(pthread_t pthread_id, void **retval) {
+ int result;
+ int i;
+ GC_thread me;
+
+# 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
+
+ /* Thread being joined might not have registered itself yet. */
+ /* After the join,thread id may have been recycled. */
+ /* FIXME: It would be better if this worked more like */
+ /* pthread_support.c. */
+
+ while ((me = GC_lookup_thread(pthread_id)) == 0) Sleep(10);
+
+ result = pthread_join(pthread_id, retval);
+
+ GC_delete_gc_thread(me);
+
+# 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 (attr != 0 &&
+ pthread_attr_getdetachstate(attr, &si->detached)
+ == PTHREAD_CREATE_DETACHED) {
+ si->detached = TRUE;
+ }
+
+# 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;
+ GC_thread me;
+ GC_bool detached;
+ 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. */
+ me = GC_new_thread();
+ UNLOCK();
+
+ start = si -> start_routine;
+ start_arg = si -> arg;
+ if (si-> detached) me -> flags |= DETACHED;
+ me -> pthread_id = pthread_id = pthread_self();
+
+ GC_free(si); /* was allocated uncollectable */
+
+ pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
+ result = (*start)(start_arg);
+ me -> status = result;
+ 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
+
+ return(result);
+}
+
+void GC_thread_exit_proc(void *arg)
+{
+ GC_thread me = (GC_thread)arg;
+ int i;
+
+# if DEBUG_CYGWIN_THREADS
+ GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
+ (int)pthread_self(),GetCurrentThreadId());
+# endif
+
+ LOCK();
+ if (me -> flags & DETACHED) {
+ GC_delete_thread(GetCurrentThreadId());
+ } else {
+ /* deallocate it as part of join */
+ me -> flags |= FINISHED;
+ }
+ UNLOCK();
+}
+
+/* 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)
+{
+ int result;
+ GC_thread thread_gc_id;
+
+ LOCK();
+ thread_gc_id = GC_lookup_thread(thread);
+ UNLOCK();
+ result = pthread_detach(thread);
+ if (result == 0) {
+ LOCK();
+ thread_gc_id -> flags |= DETACHED;
+ /* Here the pthread thread id may have been recycled. */
+ if (thread_gc_id -> flags & FINISHED) {
+ GC_delete_gc_thread(thread_gc_id);
+ }
+ UNLOCK();
+ }
+ return result;
+}
+
+#else /* !CYGWIN32 */