[Gc] porting Hans Boehm GC to MacOSX

Jesse Rosenstock jmr@ugcs.caltech.edu
Thu, 13 Feb 2003 10:34:37 -0800


Here's a new version of the patch.  I've introduced a USE_PPC_PREFETCH
#define to guard the prefetch stuff instead of doing it unconditionally,
and switched MACOSX to USE_MMAP, but with USE_MMAP_ANON which I also
added because mapping /dev/zero doesn't seem to work.  Everything else
should be the same as before.

diff -ur gc6.2alpha3.orig/Makefile gc6.2alpha3-threads/Makefile
--- gc6.2alpha3.orig/Makefile	2003-01-29 20:12:07.000000000 -0800
+++ gc6.2alpha3-threads/Makefile	2003-02-13 10:15:01.000000000 -0800
@@ -73,6 +73,7 @@
 # -DGC_OSF1_THREADS enables support for Tru64 pthreads.  Untested.
 # -DGC_FREEBSD_THREADS enables support for FreeBSD pthreads.  Untested.
 #   Appeared to run into some underlying thread problems.
+# -DGC_MACOSX_THREADS enables support for Mac OS X pthreads.  Untested.
 # -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads.
 #   See README.DGUX386.
 # -DALL_INTERIOR_POINTERS allows all pointers to the interior
@@ -218,6 +219,9 @@
 # -DUSE_3DNOW_PREFETCH causes the collector to issue AMD 3DNow style
 #   prefetch instructions.  Same restrictions as USE_I686_PREFETCH.
 #   Minimally tested.  Didn't appear to be an obvious win on a K6-2/500.
+# -DUSE_PPC_PREFETCH causes the collector to issue PowerPC style
+#   prefetch instructions.  No effect except on PowerPC OS X platforms.
+#   Performance impact untested.
 # -DGC_USE_LD_WRAP in combination with the old flags listed in README.linux
 #   causes the collector some system and pthread calls in a more transparent
 #   fashion than the usual macro-based approach.  Requires GNU ld, and
diff -ur gc6.2alpha3.orig/Makefile.direct gc6.2alpha3-threads/Makefile.direct
--- gc6.2alpha3.orig/Makefile.direct	2003-01-27 15:59:50.000000000 -0800
+++ gc6.2alpha3-threads/Makefile.direct	2003-02-13 10:13:00.000000000 -0800
@@ -73,6 +73,7 @@
 # -DGC_OSF1_THREADS enables support for Tru64 pthreads.  Untested.
 # -DGC_FREEBSD_THREADS enables support for FreeBSD pthreads.  Untested.
 #   Appeared to run into some underlying thread problems.
+# -DGC_MACOSX_THREADS enables support for Mac OS X pthreads.  Untested.
 # -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads.
 #   See README.DGUX386.
 # -DALL_INTERIOR_POINTERS allows all pointers to the interior
@@ -218,6 +219,9 @@
 # -DUSE_3DNOW_PREFETCH causes the collector to issue AMD 3DNow style
 #   prefetch instructions.  Same restrictions as USE_I686_PREFETCH.
 #   Minimally tested.  Didn't appear to be an obvious win on a K6-2/500.
+# -DUSE_PPC_PREFETCH causes the collector to issue PowerPC style
+#   prefetch instructions.  No effect except on PowerPC OS X platforms.
+#   Performance impact untested.
 # -DGC_USE_LD_WRAP in combination with the old flags listed in README.linux
 #   causes the collector some system and pthread calls in a more transparent
 #   fashion than the usual macro-based approach.  Requires GNU ld, and
diff -ur gc6.2alpha3.orig/configure.in gc6.2alpha3-threads/configure.in
--- gc6.2alpha3.orig/configure.in	2002-10-11 06:54:12.000000000 -0700
+++ gc6.2alpha3-threads/configure.in	2003-02-12 22:29:48.000000000 -0800
@@ -136,6 +136,9 @@
      *-*-cygwin*)
 	AC_DEFINE(GC_WIN32_THREADS)
 	;;
