Using GC_CreateThread with CYGWIN builds
"Thong (Tum) Nguyen" <tum-x2aT3/[email protected]>
| Newsgroups | gmane.comp.gnu.dotgnu.developer,gmane.comp.programming.garbage-collection.boehmgc |
|---|---|
| Message-ID | <[email protected]> |
Hi, I'm one of the DOTGNU/pnet developers and I've been working on threading/gc support. We use CreateThread when building on Windows regardless of whether we're using a CYGWIN or MINGW32 build. Currently, libgc doesn't wrap CreateThread when using a CYGWIN. I tried enabling the standard windows GC_CreateThread wrappers for CYGWIN but that appeared to be very unstable. The solution I eventually used (which is stable) is to write a GC_CreateThread implementation for CYGWIN builds that calls CYGWIN's pthread_create (mapping windows CreateThread semantics to pthread_create semantics). CYGWIN's pthread_create will of course eventually call the real windows CreateThread API. I think a possible reason why simply using the GC_CreateThread wrapper for normal builds crashes when using CYGWIN is because the CYGWIN runtime libraries expect threads to be created using pthread_create. In additional to the GC_CreateThread implementation for CYGWIN builds, I've had to change some GC_malloc_uncollectable/GC_free calls to malloc/free because calling the GC allocator while creating a new thread appears be a source of deadlocks between the GC and CRT. The patch is for v6.3alpha6 and is attached. All the best, ^Tum
tum_cygwin_creatthread.patch
(application/octet-stream, 6.9 KB)
--- /home/Tum/gc6.3alpha6/win32_threads.c 2004-01-27 13:14:00.000000000 +1300
+++ win32_threads.c 2004-05-27 09:22:10.062500000 +1200
@@ -25,7 +25,7 @@
typedef LONG * IE_t;
#ifndef MAX_THREADS
-# define MAX_THREADS 256
+# define MAX_THREADS 1024
/* FIXME: */
/* Things may get quite slow for large numbers of threads, */
/* since we look them up with sequential search. */
@@ -143,7 +143,7 @@
while (GC_please_stop) Sleep(20);
return thread_table + i;
}
-
+
/*
* GC_max_thread_index may temporarily be larger than MAX_THREADS.
* To avoid subscript errors, we check on access.
@@ -463,7 +463,8 @@
if (!GC_is_initialized) GC_init();
/* make sure GC is initialized (i.e. main thread is attached) */
- args = GC_malloc_uncollectable(sizeof(thread_args));
+ /* It appears to be unsafe to use the GC's allocator here */
+ args = malloc(sizeof(thread_args));
/* Handed off to and deallocated by child thread. */
if (0 == args) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -499,7 +500,7 @@
#ifndef __GNUC__
} __finally {
#endif /* __GNUC__ */
- GC_free(args);
+ free(args);
GC_delete_thread(GetCurrentThreadId());
#ifndef __GNUC__
}
@@ -568,7 +569,7 @@
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();
}
@@ -610,6 +611,173 @@
return result;
}
+/*
+ * Some CYGWIN applications (like portable.net) use CreateThread.
+ * The wrappers for CreateThread for non-cgywin systems don't seem to
+ * be stable in so calls to CreateThread are redirected to through
+ * GC_pthread_create.
+ */
+
+/*
+ * Start information for CreateThread.
+ */
+struct createthread_startinfo
+{
+ HANDLE handle;
+ DWORD threadid;
+ HANDLE waitHandle;
+ LPVOID parameter;
+ DWORD creationFlags;
+ LPTHREAD_START_ROUTINE startFunc;
+};
+
+/*
+ * pthread start function for redirected CreateThread calls.
+ */
+static void *main_thread_start(void *arg)
+{
+ struct createthread_startinfo startinfo;
+ struct createthread_startinfo *startinfo_ptr;
+
+ startinfo_ptr = (struct createthread_startinfo *)arg;
+
+ /* Get and duplicate the handle to this thread and
+ return it through the startinfo_ptr */
+ if (!DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &startinfo_ptr->handle,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS))
+ {
+ DWORD last_error = GetLastError();
+ GC_printf1("Last error code: %lx\n", last_error);
+ ABORT("DuplicateHandle failed");
+ }
+
+ /* Return the ThreadID through the startinfo_ptr */
+ startinfo_ptr->threadid = GetCurrentThreadId();
+
+ /* Make a copy of the startinfo because it'it could be freed by the
+ creater thread any time after we set startinfo.waitHandle */
+ startinfo = *startinfo_ptr;
+
+ /* Let the creater thread know we have set the handle/threadid */
+ SetEvent(startinfo.waitHandle);
+
+ /* If CREATE_SUSPENDED is requested then suspend the thread */
+ if (startinfo.creationFlags & CREATE_SUSPENDED)
+ {
+ SuspendThread(GetCurrentThread());
+ }
+
+ /* Start the actual thread function */
+ return (void *)startinfo.startFunc(startinfo.parameter);
+}
+
+/*
+ * Wrapper for CreateThread. This function is for CYGWIN systems and
+ * redirects CreateThread calls to GC_pthread_create.
+ */
+GC_API HANDLE WINAPI GC_CreateThread(
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
+{
+ HANDLE handle;
+ pthread_t new_thread;
+ pthread_attr_t attr;
+ struct createthread_startinfo *startinfo_ptr;
+
+ /* Make sure GC is initialized (i.e. main thread is attached) */
+ if (!GC_is_initialized)
+ {
+ GC_init();
+ }
+
+ /* Allocate start information for the CreateThread call */
+ /* It appears to be unsafe to use the GC's allocator here */
+ startinfo_ptr = malloc(sizeof(struct createthread_startinfo));
+
+ if (startinfo_ptr == 0)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+
+ return NULL;
+ }
+
+ /* This WaitHandle will be signalled once the new thread has filled in
+ its handle/threadid information */
+ startinfo_ptr->waitHandle = CreateEvent(0, 1, 0, 0);
+
+ if (startinfo_ptr->waitHandle == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ free(startinfo_ptr);
+
+ return NULL;
+ }
+
+ /* Setup the CreateThread start information */
+ startinfo_ptr->handle = 0;
+ startinfo_ptr->threadid = 0;
+ startinfo_ptr->parameter = lpParameter;
+ startinfo_ptr->startFunc = lpStartAddress;
+ startinfo_ptr->creationFlags = dwCreationFlags;
+
+ /* Initialize the pthread_attr_t with defaults */
+ if (pthread_attr_init(&attr) != 0)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ CloseHandle(startinfo_ptr->waitHandle);
+ free(startinfo_ptr);
+
+ return NULL;
+ }
+
+ if (dwStackSize != 0)
+ {
+ /* Set the stack size if specified */
+ pthread_attr_setstacksize(&attr, (size_t)dwStackSize);
+ }
+
+ /* Create a CYGWIN pthread */
+ if (GC_pthread_create(&new_thread, &attr, main_thread_start, startinfo_ptr) == 0)
+ {
+ /* Wait for the handle/threadid */
+ WaitForSingleObject(startinfo_ptr->waitHandle, INFINITE);
+
+ if (lpThreadId)
+ {
+ /* Get and set the new threadid */
+ *lpThreadId = startinfo_ptr->threadid;
+ }
+
+ /* Get the new thread handle */
+ handle = startinfo_ptr->handle;
+ }
+ else
+ {
+ handle = 0;
+
+ if (lpThreadId)
+ {
+ *lpThreadId = 0;
+ }
+ }
+
+ /* Free the pthread_attr_t structure */
+ pthread_attr_destroy(&attr);
+ /* Free the wait event */
+ CloseHandle(startinfo_ptr->waitHandle);
+ /* Free the startinfo */
+ free(startinfo_ptr);
+
+ return handle;
+}
+
+
/* Cygwin-pthreads calls CreateThread internally, but it's not
* easily interceptible by us..
* so intercept pthread_create instead
@@ -621,12 +789,13 @@
int result;
struct start_info * si;
- if (!GC_is_initialized) GC_init();
+ 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));
+ /* library, which isn't visible to the collector. */
+ /* It appears to be unsafe to use the GC's allocator here */
+ si = malloc(sizeof(struct start_info));
if (0 == si) return(EAGAIN);
si -> start_routine = start_routine;
@@ -644,7 +813,7 @@
result = pthread_create(new_thread, attr, GC_start_routine, si);
if (result) { /* failure */
- GC_free(si);
+ free(si);
}
return(result);
@@ -681,7 +850,7 @@
if (si-> detached) me -> flags |= DETACHED;
me -> pthread_id = pthread_id = pthread_self();
- GC_free(si); /* was allocated uncollectable */
+ free(si); /* was allocated uncollectable */
pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
result = (*start)(start_arg);