[Gc] Fix for threaded gc on MacOSX

Andrew Begel abegel@eecs.berkeley.edu
Thu, 11 Dec 2003 00:00:54 -0800


--Apple-Mail-3-480657379
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

Attached is a patch to gc6.3alpha2 to get multi-threaded programs that 
use the GC to work properly on MacOSX.

The main idea is to forget using the pthread stuff for GC_stop_world(), 
GC_start_world() and GC_push_all_stacks(), and instead, use the mach 
threads.

GC_stop_world() loops over the mach threads and suspends them (does the 
thread state really matter? What harm do you get by suspending a thread 
that is not running? Mach keeps track of how many times the thread was 
suspended and only resumes it when that count reaches 0).

GC_start_world() equivalently starts up the mach threads.

GC_push_all_stacks() loops over the threads. For the current thread, it 
pushes a stack marked by GC_approx_sp() and FindTopOfStack() 
(FindTopOfStack() is a function that walks the stack, frame by frame, 
back to the top). For other threads, it gets their thread_state and 
pushes the stack marked by register 1 (the sp on PPC) and 
FindTopOfStack(r1). It then GC_push_one()'s the other 31 registers for 
that thread.

In my testing, using the GC in a multi-threaded situation where my code 
was not in control of initializing the GC from the main thread before 
other threads were initialized (basically every other situation where 
GC-enabled code executes from arbitrary threads at any point during 
execution), it seems to work great.

On the other hand, using the test application from test.c seems to hang 
waiting on a semaphore. Apparently my changes break the traditional way 
of using the GC by override pthread_create() and stuff.

Well, it's a start, anyway. I think this solution is more robust and 
much easier to use than the one that requires you to both own main() 
and init the GC before starting any other thread.

Andrew



--Apple-Mail-3-480657379
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="gc.diff"
Content-Disposition: attachment;
	filename=gc.diff

--- gc6.3alpha2/darwin_stop_world.c	Fri Jun 13 12:11:00 2003
+++ gc6.3alpha2-andy/darwin_stop_world.c	Wed Dec 10 23:49:29 2003
@@ -16,88 +16,127 @@
 */
 #define PPC_RED_ZONE_SIZE 224
 