+     *-*-darwin*)
+	AC_DEFINE(GC_MACOSX_THREADS)
+	;;
      *-*-osf*)
 	AC_DEFINE(GC_OSF1_THREADS)
         if test "${enable_parallel_mark}" = yes; then
diff -ur gc6.2alpha3.orig/gc_dlopen.c gc6.2alpha3-threads/gc_dlopen.c
--- gc6.2alpha3.orig/gc_dlopen.c	2003-01-23 14:41:28.000000000 -0800
+++ gc6.2alpha3-threads/gc_dlopen.c	2003-02-12 10:54:22.000000000 -0800
@@ -25,7 +25,8 @@
 
 #include "private/gc_priv.h"
 
-# if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)
+# if (defined(GC_PTHREADS) && !defined(GC_MACOSX_THREADS)) \
+      || defined(GC_SOLARIS_THREADS)
 
 # if defined(dlopen) && !defined(GC_USE_LD_WRAP)
     /* To support various threads pkgs, gc.h interposes on dlopen by     */
diff -ur gc6.2alpha3.orig/include/gc.h gc6.2alpha3-threads/include/gc.h
--- gc6.2alpha3.orig/include/gc.h	2003-01-29 09:35:33.000000000 -0800
+++ gc6.2alpha3-threads/include/gc.h	2003-02-11 22:08:30.000000000 -0800
@@ -84,7 +84,7 @@
 # if defined(GC_SOLARIS_PTHREADS) || defined(GC_FREEBSD_THREADS) || \
 	defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) || \
 	defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) || \
-	defined(GC_DGUX386_THREADS) || \
+	defined(GC_DGUX386_THREADS) || defined(GC_MACOSX_THREADS) || \
         (defined(GC_WIN32_THREADS) && defined(__CYGWIN32__))
 #   define GC_PTHREADS
 # endif
diff -ur gc6.2alpha3.orig/include/private/gcconfig.h gc6.2alpha3-threads/include/private/gcconfig.h
--- gc6.2alpha3.orig/include/private/gcconfig.h	2003-01-29 09:47:10.000000000 -0800
+++ gc6.2alpha3-threads/include/private/gcconfig.h	2003-02-13 10:19:25.000000000 -0800
@@ -714,13 +714,28 @@
       /* There are reasons to suspect this may not be reliable. 	*/
 #     define ALIGNMENT 4
 #     define OS_TYPE "MACOSX"
+#     ifdef GC_MACOSX_THREADS
+#	define SIG_SUSPEND SIGXCPU
+#	define SIG_THR_RESTART SIGXFSZ
+#     endif
+      /* XXX: see get_end(3), get_etext() and get_end() should not be used */
 #     define DATASTART ((ptr_t) get_etext())
 #     define STACKBOTTOM ((ptr_t) 0xc0000000)
-#     define DATAEND	/* not needed */
+#     define DATAEND	((ptr_t) get_end())
+#     define USE_MMAP
+#     define USE_MMAP_ANON
 /* #     define MPROTECT_VDB  -- There is some evidence that this breaks 
- *       on some minor versions of MACOSX.  In theory, it should be OK */
+ *       on some minor versions of MACOSX, i.e. 10.2.3.  In theory,
+ *       it should be OK */
 #     include <unistd.h>
 #     define GETPAGESIZE() getpagesize()
+#     if defined(USE_PPC_PREFETCH) && defined(__GNUC__)
+	/* The performance impact of prefetches is untested */
+#	define PREFETCH(x) \
+	  __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x)))
+#	define PREFETCH_FOR_WRITE(x) \
+	  __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x)))
+#     endif
 #   endif
 #   ifdef NETBSD
 #     define ALIGNMENT 4
@@ -2015,7 +2030,7 @@
 					    + GC_page_size) \
 					    + GC_page_size-1)
 #   else
