RE: [Gc] 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]> |
Here's an updated patch that doesn't require dynamic memory allocation. > -----Original Message----- > From: gc-bounces-o/PNRNCSakrWxDs0y9d3MAC/[email protected] [mailto:gc-bounces-o/PNRNCSakrWxDs0y9d3MAC/[email protected]] > On Behalf Of Thong (Tum) Nguyen > Sent: Thursday, 27 May 2004 9:38 a.m. > To: 'Boehm, Hans'; gc-o/PNRNCSakrWxDs0y9d3MAC/[email protected]; 'DotGnu-Develop' > Subject: [Gc] Using GC_CreateThread with CYGWIN builds > > 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.4 KB)
--- /home/Tum/gc6.3alpha6/win32_threads.c 2004-01-27 13:14:00.000000000 +1300
+++ win32_threads.c 2004-05-28 12:11:59.340125000 +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,155 @@
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_ptr;
+ DWORD *threadid_ptr;
+ 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_ptr,
+ 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 */
+ if (startinfo_ptr->threadid_ptr)
+ {
+ *startinfo_ptr->threadid_ptr = GetCurrentThreadId();
+ }
+
+ /* Make a copy of the startinfo because it could be invalid
+ any time after we signal 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;
+
+ /* Make sure GC is initialized (i.e. main thread is attached) */
+ if (!GC_is_initialized)
+ {
+ GC_init();
+ }
+
+ /* This WaitHandle will be signalled once the new thread has filled in
+ its handle/threadid information */
+ startinfo.waitHandle = CreateEvent(0, 1, 0, 0);
+
+ if (startinfo.waitHandle == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+
+ return 0;
+ }
+
+ handle = 0;
+
+ /* Setup the CreateThread start information */
+ startinfo.handle_ptr = &handle;
+ startinfo.threadid_ptr = lpThreadId;
+ startinfo.parameter = lpParameter;
+ startinfo.startFunc = lpStartAddress;
+ startinfo.creationFlags = dwCreationFlags;
+
+ /* Initialize the pthread_attr_t with defaults */
+ if (pthread_attr_init(&attr) != 0)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ CloseHandle(startinfo.waitHandle);
+
+ return 0;
+ }
+
+ 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) == 0)
+ {
+ /* Wait for the handle/threadid to be filled in */
+ WaitForSingleObject(startinfo.waitHandle, INFINITE);
+ }
+ else
+ {
+ handle = 0;
+
+ if (lpThreadId)
+ {
+ *lpThreadId = 0;
+ }
+ }
+
+ /* Free the pthread_attr_t structure */
+ pthread_attr_destroy(&attr);
+ /* Free the wait event */
+ CloseHandle(startinfo.waitHandle);
+
+ return handle;
+}
+
+
/* Cygwin-pthreads calls CreateThread internally, but it's not
* easily interceptible by us..
* so intercept pthread_create instead
@@ -621,12 +771,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 +795,7 @@
result = pthread_create(new_thread, attr, GC_start_routine, si);
if (result) { /* failure */
- GC_free(si);
+ free(si);
}
return(result);
@@ -681,7 +832,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);