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);
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.