-#     if defined(NEXT) || defined(MACOSX) || defined(DOS4GW) || \
+#     if defined(NEXT) || defined(DOS4GW) || \
 		 (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) || \
 		 (defined(SUNOS5) && !defined(USE_MMAP))
 #       define GET_MEM(bytes) HBLKPTR((size_t) \
diff -ur gc6.2alpha3.orig/linux_threads.c gc6.2alpha3-threads/linux_threads.c
--- gc6.2alpha3.orig/linux_threads.c	2002-10-01 15:36:06.000000000 -0700
+++ gc6.2alpha3-threads/linux_threads.c	2003-02-12 23:33:57.000000000 -0800
@@ -107,6 +107,10 @@
 # include <sys/stat.h>
 # include <fcntl.h>
 
+#if defined(GC_MACOSX_THREADS)
+# include <sys/sysctl.h>
+#endif /* GC_MACOSX_THREADS */
+
 #if defined(GC_DGUX386_THREADS)
 # include <sys/dg_sys_info.h>
 # include <sys/_int_psem.h>
@@ -511,7 +515,15 @@
 #  endif
 #endif
 
-sem_t GC_suspend_ack_sem;
+#ifdef GC_MACOSX_THREADS
+#  include <mach/task.h>
+#  include <mach/mach_init.h>
+#  include <mach/semaphore.h>
+
+   semaphore_t GC_suspend_ack_sem;
+#else
+   sem_t GC_suspend_ack_sem;
+#endif
 
 #if 0
 /*
@@ -675,7 +687,11 @@
     /* Tell the thread that wants to stop the world that this   */
     /* thread has been stopped.  Note that sem_post() is  	*/
     /* the only async-signal-safe primitive in LinuxThreads.    */
-    sem_post(&GC_suspend_ack_sem);
+#   ifdef GC_MACOSX_THREADS
+      semaphore_signal(GC_suspend_ack_sem);
+#   else
+      sem_post(&GC_suspend_ack_sem);
+#   endif
     me -> last_stop_count = my_stop_count;
 
     /* Wait until that thread tells us to restart by sending    */
@@ -965,38 +981,47 @@
 #   endif /* PARALLEL_MARK */
     ++GC_stop_count;
     n_live_threads = GC_suspend_all();
-    if (GC_retry_signals) {
-	unsigned long wait_usecs = 0;  /* Total wait since retry.	*/
-#	define WAIT_UNIT 3000
-#	define RETRY_INTERVAL 100000
-	for (;;) {
-	    int ack_count;
-
-	    sem_getvalue(&GC_suspend_ack_sem, &ack_count);
-	    if (ack_count == n_live_threads) break;
-	    if (wait_usecs > RETRY_INTERVAL) {
-		int newly_sent = GC_suspend_all();
-
-#               ifdef CONDPRINT
-                  if (GC_print_stats) {
-		    GC_printf1("Resent %ld signals after timeout\n",
-		               newly_sent);
-	          }
-#               endif
-	        sem_getvalue(&GC_suspend_ack_sem, &ack_count);
-		if (newly_sent < n_live_threads - ack_count) {
-		    WARN("Lost some threads during GC_stop_world?!\n",0);
-		    n_live_threads = ack_count + newly_sent;
-		}
-		wait_usecs = 0;
-	    }
-	    usleep(WAIT_UNIT);
-	    wait_usecs += WAIT_UNIT;
-	}
-    }
+    /* sem_getvalue() is not suppored on OS X, and there does not appear */
+    /* to be a mach equivalent, so we disable this code.		 */
+#   ifndef GC_MACOSX_THREADS
+      if (GC_retry_signals) {
+	  unsigned long wait_usecs = 0;  /* Total wait since retry.	*/
+#	  define WAIT_UNIT 3000
+#	  define RETRY_INTERVAL 100000
+	  for (;;) {
+	      int ack_count;
+
+	      sem_getvalue(&GC_suspend_ack_sem, &ack_count);
+	      if (ack_count == n_live_threads) break;
+	      if (wait_usecs > RETRY_INTERVAL) {
+		  int newly_sent = GC_suspend_all();
+
+#                 ifdef CONDPRINT
+		    if (GC_print_stats) {
+		      GC_printf1("Resent %ld signals after timeout\n",
+				 newly_sent);
+		    }
+#                 endif
+		  sem_getvalue(&GC_suspend_ack_sem, &ack_count);
+		  if (newly_sent < n_live_threads - ack_count) {
+		      WARN("Lost some threads during GC_stop_world?!\n",0);
+		      n_live_threads = ack_count + newly_sent;
+		  }
+		  wait_usecs = 0;
+	      }
+	      usleep(WAIT_UNIT);
+	      wait_usecs += WAIT_UNIT;
+	  }
+      }
+#   endif /* GC_MACOSX_THREADS */
     for (i = 0; i < n_live_threads; i++) {
-    	if (0 != sem_wait(&GC_suspend_ack_sem))
-	    ABORT("sem_wait in handler failed");
+#	ifdef GC_MACOSX_THREADS
+	  if (KERN_SUCCESS != semaphore_wait(GC_suspend_ack_sem))
+	      ABORT("semaphore_wait in handler failed");
+#	else
+	  if (0 != sem_wait(&GC_suspend_ack_sem))
+	      ABORT("sem_wait in handler failed");
+#	endif
     }
 #   ifdef PARALLEL_MARK
       GC_release_mark_lock();