+/* I have no idea how big this should be. Or if I386 uses a red zone at all. */
+#define I386_RED_ZONE_SIZE 224
+
+/* Not 64-bit clean. Wait until Apple defines their 64-bit ABI */
+typedef struct StackFrame {
+  unsigned int	savedSP;
+  unsigned int	savedCR;
+  unsigned int	savedLR;
+} StackFrame;
+
+unsigned int FindTopOfStack(unsigned int stack_start) {
+  StackFrame	*frame;
+  
+  if (stack_start == 0) {
+    __asm__ volatile("lwz	%0,0(r1)" : "=r" (frame));
+  } else {
+    frame = (StackFrame *)stack_start;
+  }
+  while((StackFrame *)frame->savedSP != NULL) {
+    if (((frame->savedLR & ~3) == 0) || ((~(frame->savedLR) & ~3) == 0))
+      break;
+    frame = (StackFrame*)frame->savedSP;
+  }
+  return (unsigned int)frame;
+}	
+
 void GC_push_all_stacks() {
     int i;
     kern_return_t r;
-    GC_thread p;
-    pthread_t me;
+    mach_port_t me;
     ptr_t lo, hi;
-#	if defined(POWERPC)
-        ppc_thread_state_t state;
-#	else
-#		error FIXME for non-ppc OS X
-#	endif
-    mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
-    
-    me = pthread_self();
+    thread_act_array_t act_list;
+    mach_msg_type_number_t listcount;
+
+    me = mach_thread_self();
     if (!GC_thr_initialized) GC_thr_init();
     
-    for(i=0;i<THREAD_TABLE_SZ;i++) {
-        for(p=GC_threads[i];p!=0;p=p->next) {
-            if(p -> flags & FINISHED) continue;
-            if(pthread_equal(p->id,me)) {
-                lo = GC_approx_sp();
-            } else {
-                /* Get the thread state (registers, etc) */
-                r = thread_get_state(
-                    p->stop_info.mach_thread,
-                    MACHINE_THREAD_STATE,
-                    (natural_t*)&state,
-                    &thread_state_count);
-                if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
-    
-                #ifdef POWERPC
-                    lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
-                    
-                    GC_push_one(state.r0); 
-                    GC_push_one(state.r2); 
-                    GC_push_one(state.r3); 
-                    GC_push_one(state.r4); 
-                    GC_push_one(state.r5); 
-                    GC_push_one(state.r6); 
-                    GC_push_one(state.r7); 
-                    GC_push_one(state.r8); 
-                    GC_push_one(state.r9); 
-                    GC_push_one(state.r10); 
-                    GC_push_one(state.r11); 
-                    GC_push_one(state.r12); 
-                    GC_push_one(state.r13); 
-                    GC_push_one(state.r14); 
-                    GC_push_one(state.r15); 
-                    GC_push_one(state.r16); 
-                    GC_push_one(state.r17); 
-                    GC_push_one(state.r18); 
-                    GC_push_one(state.r19); 
-                    GC_push_one(state.r20); 
-                    GC_push_one(state.r21); 
-                    GC_push_one(state.r22); 
-                    GC_push_one(state.r23); 
-                    GC_push_one(state.r24); 
-                    GC_push_one(state.r25); 
-                    GC_push_one(state.r26); 
-                    GC_push_one(state.r27); 
-                    GC_push_one(state.r28); 
-                    GC_push_one(state.r29); 
-                    GC_push_one(state.r30); 
-                    GC_push_one(state.r31);
-                #else
-                #	error FIXME for non-PPC darwin
-                #endif /* !POWERPC */
-            } /* p != me */
-            if(p->flags & MAIN_THREAD)
-                hi = GC_stackbottom;
-            else
-                hi = p->stack_end;
-            #if DEBUG_THREADS
-                GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
-                    (unsigned long) p -> id,
-                    (unsigned long) lo,
-                    (unsigned long) hi
-                );
-            #endif
-            GC_push_all_stack(lo,hi);
-        } /* for(p=GC_threads[i]...) */
-    } /* for(i=0;i<THREAD_TABLE_SZ...) */
+    r = task_threads(current_task(), &act_list, &listcount);
+    if(r != KERN_SUCCESS) ABORT("task_threads failed");
+    for(i = 0; i < listcount; i++) {
+      thread_act_t thread = act_list[i];
+      if (thread == me) {
+	lo = GC_approx_sp();
+	hi = (ptr_t)FindTopOfStack(0);
+      } else {
+ #ifdef POWERPC
+	ppc_thread_state_t info;
+	mach_msg_type_number_t outCount = THREAD_STATE_MAX;
+	r = thread_get_state(thread, MACHINE_THREAD_STATE, (natural_t *)&info, &outCount);
+	if(r != KERN_SUCCESS) ABORT("task_get_state failed");
+
+	lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
+	hi = (ptr_t)FindTopOfStack(info.r1);
+
+	GC_push_one(info.r0); 
+	GC_push_one(info.r2); 
+	GC_push_one(info.r3); 
+	GC_push_one(info.r4); 
+	GC_push_one(info.r5); 
+	GC_push_one(info.r6); 
+	GC_push_one(info.r7); 
+	GC_push_one(info.r8); 
+	GC_push_one(info.r9); 
+	GC_push_one(info.r10); 
+	GC_push_one(info.r11); 
+	GC_push_one(info.r12); 
+	GC_push_one(info.r13); 
+	GC_push_one(info.r14); 
+	GC_push_one(info.r15); 
+	GC_push_one(info.r16); 
+	GC_push_one(info.r17); 
+	GC_push_one(info.r18); 
+	GC_push_one(info.r19); 
+	GC_push_one(info.r20); 
+	GC_push_one(info.r21); 
+	GC_push_one(info.r22); 
+	GC_push_one(info.r23); 
+	GC_push_one(info.r24); 
+	GC_push_one(info.r25); 
+	GC_push_one(info.r26); 
+	GC_push_one(info.r27); 
+	GC_push_one(info.r28); 
+	GC_push_one(info.r29); 
+	GC_push_one(info.r30); 
+	GC_push_one(info.r31);
+#else
+#warning This is completely untested and likely will not work
+	i386_thread_state_t info;
+	mach_msg_type_number_t outCount = THREAD_STATE_MAX;
+	r = thread_get_state(thread, MACHINE_THREAD_STATE, (natural_t *)&info, &outCount);
+	if(r != KERN_SUCCESS) ABORT("task_get_state failed");
+
+	lo = (void*)(info.esp - I386_RED_ZONE_SIZE);
+	hi = (ptr_t)FindTopOfStack(info.esp);
+
+	GC_push_one(info.eax); 
+	GC_push_one(info.ebx); 
+	GC_push_one(info.ecx); 
+	GC_push_one(info.edx); 
+	GC_push_one(info.edi); 
+	GC_push_one(info.esi); 
+	/* GC_push_one(info.ebp);  */
+	/* GC_push_one(info.esp);  */
+	GC_push_one(info.ss); 
+	GC_push_one(info.eip); 
+	GC_push_one(info.cs); 
+	GC_push_one(info.ds); 
+	GC_push_one(info.es); 
+	GC_push_one(info.fs); 
+	GC_push_one(info.gs); 
+#endif /* !POWERPC */
+      }
+#if DEBUG_THREADS
+      GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
+		 (unsigned long) thread,
+		 (unsigned long) lo,
+		 (unsigned long) hi
+		 );
+#endif
+      GC_push_all_stack(lo, hi); 
+    } /* for(p=GC_threads[i]...) */
 }
 
 /* Caller holds allocation lock.	*/
