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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.