@@ -1295,8 +1320,14 @@
     if (GC_thr_initialized) return;
     GC_thr_initialized = TRUE;
 
-    if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
-    	ABORT("sem_init failed");
+#   ifdef GC_MACOSX_THREADS
+      if (semaphore_create(mach_task_self(), &GC_suspend_ack_sem,
+			   SYNC_POLICY_FIFO, 0) != KERN_SUCCESS)
+	  ABORT("semaphore_create failed");
+#   else
+      if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
+	  ABORT("sem_init failed");
+#   endif
 
     act.sa_flags = SA_RESTART;
     if (sigfillset(&act.sa_mask) != 0) {
@@ -1361,6 +1392,12 @@
 #       if defined(GC_FREEBSD_THREADS)
           GC_nprocs = 1;
 #       endif
+#       if defined(GC_MACOSX_THREADS)
+	  int ncpus = 1;
+	  size_t len = sizeof(ncpus);
+	  sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
+	  GC_nprocs = ncpus;
+#       endif
 #	if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
           GC_nprocs = GC_get_nprocs();
 #	endif
@@ -1500,8 +1537,12 @@
     void *(*start_routine)(void *);
     void *arg;
     word flags;
+#ifdef GC_MACOSX_THREADS
+    semaphore_t registered;
+#else
     sem_t registered;   	/* 1 ==> in our thread table, but 	*/
 				/* parent hasn't yet noticed.		*/
+#endif
 };
 
 /* Called at thread exit.				*/
@@ -1630,7 +1671,11 @@
 	GC_printf1("start_routine = 0x%lx\n", start);
 #   endif
     start_arg = si -> arg;
-    sem_post(&(si -> registered));	/* Last action on si.	*/
+#   ifdef GC_MACOSX_THREADS
+      semaphore_signal(si->registered);
+#   else
+      sem_post(&(si -> registered));	/* Last action on si.	*/
+#   endif
     					/* OK to deallocate.	*/
     pthread_cleanup_push(GC_thread_exit_proc, 0);
 #   if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
@@ -1675,7 +1720,11 @@
     UNLOCK();
     if (!parallel_initialized) GC_init_parallel();
     if (0 == si) return(ENOMEM);
-    sem_init(&(si -> registered), 0, 0);
+#   ifdef GC_MACOSX_THREADS
+      semaphore_create(mach_task_self(), &si->registered, SYNC_POLICY_FIFO, 0);
+#   else
+      sem_init(&(si -> registered), 0, 0);
+#   endif
     si -> start_routine = start_routine;
     si -> arg = arg;
     LOCK();