@@ -105,11 +144,13 @@
 {
     int i;
     GC_thread p;
-    pthread_t my_thread = pthread_self();
+    mach_port_t my_thread = mach_thread_self();
     kern_return_t kern_result;
+    thread_act_array_t act_list;
+    mach_msg_type_number_t listcount;
     
     #if DEBUG_THREADS
-    GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
+    GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
     #endif
        
     /* Make sure all free list construction has stopped before we start. */
@@ -122,31 +163,26 @@
       /* We should have previously waited for it to become zero. */
 #   endif /* PARALLEL_MARK */
 
-    for (i = 0; i < THREAD_TABLE_SZ; i++) {
-        for (p = GC_threads[i]; p != 0; p = p -> next) {
-            if (p -> id == my_thread) continue;
-            if (p -> flags & FINISHED) continue;
-            if (p -> thread_blocked) /* Will wait */ continue;
-            
-            #if DEBUG_THREADS
-            GC_printf1("Suspending thread 0x%lx\n", p -> id);
-            #endif
-            
-            /* Suspend the thread */
-            kern_result = thread_suspend(p->stop_info.mach_thread);
-            if(kern_result != KERN_SUCCESS) ABORT("thread_suspend failed");
-            
-            /* This is only needed if we are modifying the threads 
-               state. thread_abort_safely should also be used
-               if this code is ever added in again.
-               
-               kern_result = thread_abort(p->stop_info.mach_thread);
-               if(kern_result != KERN_SUCCESS)
-                   ABORT("thread_abort failed (%ul)",kern_result);
-            */
-        }
-    }
-    
+      kern_result = task_threads(current_task(), &act_list, &listcount);
+      for(i = 0; i < listcount; i++) {
+	thread_act_t thread = act_list[i];
+	if (thread != my_thread) { 
+
+	  /* 
+	   * Should we worry about what state the thread is in? 
+	   * Does it matter if we try to suspend a thread that is
+	   *  already waiting?  
+	   */
+#if DEBUG_THREADS
+	  GC_printf1("Suspending 0x%lx\n", thread);
+#endif
+	  /* Suspend the thread */
+	  kern_result = thread_suspend(thread);
+	  if(kern_result != KERN_SUCCESS) ABORT("thread_suspend failed");
+	} 
+      }
+      
+      
 #   ifdef MPROTECT_VDB
     if(GC_incremental) {
         extern void GC_mprotect_stop();
@@ -158,7 +194,7 @@
       GC_release_mark_lock();
 #   endif
     #if DEBUG_THREADS
-      GC_printf1("World stopped from 0x%lx\n", pthread_self());
+      GC_printf1("World stopped from 0x%lx\n", my_thread);
     #endif
 }
 
@@ -166,11 +202,13 @@
 /* the world stopped.							*/
 void GC_start_world()
 {
-    pthread_t my_thread = pthread_self();
-    int i;
-    GC_thread p;
-    kern_return_t kern_result;
-
+  mach_port_t my_thread = mach_thread_self();
+  int i;
+  GC_thread p;
+  kern_return_t kern_result;
+  thread_act_array_t act_list;
+  mach_msg_type_number_t listcount;
+  
 #   if DEBUG_THREADS
       GC_printf0("World starting\n");
 #   endif
@@ -182,28 +220,27 @@
     }
 #   endif
 
-    for (i = 0; i < THREAD_TABLE_SZ; i++) {
-        for (p = GC_threads[i]; p != 0; p = p -> next) {
-            if (p -> id == my_thread) continue;
-            if (p -> flags & FINISHED) continue;
-            if (p -> thread_blocked) continue;
-    
-            #if DEBUG_THREADS
-            GC_printf1("Resuming 0x%lx\n", p -> id);
-            #endif
-            
-            /* Resume the thread */
-            kern_result = thread_resume(p->stop_info.mach_thread);
-            if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
-        }
+    kern_result = task_threads(current_task(), &act_list, &listcount);
+    for(i = 0; i < listcount; i++) {
+      thread_act_t thread = act_list[i];
+      if (thread != my_thread) { 
+#if DEBUG_THREADS
+	GC_printf1("Resuming 0x%lx\n", thread);
+#endif
+	/* Resume the thread */
+	kern_result = thread_resume(thread);
+	if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
+      } 
     }
-    #if DEBUG_THREADS
+
+#if DEBUG_THREADS
       GC_printf0("World started\n");
-    #endif
+#endif
 }
 
 void GC_stop_init() {
 
 }
+
 
 #endif

--Apple-Mail-3-480657379--