@@ -1701,10 +1750,15 @@
     /* This also ensures that we hold onto si until the child is done	*/
     /* with it.  Thus it doesn't matter whether it is otherwise		*/
     /* visible to the collector.					*/
-        while (0 != sem_wait(&(si -> registered))) {
-	    if (EINTR != errno) ABORT("sem_wait failed");
-	}
-        sem_destroy(&(si -> registered));
+#	ifdef GC_MACOSX_THREADS
+	    semaphore_wait(si->registered);
+	    semaphore_destroy(mach_task_self(), si->registered);
+#	else
+	    while (0 != sem_wait(&(si -> registered))) {
+		if (EINTR != errno) ABORT("sem_wait failed");
+	    }
+	    sem_destroy(&(si -> registered));
+#	endif
 	LOCK();
 	GC_INTERNAL_FREE(si);
 	UNLOCK();
diff -ur gc6.2alpha3.orig/os_dep.c gc6.2alpha3-threads/os_dep.c
--- gc6.2alpha3.orig/os_dep.c	2003-01-29 19:56:03.000000000 -0800
+++ gc6.2alpha3-threads/os_dep.c	2003-02-13 10:09:22.000000000 -0800
@@ -1329,19 +1329,28 @@
 ptr_t GC_unix_get_mem(bytes)
 word bytes;
 {
-    static GC_bool initialized = FALSE;
-    static int fd;
     void *result;
     static ptr_t last_addr = HEAP_START;
 
-    if (!initialized) {
-	fd = open("/dev/zero", O_RDONLY);
-	fcntl(fd, F_SETFD, FD_CLOEXEC);
-	initialized = TRUE;
-    }
+#   ifndef USE_MMAP_ANON
+      static GC_bool initialized = FALSE;
+      static int fd;
+
+      if (!initialized) {
+	  fd = open("/dev/zero", O_RDONLY);
+	  fcntl(fd, F_SETFD, FD_CLOEXEC);
+	  initialized = TRUE;
+      }
+#   endif
+
     if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
-    result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
-		  GC_MMAP_FLAGS, fd, 0/* offset */);
+#   ifdef USE_MMAP_ANON
+      result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+		    GC_MMAP_FLAGS | MAP_ANON, -1, 0/* offset */);
+#   else
+      result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+		    GC_MMAP_FLAGS, fd, 0/* offset */);
+#   endif
     if (result == MAP_FAILED) return(0);
     last_addr = (ptr_t)result + bytes + GC_page_size - 1;
     last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
diff -ur gc6.2alpha3.orig/tests/test.c gc6.2alpha3-threads/tests/test.c
--- gc6.2alpha3.orig/tests/test.c	2002-11-06 09:28:14.000000000 -0800
+++ gc6.2alpha3-threads/tests/test.c	2003-02-11 22:00:25.000000000 -0800
@@ -1748,7 +1748,8 @@
 	}
 #   endif	/* GC_HPUX_THREADS */
     pthread_attr_init(&attr);
-#   if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS)
+#   if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \
+    	|| defined(GC_MACOSX_THREADS)
     	pthread_attr_setstacksize(&attr, 1000000);
 #   endif
     n_tests = 0;
diff -ur gc6.2alpha3.orig/threadlibs.c gc6.2alpha3-threads/threadlibs.c
--- gc6.2alpha3.orig/threadlibs.c	2002-08-01 18:16:21.000000000 -0700
+++ gc6.2alpha3-threads/threadlibs.c	2003-02-11 21:58:30.000000000 -0800
@@ -10,7 +10,8 @@
 	       "-Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,sleep\n");
 #   endif
 #   if defined(GC_LINUX_THREADS) || defined(GC_IRIX_THREADS) \
-	|| defined(GC_FREEBSD_THREADS) || defined(GC_SOLARIS_PTHREADS)
+	|| defined(GC_FREEBSD_THREADS) || defined(GC_SOLARIS_PTHREADS) \
+	|| defined(GC_MACOSX_THREADS)
         printf("-lpthread\n");
 #   endif
 #   if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)