]> granicus.if.org Git - gc/commitdiff
2011-02-20 Ivan Maidanski <ivmai@mail.ru> (mostly really Elijah Taylor)
authorivmai <ivmai>
Sun, 20 Feb 2011 10:54:32 +0000 (10:54 +0000)
committerIvan Maidanski <ivmai@mail.ru>
Tue, 26 Jul 2011 17:06:56 +0000 (21:06 +0400)
* gc_dlopen.c: Empty unit for NaCl.
* os_dep.c: Include fcntl.h for NaCl.
* os_dep.c (GC_get_main_stack_base): Ignore
USE_GET_STACKBASE_FOR_MAIN macro for NaCl.
* os_dep.c (GC_get_stack_base): Return GC_UNIMPLEMENTED for NaCl.
* os_dep.c (GC_remap): Use mmap (instead of mprotect) for NaCl.
* pthread_start.c (GC_inner_start_routine): Don't invoke
pthread_cleanup_push/pop for NaCl.
* pthread_stop_world.c (GC_nacl_num_gc_threads,
GC_nacl_thread_idx, GC_nacl_park_threads_now,
GC_nacl_thread_parker, GC_nacl_gc_thread_self,
GC_nacl_thread_parked, GC_nacl_thread_used,
GC_nacl_thread_parking_inited, GC_nacl_thread_alloc_lock): New
variable (fo NaCl only).
* pthread_stop_world.c (GC_suspend_handler): Reformat the code.
* pthread_stop_world.c (GC_remove_allowed_signals,
suspend_handler_mask, GC_stop_count, GC_world_is_stopped,
GC_retry_signals, SIG_THR_RESTART, GC_suspend_ack_sem,
GC_restart_ack_sem, GC_suspend_handler_inner, GC_suspend_handler,
GC_restart_handler): Don't define for NaCl.
* pthread_support.c (GC_get_nprocs): Ditto.
* include/private/gc_priv.h (SIG_SUSPEND): Ditto.
* include/private/gcconfig.h (LINUX): Ditto.
* pthread_stop_world.c (GC_push_all_stacks): Push register storage
for NaCl.
* pthread_stop_world.c (GC_suspend_all, GC_stop_world,
GC_start_world): Implement for NaCl.
* pthread_stop_world.c (GC_stop_world): Don't define unused "i"
local variable for OpenBSD (and NaCl).
* pthread_stop_world.c (NACL_STORE_REGS): New macro definition for
NaCl.
* pthread_stop_world.c (nacl_pre_syscall_hook,
__nacl_suspend_thread_if_needed, nacl_post_syscall_hook,
GC_nacl_initialize_gc_thread, GC_nacl_shutdown_gc_thread): New
function (for NaCl only).
* pthread_stop_world.c (GC_stop_init): Empty for NaCl.
* pthread_support.c (pthread_cancel, pthread_sigmask): Don't
redirect for NaCl.
* include/gc_pthread_redirects.h (pthread_cancel,
pthread_sigmask): Ditto.
* pthread_support.c (GC_nacl_initialize_gc_thread,
GC_nacl_shutdown_gc_thread): New internal prototype (NaCl only).
* pthread_support.c (GC_new_thread, GC_delete_thread): Initialize
and shutdown thread for NaCl.
* pthread_support.c (GC_thr_init): Call sysconf for NaCl.
* pthread_support.c (GC_pthread_exit): Call GC_thread_exit_proc
for NaCl.
* include/gc.h: Don't include features.h for NaCl.
* include/gc_pthread_redirects.h (GC_PTHREAD_CONST): New macro.
* include/gc_pthread_redirects.h (GC_pthread_create): Use
GC_PTHREAD_CONST instead of const.
* win32_threads.c (GC_pthread_create): Ditto.
* pthread_support.c (GC_pthread_create_t, GC_pthread_create,
pthread_create): Ditto.
* include/private/gcconfig.h (NACL): Recognize NaCl.
* include/private/gcconfig.h (GC_LINUX_THREADS): Valid for NaCl.
* include/private/pthread_stop_world.h (thread_stop_info): Add
reg_storage member; define NACL_GC_REG_STORAGE_SIZE macro (for
NaCl only).
* include/private/pthread_support.h (GC_nacl_gc_thread_self):
Declare internal variable (for NaCl only).

13 files changed:
ChangeLog
gc_dlopen.c
include/gc.h
include/gc_pthread_redirects.h
include/private/gc_priv.h
include/private/gcconfig.h
include/private/pthread_stop_world.h
include/private/pthread_support.h
os_dep.c
pthread_start.c
pthread_stop_world.c
pthread_support.c
win32_threads.c

index eaa595ea3d180ce0e839680564f9142ed547dfdc..8b386de91ba056b15107a59f41469fdda562a281 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,67 @@
+2011-02-20  Ivan Maidanski  <ivmai@mail.ru> (mostly really Elijah Taylor)
+
+       * gc_dlopen.c: Empty unit for NaCl.
+       * os_dep.c: Include fcntl.h for NaCl.
+       * os_dep.c (GC_get_main_stack_base): Ignore
+       USE_GET_STACKBASE_FOR_MAIN macro for NaCl.
+       * os_dep.c (GC_get_stack_base): Return GC_UNIMPLEMENTED for NaCl.
+       * os_dep.c (GC_remap): Use mmap (instead of mprotect) for NaCl.
+       * pthread_start.c (GC_inner_start_routine): Don't invoke
+       pthread_cleanup_push/pop for NaCl.
+       * pthread_stop_world.c (GC_nacl_num_gc_threads,
+       GC_nacl_thread_idx, GC_nacl_park_threads_now,
+       GC_nacl_thread_parker, GC_nacl_gc_thread_self,
+       GC_nacl_thread_parked, GC_nacl_thread_used,
+       GC_nacl_thread_parking_inited, GC_nacl_thread_alloc_lock): New
+       variable (fo NaCl only).
+       * pthread_stop_world.c (GC_suspend_handler): Reformat the code.
+       * pthread_stop_world.c (GC_remove_allowed_signals,
+       suspend_handler_mask, GC_stop_count, GC_world_is_stopped,
+       GC_retry_signals, SIG_THR_RESTART, GC_suspend_ack_sem,
+       GC_restart_ack_sem, GC_suspend_handler_inner, GC_suspend_handler,
+       GC_restart_handler): Don't define for NaCl.
+       * pthread_support.c (GC_get_nprocs): Ditto.
+       * include/private/gc_priv.h (SIG_SUSPEND): Ditto.
+       * include/private/gcconfig.h (LINUX): Ditto.
+       * pthread_stop_world.c (GC_push_all_stacks): Push register storage
+       for NaCl.
+       * pthread_stop_world.c (GC_suspend_all, GC_stop_world,
+       GC_start_world): Implement for NaCl.
+       * pthread_stop_world.c (GC_stop_world): Don't define unused "i"
+       local variable for OpenBSD (and NaCl).
+       * pthread_stop_world.c (NACL_STORE_REGS): New macro definition for
+       NaCl.
+       * pthread_stop_world.c (nacl_pre_syscall_hook,
+       __nacl_suspend_thread_if_needed, nacl_post_syscall_hook,
+       GC_nacl_initialize_gc_thread, GC_nacl_shutdown_gc_thread): New
+       function (for NaCl only).
+       * pthread_stop_world.c (GC_stop_init): Empty for NaCl.
+       * pthread_support.c (pthread_cancel, pthread_sigmask): Don't
+       redirect for NaCl.
+       * include/gc_pthread_redirects.h (pthread_cancel,
+       pthread_sigmask): Ditto.
+       * pthread_support.c (GC_nacl_initialize_gc_thread,
+       GC_nacl_shutdown_gc_thread): New internal prototype (NaCl only).
+       * pthread_support.c (GC_new_thread, GC_delete_thread): Initialize
+       and shutdown thread for NaCl.
+       * pthread_support.c (GC_thr_init): Call sysconf for NaCl.
+       * pthread_support.c (GC_pthread_exit): Call GC_thread_exit_proc
+       for NaCl.
+       * include/gc.h: Don't include features.h for NaCl.
+       * include/gc_pthread_redirects.h (GC_PTHREAD_CONST): New macro.
+       * include/gc_pthread_redirects.h (GC_pthread_create): Use
+       GC_PTHREAD_CONST instead of const.
+       * win32_threads.c (GC_pthread_create): Ditto.
+       * pthread_support.c (GC_pthread_create_t, GC_pthread_create,
+       pthread_create): Ditto.
+       * include/private/gcconfig.h (NACL): Recognize NaCl.
+       * include/private/gcconfig.h (GC_LINUX_THREADS): Valid for NaCl.
+       * include/private/pthread_stop_world.h (thread_stop_info): Add
+       reg_storage member; define NACL_GC_REG_STORAGE_SIZE macro (for
+       NaCl only).
+       * include/private/pthread_support.h (GC_nacl_gc_thread_self):
+       Declare internal variable (for NaCl only).
+
 2011-02-19  Ivan Maidanski  <ivmai@mail.ru>
 
        * aclocal.m4: Regenerate (by autoreconf -vif using autoconf-2.68,
index 6d886fd6ac42534f0b68fde10fc660ff398e94ab..39074f769c4d6e4ddf6524d5ee233c831908442b 100644 (file)
@@ -24,7 +24,7 @@
 /* a dynamic library. -HB                                               */
 
 #if defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS) \
-    && !defined(GC_WIN32_PTHREADS)
+    && !defined(GC_WIN32_PTHREADS) && !defined(NACL)
 
 #undef GC_MUST_RESTORE_REDEFINED_DLOPEN
 #if defined(dlopen) && !defined(GC_USE_LD_WRAP)
index d90ba5c5653c00eacd4ff5b4b8d11ab4d0ccbf12..d891cf1da81808339eded0069834c2f43fbf57c9 100644 (file)
@@ -591,7 +591,9 @@ GC_API void * GC_CALL GC_malloc_atomic_ignore_off_page(size_t /* lb */)
 #endif
 
 #if defined(__linux__) || defined(__GLIBC__)
-# include <features.h>
+# if !defined(__native_client__)
+#   include <features.h>
+# endif
 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \
         && !defined(__ia64__) && !defined(__UCLIBC__) \
         && !defined(GC_HAVE_BUILTIN_BACKTRACE)
index dfb014c566d299310d1fcbcc311905c2fcfc9a18..8b2c47d0ee73ac9dbf0fb73358dfbe353103df5d 100644 (file)
@@ -32,7 +32,8 @@
 
 #include <pthread.h>
 
-#if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_PTHREADS)
+#if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_PTHREADS) \
+    && !defined(__native_client__)
 # include <signal.h>
 # include <dlfcn.h>
 
                                   sigset_t * /* oset */);
 # endif
   GC_API void *GC_dlopen(const char * /* path */, int /* mode */);
+#endif /* !GC_DARWIN_THREADS */
+
+#ifndef GC_PTHREAD_CONST
+# define GC_PTHREAD_CONST const
 #endif
 
-GC_API int GC_pthread_create(pthread_t *, const pthread_attr_t *,
+GC_API int GC_pthread_create(pthread_t *, GC_PTHREAD_CONST pthread_attr_t *,
                              void *(*)(void *), void * /* arg */);
 GC_API int GC_pthread_join(pthread_t, void ** /* retval */);
 GC_API int GC_pthread_detach(pthread_t);
@@ -61,7 +66,9 @@ GC_API int GC_pthread_detach(pthread_t);
 #endif
 
 #ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-  GC_API int GC_pthread_cancel(pthread_t);
+# if !defined(__native_client__)
+    GC_API int GC_pthread_cancel(pthread_t);
+# endif
   GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
 #endif
 
@@ -77,7 +84,8 @@ GC_API int GC_pthread_detach(pthread_t);
 # define pthread_join GC_pthread_join
 # define pthread_detach GC_pthread_detach
 
-# if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_PTHREADS)
+# if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_PTHREADS) \
+     && !defined(__native_client__)
 #   ifndef GC_OPENBSD_THREADS
 #     undef pthread_sigmask
 #     define pthread_sigmask GC_pthread_sigmask
@@ -87,8 +95,10 @@ GC_API int GC_pthread_detach(pthread_t);
 # endif
 
 # ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-#   undef pthread_cancel
-#   define pthread_cancel GC_pthread_cancel
+#   if !defined(__native_client__)
+#     undef pthread_cancel
+#     define pthread_cancel GC_pthread_cancel
+#   endif
 #   undef pthread_exit
 #   define pthread_exit GC_pthread_exit
 # endif
index f39d0e21b3d812e33b48c4c2b1e44aec12c5acd0..ac169595b687cad2861d30b2ef8c7f5ca5a5fb1e 100644 (file)
@@ -2086,32 +2086,30 @@ GC_EXTERN signed_word GC_bytes_found;
               /* some other reason.                                     */
 #endif /* PARALLEL_MARK */
 
-#if defined(GC_PTHREADS)
+#if defined(GC_PTHREADS) && !defined(NACL) && !defined(SIG_SUSPEND)
   /* We define the thread suspension signal here, so that we can refer  */
   /* to it in the dirty bit implementation, if necessary.  Ideally we   */
   /* would allocate a (real-time?) signal using the standard mechanism. */
   /* unfortunately, there is no standard mechanism.  (There is one      */
   /* in Linux glibc, but it's not exported.)  Thus we continue to use   */
   /* the same hard-coded signals we've always used.                     */
-# if !defined(SIG_SUSPEND)
-#   if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
-#     if defined(SPARC) && !defined(SIGPWR)
-        /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>.    */
-        /* It is aliased to SIGLOST in asm/signal.h, though.            */
-#       define SIG_SUSPEND SIGLOST
-#     else
-        /* Linuxthreads itself uses SIGUSR1 and SIGUSR2.                */
-#       define SIG_SUSPEND SIGPWR
-#     endif
-#   elif !defined(GC_OPENBSD_THREADS) && !defined(GC_DARWIN_THREADS)
-#     if defined(_SIGRTMIN)
-#       define SIG_SUSPEND _SIGRTMIN + 6
-#     else
-#       define SIG_SUSPEND SIGRTMIN + 6
-#     endif
+# if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
+#   if defined(SPARC) && !defined(SIGPWR)
+      /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>.      */
+      /* It is aliased to SIGLOST in asm/signal.h, though.              */
+#     define SIG_SUSPEND SIGLOST
+#   else
+      /* Linuxthreads itself uses SIGUSR1 and SIGUSR2.                  */
+#     define SIG_SUSPEND SIGPWR
+#   endif
+# elif !defined(GC_OPENBSD_THREADS) && !defined(GC_DARWIN_THREADS)
+#   if defined(_SIGRTMIN)
+#     define SIG_SUSPEND _SIGRTMIN + 6
+#   else
+#     define SIG_SUSPEND SIGRTMIN + 6
 #   endif
-# endif /* !SIG_SUSPEND */
-#endif /* GC_PTHREADS */
+# endif
+#endif /* GC_PTHREADS && !SIG_SUSPEND */
 
 /* Some macros for setjmp that works across signal handlers     */
 /* were possible, and a couple of routines to facilitate        */
index 53feea1d63525987225e662789d4d26a0cab010b..02323a5d99a29378e869c6bde2065b86ca4fabd5 100644 (file)
@@ -39,7 +39,7 @@
 
 /* First a unified test for Linux: */
 # if (defined(linux) || defined(__linux__) || defined(PLATFORM_ANDROID)) \
-     && !defined(LINUX)
+     && !defined(LINUX) && !defined(__native_client__)
 #   define LINUX
 # endif
 
 # endif
 
 /* Determine the machine type: */
+# if defined(__native_client__)
+#    define NACL
+#    define I386
+#    define mach_type_known
+# endif
 # if defined(__arm__) || defined(__thumb__)
 #    define ARM32
 #    if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \
 #       endif
 #   endif /* DGUX */
 
+#   ifdef NACL
+#      define OS_TYPE "NACL"
+       extern int etext[];
+#      define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+       extern int _end[];
+#      define DATAEND (_end)
+#      undef STACK_GRAN
+#      define STACK_GRAN 0x10000
+#      define HEURISTIC1
+#      define GETPAGESIZE() 65536
+#      ifndef MAX_NACL_GC_THREADS
+#        define MAX_NACL_GC_THREADS 1024
+#      endif
+#   endif /* NACL */
+
 #   ifdef LINUX
 #       define OS_TYPE "LINUX"
 #       define LINUX_STACKBOTTOM
 
 # if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \
             || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \
-            || defined(DGUX) || defined(BSD) \
-            || defined(AIX) || defined(DARWIN) || defined(OSF1) \
-            || defined(HURD)
+            || defined(DGUX) || defined(BSD) || defined(HURD) \
+            || defined(AIX) || defined(DARWIN) || defined(OSF1)
 #   define UNIX_LIKE   /* Basic Unix-like system calls work.    */
 # endif
 
 # if defined(GC_IRIX_THREADS) && !defined(IRIX5)
         --> inconsistent configuration
 # endif
-# if defined(GC_LINUX_THREADS) && !defined(LINUX)
+# if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL)
         --> inconsistent configuration
 # endif
 # if defined(GC_NETBSD_THREADS) && !defined(NETBSD)
index 6690c58e4377171f4260d4e80c4c5a75d826cec7..c883c3c8076b730bad447d29b0c29546a6744fe5 100644 (file)
@@ -26,6 +26,17 @@ struct thread_stop_info {
 #   endif
 
     ptr_t stack_ptr;            /* Valid only when stopped.             */
+
+#   ifdef NACL
+      /* Grab NACL_GC_REG_STORAGE_SIZE pointers off the stack when      */
+      /* going into a syscall.  20 is more than we need, but it's an    */
+      /* overestimate in case the instrumented function uses any callee */
+      /* saved registers, they may be pushed to the stack much earlier. */
+      /* Also, on amd64 'push' puts 8 bytes on the stack even though    */
+      /* our pointers are 4 bytes.                                      */
+#     define NACL_GC_REG_STORAGE_SIZE 20
+      ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE];
+#   endif
 };
 
 #endif
index 5251f604b2b6e2e6d0621f57def8680ede96333d..73fa8feef41c0ca4ca509b716fddb59b5cf0b1e3 100644 (file)
@@ -123,6 +123,10 @@ GC_EXTERN GC_bool GC_in_thread_creation;
         /* Only set to TRUE while allocation lock is held.              */
         /* When set, it is OK to run GC from unknown thread.            */
 
+# ifdef NACL
+    GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self;
+# endif
+
 #endif /* GC_PTHREADS && !GC_WIN32_THREADS */
 
 #endif /* GC_PTHREAD_SUPPORT_H */
index e52661f0afcfbe7949720d622c0d4a825ec2e2f9..768ab451798f2727a688e6aaa3d1b064bfa17a2d 100644 (file)
--- a/os_dep.c
+++ b/os_dep.c
@@ -61,7 +61,7 @@
 # include <signal.h>
 #endif
 
-#if defined(UNIX_LIKE) || defined(CYGWIN32)
+#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL)
 # include <fcntl.h>
 #endif
 
@@ -1143,7 +1143,7 @@ GC_INNER word GC_page_size = 0;
   ptr_t GC_get_main_stack_base(void)
   {
     ptr_t result; /* also used as "dummy" to get the approx. sp value */
-#   if defined(LINUX) && defined(USE_GET_STACKBASE_FOR_MAIN)
+#   if defined(LINUX) && defined(USE_GET_STACKBASE_FOR_MAIN) && !defined(NACL)
       pthread_attr_t attr;
       void *stackaddr;
       size_t size;
@@ -1208,7 +1208,8 @@ GC_INNER word GC_page_size = 0;
   }
 #endif /* !AMIGA, !BEOS, !OPENBSD, !OS2, !Windows */
 
-#if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE)
+#if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE) \
+    && !defined(NACL)
 
 # include <pthread.h>
   /* extern int pthread_getattr_np(pthread_t, pthread_attr_t *); */
@@ -2432,10 +2433,24 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes)
 #   else
       /* It was already remapped with PROT_NONE. */
       int result;
-
       if (0 == start_addr) return;
-      result = mprotect(start_addr, len, (PROT_READ | PROT_WRITE)
-                                         | (pages_executable ? PROT_EXEC : 0));
+
+#     ifndef NACL
+        result = mprotect(start_addr, len, (PROT_READ | PROT_WRITE)
+                                        | (pages_executable ? PROT_EXEC : 0));
+#     else
+        {
+          /* NaCl does not expose mprotect, but mmap should work fine.  */
+          void *mmap_result = mmap(start_addr, len, (PROT_READ | PROT_WRITE)
+                                         | (pages_executable ? PROT_EXEC : 0),
+                                   MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+                                   zero_fd, 0 /* offset */);
+          if (mmap_result != (void *)start_addr)
+            ABORT("mmap as mprotect failed");
+          /* Fake the return value as if mprotect succeeded.    */
+          result = 0;
+        }
+#     endif /* NACL */
 #     undef IGNORE_PAGES_EXECUTABLE
 
       if (result != 0) {
index a6e1f72daee9fb2e9e3ae5d52f30d75962d8ce7c..cbe5a8576fbc4f86902d206b2b867cdb9d5755e5 100644 (file)
@@ -55,15 +55,19 @@ void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg)
   void * result;
   GC_thread me = GC_start_rtn_prepare_thread(&start, &start_arg, sb, arg);
 
-  pthread_cleanup_push(GC_thread_exit_proc, 0);
+# ifndef NACL
+    pthread_cleanup_push(GC_thread_exit_proc, 0);
+# endif
   result = (*start)(start_arg);
 # ifdef DEBUG_THREADS
     GC_printf("Finishing thread 0x%x\n", (unsigned)pthread_self());
 # endif
   me -> status = result;
-  pthread_cleanup_pop(1);
-  /* Cleanup acquires lock, ensuring that we can't exit while   */
-  /* a collection that thinks we're alive is trying to stop us. */
+# ifndef NACL
+    pthread_cleanup_pop(1);
+    /* Cleanup acquires lock, ensuring that we can't exit while         */
+    /* a collection that thinks we're alive is trying to stop us.       */
+# endif
   return result;
 }
 
index 64789803741d6e356bbae47e26cc1bd8989dfc7e..53bc3b62f3577a005f7aaf6c35dec9eca1ab0fbf 100644 (file)
 #if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && \
     !defined(GC_DARWIN_THREADS)
 
-#ifndef GC_OPENBSD_THREADS
+#ifdef NACL
+
+#include <unistd.h>
+#include <sys/time.h>
+
+STATIC int GC_nacl_num_gc_threads = 0;
+STATIC __thread int GC_nacl_thread_idx = -1;
+STATIC int GC_nacl_park_threads_now = 0;
+STATIC pthread_t GC_nacl_thread_parker = -1;
+
+GC_INNER __thread GC_thread GC_nacl_gc_thread_self = NULL;
+
+int GC_nacl_thread_parked[MAX_NACL_GC_THREADS];
+int GC_nacl_thread_used[MAX_NACL_GC_THREADS];
+
+#elif !defined(GC_OPENBSD_THREADS)
 
 #include <signal.h>
 #include <semaphore.h>
 #include "atomic_ops.h"
 
 #ifdef DEBUG_THREADS
+# ifndef NSIG
+#   if defined(MAXSIG)
+#     define NSIG (MAXSIG+1)
+#   elif defined(_NSIG)
+#     define NSIG _NSIG
+#   elif defined(__SIGRTMAX)
+#     define NSIG (__SIGRTMAX+1)
+#   else
+      --> please fix it
+#   endif
+# endif /* NSIG */
 
-#ifndef NSIG
-# if defined(MAXSIG)
-#  define NSIG (MAXSIG+1)
-# elif defined(_NSIG)
-#  define NSIG _NSIG
-# elif defined(__SIGRTMAX)
-#  define NSIG (__SIGRTMAX+1)
-# else
-  --> please fix it
-# endif
-#endif
-
-/* It's safe to call original pthread_sigmask() here. */
-#undef pthread_sigmask
+  /* It's safe to call original pthread_sigmask() here. */
+# undef pthread_sigmask
 
-void GC_print_sig_mask(void)
-{
+  void GC_print_sig_mask(void)
+  {
     sigset_t blocked;
     int i;
 
@@ -57,8 +71,7 @@ void GC_print_sig_mask(void)
         if (sigismember(&blocked, i)) { GC_printf("%d ", i); }
     }
     GC_printf("\n");
-}
-
+  }
 #endif /* DEBUG_THREADS */
 
 /* Remove the signals that we want to allow in thread stopping  */
@@ -137,7 +150,6 @@ STATIC sem_t GC_suspend_ack_sem;
 
 STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context);
 
-#if defined(IA64) || defined(HP_PA) || defined(M68K)
 #ifdef SA_SIGINFO
   /*ARGSUSED*/
   STATIC void GC_suspend_handler(int sig, siginfo_t *info, void *context)
@@ -145,138 +157,132 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context);
   STATIC void GC_suspend_handler(int sig)
 #endif
 {
-  int old_errno = errno;
-  GC_with_callee_saves_pushed(GC_suspend_handler_inner, (ptr_t)(word)sig);
-  errno = old_errno;
-}
-#else
-/* We believe that in all other cases the full context is already       */
-/* in the signal handler frame.                                         */
-#ifdef SA_SIGINFO
-  STATIC void GC_suspend_handler(int sig, siginfo_t *info, void *context)
-#else
-  STATIC void GC_suspend_handler(int sig)
-#endif
-{
-  int old_errno = errno;
-# ifndef SA_SIGINFO
-    void *context = 0;
+# if defined(IA64) || defined(HP_PA) || defined(M68K)
+    int old_errno = errno;
+    GC_with_callee_saves_pushed(GC_suspend_handler_inner, (ptr_t)(word)sig);
+    errno = old_errno;
+# else
+    /* We believe that in all other cases the full context is already   */
+    /* in the signal handler frame.                                     */
+    int old_errno = errno;
+#   ifndef SA_SIGINFO
+      void *context = 0;
+#   endif
+    GC_suspend_handler_inner((ptr_t)(word)sig, context);
+    errno = old_errno;
 # endif
-  GC_suspend_handler_inner((ptr_t)(word)sig, context);
-  errno = old_errno;
 }
-#endif
 
 /*ARGSUSED*/
 STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context)
 {
-    int sig = (int)(word)sig_arg;
-    int dummy;
-    pthread_t my_thread = pthread_self();
-    GC_thread me;
-    IF_CANCEL(int cancel_state;)
-
-    AO_t my_stop_count = AO_load(&GC_stop_count);
-
-    if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
-
-    DISABLE_CANCEL(cancel_state);
-        /* pthread_setcancelstate is not defined to be async-signal-safe. */
-        /* But the glibc version appears to be in the absence of          */
-        /* asynchronous cancellation.  And since this signal handler      */
-        /* to block on sigsuspend, which is both async-signal-safe        */
-        /* and a cancellation point, there seems to be no obvious way     */
-        /* out of it.  In fact, it looks to me like an async-signal-safe  */
-        /* cancellation point is inherently a problem, unless there is    */
-        /* some way to disable cancellation in the handler.               */
-#   ifdef DEBUG_THREADS
-      GC_printf("Suspending 0x%x\n", (unsigned)my_thread);
-#   endif
-
-    me = GC_lookup_thread(my_thread);
-    /* The lookup here is safe, since I'm doing this on behalf  */
-    /* of a thread which holds the allocation lock in order     */
-    /* to stop the world.  Thus concurrent modification of the  */
-    /* data structure is impossible.                            */
-    if (me -> stop_info.last_stop_count == my_stop_count) {
-        /* Duplicate signal.  OK if we are retrying.    */
-        if (!GC_retry_signals) {
-            WARN("Duplicate suspend signal in thread %p\n", pthread_self());
-        }
-        RESTORE_CANCEL(cancel_state);
-        return;
-    }
-#   ifdef SPARC
-        me -> stop_info.stack_ptr = GC_save_regs_in_stack();
-#   else
-        me -> stop_info.stack_ptr = (ptr_t)(&dummy);
-#   endif
-#   ifdef IA64
-        me -> backing_store_ptr = GC_save_regs_in_stack();
-#   endif
+  int sig = (int)(word)sig_arg;
+  int dummy;
+  pthread_t my_thread = pthread_self();
+  GC_thread me;
+  IF_CANCEL(int cancel_state;)
+
+  AO_t my_stop_count = AO_load(&GC_stop_count);
+
+  if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
+
+  DISABLE_CANCEL(cancel_state);
+      /* pthread_setcancelstate is not defined to be async-signal-safe. */
+      /* But the glibc version appears to be in the absence of          */
+      /* asynchronous cancellation.  And since this signal handler      */
+      /* to block on sigsuspend, which is both async-signal-safe        */
+      /* and a cancellation point, there seems to be no obvious way     */
+      /* out of it.  In fact, it looks to me like an async-signal-safe  */
+      /* cancellation point is inherently a problem, unless there is    */
+      /* some way to disable cancellation in the handler.               */
+# ifdef DEBUG_THREADS
+    GC_printf("Suspending 0x%x\n", (unsigned)my_thread);
+# endif
 
-    /* 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);
-    me -> stop_info.last_stop_count = my_stop_count;
-
-    /* Wait until that thread tells us to restart by sending    */
-    /* this thread a SIG_THR_RESTART signal.                    */
-    /* SIG_THR_RESTART should be masked at this point.  Thus there      */
-    /* is no race.                                              */
-    /* We do not continue until we receive a SIG_THR_RESTART,   */
-    /* but we do not take that as authoritative.  (We may be    */
-    /* accidentally restarted by one of the user signals we     */
-    /* don't block.)  After we receive the signal, we use a     */
-    /* primitive and expensive mechanism to wait until it's     */
-    /* really safe to proceed.  Under normal circumstances,     */
-    /* this code should not be executed.                        */
-    do {
-        sigsuspend (&suspend_handler_mask);
-    } while (AO_load_acquire(&GC_world_is_stopped)
-             && AO_load(&GC_stop_count) == my_stop_count);
-    /* If the RESTART signal gets lost, we can still lose.  That should be  */
-    /* less likely than losing the SUSPEND signal, since we don't do much   */
-    /* between the sem_post and sigsuspend.                                 */
-    /* We'd need more handshaking to work around that.                      */
-    /* Simply dropping the sigsuspend call should be safe, but is unlikely  */
-    /* to be efficient.                                                     */
+  me = GC_lookup_thread(my_thread);
+  /* The lookup here is safe, since I'm doing this on behalf    */
+  /* of a thread which holds the allocation lock in order       */
+  /* to stop the world.  Thus concurrent modification of the    */
+  /* data structure is impossible.                              */
+  if (me -> stop_info.last_stop_count == my_stop_count) {
+      /* Duplicate signal.  OK if we are retrying.      */
+      if (!GC_retry_signals) {
+          WARN("Duplicate suspend signal in thread %p\n", pthread_self());
+      }
+      RESTORE_CANCEL(cancel_state);
+      return;
+  }
+# ifdef SPARC
+      me -> stop_info.stack_ptr = GC_save_regs_in_stack();
+# else
+      me -> stop_info.stack_ptr = (ptr_t)(&dummy);
+# endif
+# ifdef IA64
+      me -> backing_store_ptr = GC_save_regs_in_stack();
+# endif
 
-#   ifdef DEBUG_THREADS
-      GC_printf("Continuing 0x%x\n", (unsigned)my_thread);
-#   endif
-    RESTORE_CANCEL(cancel_state);
+  /* 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);
+  me -> stop_info.last_stop_count = my_stop_count;
+
+  /* Wait until that thread tells us to restart by sending      */
+  /* this thread a SIG_THR_RESTART signal.                      */
+  /* SIG_THR_RESTART should be masked at this point.  Thus      */
+  /* there is no race.                                          */
+  /* We do not continue until we receive a SIG_THR_RESTART,     */
+  /* but we do not take that as authoritative.  (We may be      */
+  /* accidentally restarted by one of the user signals we       */
+  /* don't block.)  After we receive the signal, we use a       */
+  /* primitive and expensive mechanism to wait until it's       */
+  /* really safe to proceed.  Under normal circumstances,       */
+  /* this code should not be executed.                          */
+  do {
+      sigsuspend (&suspend_handler_mask);
+  } while (AO_load_acquire(&GC_world_is_stopped)
+           && AO_load(&GC_stop_count) == my_stop_count);
+  /* If the RESTART signal gets lost, we can still lose.  That should   */
+  /* be less likely than losing the SUSPEND signal, since we don't do   */
+  /* much between the sem_post and sigsuspend.                          */
+  /* We'd need more handshaking to work around that.                    */
+  /* Simply dropping the sigsuspend call should be safe, but is         */
+  /* unlikely to be efficient.                                          */
+
+# ifdef DEBUG_THREADS
+    GC_printf("Continuing 0x%x\n", (unsigned)my_thread);
+# endif
+  RESTORE_CANCEL(cancel_state);
 }
 
 STATIC void GC_restart_handler(int sig)
 {
-    if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
+  if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
 
-#   ifdef GC_NETBSD_THREADS_WORKAROUND
-      sem_post(&GC_restart_ack_sem);
-#   endif
+# ifdef GC_NETBSD_THREADS_WORKAROUND
+    sem_post(&GC_restart_ack_sem);
+# endif
 
-    /*
-    ** Note: even if we don't do anything useful here,
-    ** it would still be necessary to have a signal handler,
-    ** rather than ignoring the signals, otherwise
-    ** the signals will not be delivered at all, and
-    ** will thus not interrupt the sigsuspend() above.
-    */
+  /*
+  ** Note: even if we don't do anything useful here,
+  ** it would still be necessary to have a signal handler,
+  ** rather than ignoring the signals, otherwise
+  ** the signals will not be delivered at all, and
+  ** will thus not interrupt the sigsuspend() above.
+  */
 
-#   ifdef DEBUG_THREADS
-      GC_printf("In GC_restart_handler for 0x%x\n", (unsigned)pthread_self());
-#   endif
+# ifdef DEBUG_THREADS
+    GC_printf("In GC_restart_handler for 0x%x\n", (unsigned)pthread_self());
+# endif
 }
 
-#endif /* !GC_OPENBSD_THREADS */
+#endif /* !GC_OPENBSD_THREADS && !NACL */
 
-# ifdef IA64
-#   define IF_IA64(x) x
-# else
-#   define IF_IA64(x)
-# endif
+#ifdef IA64
+# define IF_IA64(x) x
+#else
+# define IF_IA64(x)
+#endif
 /* We hold allocation lock.  Should do exactly the right thing if the   */
 /* world is stopped.  Should not fail if it isn't.                      */
 GC_INNER void GC_push_all_stacks(void)
@@ -331,6 +337,12 @@ GC_INNER void GC_push_all_stacks(void)
 #       else
           total_size += hi - lo; /* lo <= hi */
 #       endif
+#       ifdef NACL
+          /* Push reg_storage as roots, this will cover the reg context. */
+          GC_push_all_stack((ptr_t)p -> stop_info.reg_storage,
+              (ptr_t)(p -> stop_info.reg_storage + NACL_GC_REG_STORAGE_SIZE));
+          total_size += NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t);
+#       endif
 #       ifdef IA64
 #         ifdef DEBUG_THREADS
             GC_printf("Reg stack for thread 0x%x = [%p,%p)\n",
@@ -353,9 +365,9 @@ GC_INNER void GC_push_all_stacks(void)
     GC_total_stacksize = total_size;
 }
 
-/* There seems to be a very rare thread stopping problem.  To help us  */
-/* debug that, we save the ids of the stopping thread. */
 #ifdef DEBUG_THREADS
+  /* There seems to be a very rare thread stopping problem.  To help us */
+  /* debug that, we save the ids of the stopping thread.                */
   pthread_t GC_stopping_thread;
   int GC_stopping_pid = 0;
 #endif
@@ -378,11 +390,13 @@ GC_INNER void GC_push_all_stacks(void)
 
 /* We hold the allocation lock.  Suspend all threads that might */
 /* still be running.  Return the number of suspend signals that */
-/* were sent. */
+/* were sent.                                                   */
 STATIC int GC_suspend_all(void)
 {
-    int n_live_threads = 0;
-    int i;
+  int n_live_threads = 0;
+  int i;
+
+# ifndef NACL
     GC_thread p;
 #   ifndef GC_OPENBSD_THREADS
       int result;
@@ -437,35 +451,78 @@ STATIC int GC_suspend_all(void)
         }
       }
     }
-    return n_live_threads;
-}
 
-GC_INNER void GC_stop_world(void)
-{
-    int i;
-#   ifndef GC_OPENBSD_THREADS
-      int n_live_threads;
-      int code;
+# else /* NACL */
+#   ifndef NACL_PARK_WAIT_NANOSECONDS
+#     define NACL_PARK_WAIT_NANOSECONDS (100 * 1000)
 #   endif
-
-    GC_ASSERT(I_HOLD_LOCK());
 #   ifdef DEBUG_THREADS
-      GC_printf("Stopping the world from 0x%x\n", (unsigned)pthread_self());
+      GC_printf("pthread_stop_world: num_threads %d\n",
+                 GC_nacl_num_gc_threads - 1);
+#   endif
+    GC_nacl_thread_parker = pthread_self();
+    GC_nacl_park_threads_now = 1;
+#   ifdef DEBUG_THREADS
+      GC_stopping_thread = GC_nacl_thread_parker;
+      GC_stopping_pid = getpid();
 #   endif
 
-    /* Make sure all free list construction has stopped before we start. */
-    /* No new construction can start, since free list construction is   */
-    /* required to acquire and release the GC lock before it starts,    */
-    /* and we have the lock.                                            */
-#   ifdef PARALLEL_MARK
-      if (GC_parallel) {
-        GC_acquire_mark_lock();
-        GC_ASSERT(GC_fl_builder_count == 0);
-        /* We should have previously waited for it to become zero. */
+    while (1) {
+      int num_threads_parked = 0;
+      struct timespec ts;
+      int num_used = 0;
+
+      /* Check the 'parked' flag for each thread the GC knows about.    */
+      for (i = 0; i < MAX_NACL_GC_THREADS
+                  && num_used < GC_nacl_num_gc_threads; i++) {
+        if (GC_nacl_thread_used[i] == 1) {
+          num_used++;
+          if (GC_nacl_thread_parked[i] == 1) {
+            num_threads_parked++;
+          }
+        }
       }
-#   endif /* PARALLEL_MARK */
+      /* -1 for the current thread.     */
+      if (num_threads_parked >= GC_nacl_num_gc_threads - 1)
+        break;
+      ts.tv_sec = 0;
+      ts.tv_nsec = NACL_PARK_WAIT_NANOSECONDS;
+#     ifdef DEBUG_THREADS
+        GC_printf("Sleep waiting for %d threads to park...\n",
+                   GC_nacl_num_gc_threads - num_threads_parked - 1);
+#     endif
+      /* This requires _POSIX_TIMERS feature.   */
+      nanosleep(&ts, 0);
+    }
+# endif /* NACL */
+  return n_live_threads;
+}
+
+GC_INNER void GC_stop_world(void)
+{
+# if !defined(GC_OPENBSD_THREADS) && !defined(NACL)
+    int i;
+    int n_live_threads;
+    int code;
+# endif
+  GC_ASSERT(I_HOLD_LOCK());
+# ifdef DEBUG_THREADS
+    GC_printf("Stopping the world from 0x%x\n", (unsigned)pthread_self());
+# endif
 
-# ifdef GC_OPENBSD_THREADS
+  /* Make sure all free list construction has stopped before we start.  */
+  /* No new construction can start, since free list construction is     */
+  /* required to acquire and release the GC lock before it starts,      */
+  /* and we have the lock.                                              */
+# ifdef PARALLEL_MARK
+    if (GC_parallel) {
+      GC_acquire_mark_lock();
+      GC_ASSERT(GC_fl_builder_count == 0);
+      /* We should have previously waited for it to become zero.        */
+    }
+# endif /* PARALLEL_MARK */
+
+# if defined(GC_OPENBSD_THREADS) || defined(NACL)
     (void)GC_suspend_all();
 # else
     AO_store(&GC_stop_count, GC_stop_count+1);
@@ -473,61 +530,188 @@ GC_INNER void GC_stop_world(void)
     AO_store_release(&GC_world_is_stopped, TRUE);
     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();
-
-                  if (GC_print_stats) {
-                      GC_log_printf("Resent %d signals after timeout\n",
-                                newly_sent);
-                  }
-                  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;
+    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();
+
+          if (GC_print_stats) {
+            GC_log_printf("Resent %d signals after timeout\n",
+                          newly_sent);
           }
+          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;
       }
+    }
+
     for (i = 0; i < n_live_threads; i++) {
-        retry:
-          if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
-              /* On Linux, sem_wait is documented to always return zero.*/
-              /* But the documentation appears to be incorrect.         */
-              if (errno == EINTR) {
-                /* Seems to happen with some versions of gdb.   */
-                goto retry;
-              }
-              ABORT("sem_wait for handler failed");
+      retry:
+        if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+          /* On Linux, sem_wait is documented to always return zero.    */
+          /* But the documentation appears to be incorrect.             */
+          if (errno == EINTR) {
+            /* Seems to happen with some versions of gdb.       */
+            goto retry;
           }
+          ABORT("sem_wait for handler failed");
+        }
     }
 # endif
 
-#   ifdef PARALLEL_MARK
-      if (GC_parallel)
-        GC_release_mark_lock();
-#   endif
-#   ifdef DEBUG_THREADS
-      GC_printf("World stopped from 0x%x\n", (unsigned)pthread_self());
-      GC_stopping_thread = 0;
-#   endif
+# ifdef PARALLEL_MARK
+    if (GC_parallel)
+      GC_release_mark_lock();
+# endif
+# ifdef DEBUG_THREADS
+    GC_printf("World stopped from 0x%x\n", (unsigned)pthread_self());
+    GC_stopping_thread = 0;
+# endif
 }
 
+#ifdef NACL
+# if defined(__x86_64__)
+#   define NACL_STORE_REGS() \
+        do { \
+          __asm__ __volatile__ ("push %rbx"); \
+          __asm__ __volatile__ ("push %rbp"); \
+          __asm__ __volatile__ ("push %r12"); \
+          __asm__ __volatile__ ("push %r13"); \
+          __asm__ __volatile__ ("push %r14"); \
+          __asm__ __volatile__ ("push %r15"); \
+          __asm__ __volatile__ ("mov %%esp, %0" \
+                    : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \
+          memcpy(GC_nacl_gc_thread_self->stop_info.reg_storage, \
+                 GC_nacl_gc_thread_self->stop_info.stack_ptr, \
+                 NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \
+          __asm__ __volatile__ ("naclasp $48, %r15"); \
+        } while (0)
+# elif defined(__i386__)
+#   define NACL_STORE_REGS() \
+        do { \
+          __asm__ __volatile__ ("push %ebx"); \
+          __asm__ __volatile__ ("push %ebp"); \
+          __asm__ __volatile__ ("push %esi"); \
+          __asm__ __volatile__ ("push %edi"); \
+          __asm__ __volatile__ ("mov %%esp, %0" \
+                    : "=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr)); \
+           memcpy(GC_nacl_gc_thread_self->stop_info.reg_storage, \
+                  GC_nacl_gc_thread_self->stop_info.stack_ptr, \
+                  NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\
+          __asm__ __volatile__ ("add $16, %esp"); \
+        } while (0)
+# else
+#   error FIXME for non-amd64/x86 NaCl
+# endif
+
+  GC_API_OSCALL void nacl_pre_syscall_hook(void)
+  {
+    int local_dummy = 0;
+    if (GC_nacl_thread_idx != -1) {
+      NACL_STORE_REGS();
+      GC_nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy);
+      GC_nacl_thread_parked[GC_nacl_thread_idx] = 1;
+    }
+  }
+
+  GC_API_OSCALL void __nacl_suspend_thread_if_needed(void)
+  {
+    if (GC_nacl_park_threads_now) {
+      pthread_t self = pthread_self();
+      int local_dummy = 0;
+
+      /* Don't try to park the thread parker.   */
+      if (GC_nacl_thread_parker == self)
+        return;
+
+      /* This can happen when a thread is created outside of the GC     */
+      /* system (wthread mostly).                                       */
+      if (GC_nacl_thread_idx < 0)
+        return;
+
+      /* If it was already 'parked', we're returning from a syscall,    */
+      /* so don't bother storing registers again, the GC has a set.     */
+      if (!GC_nacl_thread_parked[GC_nacl_thread_idx]) {
+        NACL_STORE_REGS();
+        GC_nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy);
+      }
+      GC_nacl_thread_parked[GC_nacl_thread_idx] = 1;
+      while (GC_nacl_park_threads_now) {
+        /* Just spin.   */
+      }
+      GC_nacl_thread_parked[GC_nacl_thread_idx] = 0;
+
+      /* Clear out the reg storage for next suspend.    */
+      memset(GC_nacl_gc_thread_self->stop_info.reg_storage, 0,
+             NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));
+    }
+  }
+
+  GC_API_OSCALL void nacl_post_syscall_hook(void)
+  {
+    /* Calling __nacl_suspend_thread_if_needed right away should        */
+    /* guarantee we don't mutate the GC set.                            */
+    __nacl_suspend_thread_if_needed();
+    if (GC_nacl_thread_idx != -1) {
+      GC_nacl_thread_parked[GC_nacl_thread_idx] = 0;
+    }
+  }
+
+  STATIC GC_bool GC_nacl_thread_parking_inited = FALSE;
+  STATIC pthread_mutex_t GC_nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER;
+
+  GC_INNER void GC_nacl_initialize_gc_thread(void)
+  {
+    int i;
+    pthread_mutex_lock(&GC_nacl_thread_alloc_lock);
+    if (!GC_nacl_thread_parking_inited) {
+      memset(GC_nacl_thread_parked, 0, sizeof(GC_nacl_thread_parked));
+      memset(GC_nacl_thread_used, 0, sizeof(GC_nacl_thread_used));
+      GC_nacl_thread_parking_inited = TRUE;
+    }
+    GC_ASSERT(GC_nacl_num_gc_threads <= MAX_NACL_GC_THREADS);
+    for (i = 0; i < MAX_NACL_GC_THREADS; i++) {
+      if (GC_nacl_thread_used[i] == 0) {
+        GC_nacl_thread_used[i] = 1;
+        GC_nacl_thread_idx = i;
+        GC_nacl_num_gc_threads++;
+        break;
+      }
+    }
+    pthread_mutex_unlock(&GC_nacl_thread_alloc_lock);
+  }
+
+  GC_INNER void GC_nacl_shutdown_gc_thread(void)
+  {
+    pthread_mutex_lock(&GC_nacl_thread_alloc_lock);
+    GC_ASSERT(GC_nacl_thread_idx >= 0);
+    GC_ASSERT(GC_nacl_thread_idx < MAX_NACL_GC_THREADS);
+    GC_ASSERT(GC_nacl_thread_used[GC_nacl_thread_idx] != 0);
+    GC_nacl_thread_used[GC_nacl_thread_idx] = 0;
+    GC_nacl_thread_idx = -1;
+    GC_nacl_num_gc_threads--;
+    pthread_mutex_unlock(&GC_nacl_thread_alloc_lock);
+  }
+#endif /* NACL */
+
 /* Caller holds allocation lock, and has held it continuously since     */
 /* the world stopped.                                                   */
 GC_INNER void GC_start_world(void)
 {
+# ifndef NACL
     pthread_t my_thread = pthread_self();
     register int i;
     register GC_thread p;
@@ -590,15 +774,21 @@ GC_INNER void GC_start_world(void)
                   GC_printf("sem_wait() returned %d\n", code);
                 ABORT("sem_wait() for restart handler failed");
             }
-#    endif
-#    ifdef DEBUG_THREADS
-       GC_printf("World started\n");
-#    endif
+#   endif
+#   ifdef DEBUG_THREADS
+      GC_printf("World started\n");
+#   endif
+# else /* NACL */
+#   ifdef DEBUG_THREADS
+      GC_printf("World starting...\n");
+#   endif
+    GC_nacl_park_threads_now = 0;
+# endif
 }
 
 GC_INNER void GC_stop_init(void)
 {
-# ifndef GC_OPENBSD_THREADS
+# if !defined(GC_OPENBSD_THREADS) && !defined(NACL)
     struct sigaction act;
 
     if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
@@ -637,22 +827,22 @@ GC_INNER void GC_stop_init(void)
     }
 
     /* Initialize suspend_handler_mask. It excludes SIG_THR_RESTART. */
-      if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset() failed");
-      GC_remove_allowed_signals(&suspend_handler_mask);
-      if (sigdelset(&suspend_handler_mask, SIG_THR_RESTART) != 0)
-          ABORT("sigdelset() failed");
+    if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset() failed");
+    GC_remove_allowed_signals(&suspend_handler_mask);
+    if (sigdelset(&suspend_handler_mask, SIG_THR_RESTART) != 0)
+        ABORT("sigdelset() failed");
 
     /* Check for GC_RETRY_SIGNALS.      */
-      if (0 != GETENV("GC_RETRY_SIGNALS")) {
-          GC_retry_signals = TRUE;
-      }
-      if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
-          GC_retry_signals = FALSE;
-      }
-      if (GC_print_stats && GC_retry_signals) {
-          GC_log_printf("Will retry suspend signal if necessary.\n");
-      }
-# endif /* !GC_OPENBSD_THREADS */
+    if (0 != GETENV("GC_RETRY_SIGNALS")) {
+        GC_retry_signals = TRUE;
+    }
+    if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
+        GC_retry_signals = FALSE;
+    }
+    if (GC_print_stats && GC_retry_signals) {
+        GC_log_printf("Will retry suspend signal if necessary.\n");
+    }
+# endif /* !GC_OPENBSD_THREADS && !NACL */
 }
 
 #endif
index 37e5d95fd2ceebb30eb379e3dcf00b1acb971156..1167330171fd09e707c08db8d52bce1bf22350be 100644 (file)
@@ -93,11 +93,14 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
 
 /* Undefine macros used to redirect pthread primitives. */
 # undef pthread_create
-# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
+# if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS) \
+     && !defined(NACL)
 #   undef pthread_sigmask
 # endif
 # ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-#   undef pthread_cancel
+#   ifndef NACL
+#     undef pthread_cancel
+#   endif
 #   undef pthread_exit
 # endif
 # undef pthread_join
@@ -109,7 +112,9 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
 #   define pthread_join __pthread_join
 #   define pthread_detach __pthread_detach
 #   ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-#     define pthread_cancel __pthread_cancel
+#     ifndef NACL
+#       define pthread_cancel __pthread_cancel
+#     endif
 #     define pthread_exit __pthread_exit
 #   endif
 # endif
@@ -117,15 +122,19 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
 #ifdef GC_USE_LD_WRAP
 #   define WRAP_FUNC(f) __wrap_##f
 #   define REAL_FUNC(f) __real_##f
-    int REAL_FUNC(pthread_create)(pthread_t *, const pthread_attr_t *,
+    int REAL_FUNC(pthread_create)(pthread_t *,
+                                  GC_PTHREAD_CONST pthread_attr_t *,
                                   void *(*start_routine)(void *), void *);
     int REAL_FUNC(pthread_join)(pthread_t, void **);
     int REAL_FUNC(pthread_detach)(pthread_t);
-#   if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
+#   if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS) \
+       && !defined(NACL)
       int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *);
 #   endif
 #   ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-      int REAL_FUNC(pthread_cancel)(pthread_t);
+#     ifndef NACL
+        int REAL_FUNC(pthread_cancel)(pthread_t);
+#     endif
       void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
 #   endif
 #else
@@ -137,7 +146,8 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
       /* In that way plain calls work, as do calls from files that      */
       /* included gc.h, wich redefined f to GC_f.                       */
       /* FIXME: Needs work for DARWIN and True64 (OSF1) */
-      typedef int (* GC_pthread_create_t)(pthread_t *, const pthread_attr_t *,
+      typedef int (* GC_pthread_create_t)(pthread_t *,
+                                          GC_PTHREAD_CONST pthread_attr_t *,
                                           void * (*)(void *), void *);
       static GC_pthread_create_t REAL_FUNC(pthread_create);
       typedef int (* GC_pthread_sigmask_t)(int, const sigset_t *, sigset_t *);
@@ -147,8 +157,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
       typedef int (* GC_pthread_detach_t)(pthread_t);
       static GC_pthread_detach_t REAL_FUNC(pthread_detach);
 #     ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-        typedef int (* GC_pthread_cancel_t)(pthread_t);
-        static GC_pthread_cancel_t REAL_FUNC(pthread_cancel);
+#       ifndef NACL
+          typedef int (* GC_pthread_cancel_t)(pthread_t);
+          static GC_pthread_cancel_t REAL_FUNC(pthread_cancel);
+#       endif
         typedef void (* GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
         static GC_pthread_exit_t REAL_FUNC(pthread_exit);
 #     endif
@@ -163,10 +175,11 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
 #endif
 
 #if defined(GC_USE_LD_WRAP) || defined(GC_USE_DLOPEN_WRAP)
-/* Define GC_ functions as aliases for the plain ones, which will       */
-/* be intercepted.  This allows files which include gc.h, and hence     */
-/* generate references to the GC_ symbols, to see the right symbols.    */
-      GC_API int GC_pthread_create(pthread_t * t, const pthread_attr_t * a,
+  /* Define GC_ functions as aliases for the plain ones, which will     */
+  /* be intercepted.  This allows files which include gc.h, and hence   */
+  /* generate references to the GC_ symbols, to see the right symbols.  */
+      GC_API int GC_pthread_create(pthread_t * t,
+                                   GC_PTHREAD_CONST pthread_attr_t * a,
                                    void * (* fn)(void *), void * arg)
       {
           return pthread_create(t, a, fn, arg);
@@ -189,10 +202,12 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
       }
 
 #     ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-        GC_API int GC_pthread_cancel(pthread_t t)
-        {
-          return pthread_cancel(t);
-        }
+#       ifndef NACL
+          GC_API int GC_pthread_cancel(pthread_t t)
+          {
+            return pthread_cancel(t);
+          }
+#       endif /* !NACL */
 
         GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void *retval)
         {
@@ -243,8 +258,10 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
     REAL_FUNC(pthread_detach) = (GC_pthread_detach_t)
                                 dlsym(dl_handle, "pthread_detach");
 #   ifdef GC_PTHREAD_EXIT_ATTRIBUTE
-      REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)
+#     ifndef NACL
+        REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)
                                     dlsym(dl_handle, "pthread_cancel");
+#     endif
       REAL_FUNC(pthread_exit) = (GC_pthread_exit_t)
                                   dlsym(dl_handle, "pthread_exit");
 #   endif
@@ -442,6 +459,11 @@ void GC_push_thread_structures(void)
 /* It may not be safe to allocate when we register the first thread.    */
 static struct GC_Thread_Rep first_thread;
 
+#ifdef NACL
+  GC_INNER void GC_nacl_initialize_gc_thread(void);
+  GC_INNER void GC_nacl_shutdown_gc_thread(void);
+#endif
+
 /* Add a thread to GC_threads.  We assume it wasn't already there.      */
 /* Caller holds allocation lock.                                        */
 STATIC GC_thread GC_new_thread(pthread_t id)
@@ -465,6 +487,10 @@ STATIC GC_thread GC_new_thread(pthread_t id)
 #   endif
     result -> next = GC_threads[hv];
     GC_threads[hv] = result;
+#   ifdef NACL
+      GC_nacl_gc_thread_self = result;
+      GC_nacl_initialize_gc_thread();
+#   endif
     GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
     return(result);
 }
@@ -478,6 +504,11 @@ STATIC void GC_delete_thread(pthread_t id)
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
 
+#   ifdef NACL
+      GC_nacl_shutdown_gc_thread();
+      GC_nacl_gc_thread_self = NULL;
+#   endif
+
     GC_ASSERT(I_HOLD_LOCK());
     while (!THREAD_EQUAL(p -> id, id)) {
         prev = p;
@@ -672,44 +703,44 @@ STATIC void GC_remove_all_threads_but_me(void)
   }
 #endif /* IA64 */
 
-#ifdef GC_LINUX_THREADS
+#if defined(GC_LINUX_THREADS) && !defined(NACL)
   /* Return the number of processors, or i<= 0 if it can't be determined. */
   STATIC int GC_get_nprocs(void)
   {
-    /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that       */
-    /* appears to be buggy in many cases.                               */
-    /* We look for lines "cpu<n>" in /proc/stat.                        */
+    /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that     */
+    /* appears to be buggy in many cases.                             */
+    /* We look for lines "cpu<n>" in /proc/stat.                      */
 #   ifndef STAT_READ
       /* Also defined in os_dep.c. */
 #     define STAT_BUF_SIZE 4096
 #     define STAT_READ read
 #   endif
-        /* If read is wrapped, this may need to be redefined to call    */
-        /* the real one.                                                */
+    /* If read is wrapped, this may need to be redefined to call      */
+    /* the real one.                                                  */
     char stat_buf[STAT_BUF_SIZE];
     int f;
     word result = 1;
-        /* Some old kernels only have a single "cpu nnnn ..."   */
-        /* entry in /proc/stat.  We identify those as           */
-        /* uniprocessors.                                       */
+    /* Some old kernels only have a single "cpu nnnn ..."     */
+    /* entry in /proc/stat.  We identify those as             */
+    /* uniprocessors.                                         */
     size_t i, len = 0;
 
     f = open("/proc/stat", O_RDONLY);
     if (f < 0 || (len = STAT_READ(f, stat_buf, STAT_BUF_SIZE)) < 100) {
-        WARN("Couldn't read /proc/stat\n", 0);
-        return -1;
+      WARN("Couldn't read /proc/stat\n", 0);
+      return -1;
     }
     for (i = 0; i < len - 100; ++i) {
-        if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c'
-            && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') {
-            int cpu_no = atoi(stat_buf + i + 4);
-            if (cpu_no >= result) result = cpu_no + 1;
-        }
+      if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c'
+          && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') {
+        int cpu_no = atoi(stat_buf + i + 4);
+        if (cpu_no >= result) result = cpu_no + 1;
+      }
     }
     close(f);
     return result;
   }
-#endif /* GC_LINUX_THREADS */
+#endif /* GC_LINUX_THREADS && !NACL */
 
 /* We hold the GC lock.  Wait until an in-progress GC has finished.     */
 /* Repeatedly RELEASES GC LOCK in order to wait.                        */
@@ -906,21 +937,18 @@ GC_INNER void GC_thr_init(void)
   if (GC_nprocs <= 0) {
 #   if defined(GC_HPUX_THREADS)
       GC_nprocs = pthread_num_processors_np();
-#   endif
-#   if defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
-       || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS)
+#   elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
+         || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS) \
+         || defined(NACL)
       GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
       if (GC_nprocs <= 0) GC_nprocs = 1;
-#   endif
-#   if defined(GC_IRIX_THREADS)
+#   elif defined(GC_IRIX_THREADS)
       GC_nprocs = sysconf(_SC_NPROC_ONLN);
       if (GC_nprocs <= 0) GC_nprocs = 1;
-#   endif
-#   if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \
-       || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
+#   elif defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \
+         || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
       GC_nprocs = get_ncpu();
-#   endif
-#   if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
+#   elif defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
       GC_nprocs = GC_get_nprocs();
 #   endif
   }
@@ -993,7 +1021,8 @@ GC_INNER void GC_init_parallel(void)
 #   endif
 }
 
-#if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
+#if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS) \
+    && !defined(NACL)
   GC_API int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set,
                                         sigset_t *oset)
   {
@@ -1007,7 +1036,7 @@ GC_INNER void GC_init_parallel(void)
     }
     return(REAL_FUNC(pthread_sigmask)(how, set, oset));
   }
-#endif /* !GC_DARWIN_THREADS && !GC_OPENBSD_THREADS */
+#endif /* !GC_DARWIN_THREADS && !GC_OPENBSD_THREADS && !NACL */
 
 #if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK)
   GC_INNER ptr_t GC_FindTopOfStack(unsigned long);
@@ -1237,28 +1266,31 @@ GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread)
   /* really have an option in that the process is not in a fully        */
   /* functional state while a thread is exiting.                        */
 
-  GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread)
-  {
-#   ifdef CANCEL_SAFE
-      GC_thread thread_gc_id;
-      DCL_LOCK_STATE;
-#   endif
+# ifndef NACL
+    GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread)
+    {
+#     ifdef CANCEL_SAFE
+        GC_thread thread_gc_id;
+        DCL_LOCK_STATE;
+#     endif
 
-    INIT_REAL_SYMS();
-#   ifdef CANCEL_SAFE
-      LOCK();
-      thread_gc_id = GC_lookup_thread(thread);
-      /* We test DISABLED_GC because pthread_exit could be called at    */
-      /* the same time.  (If thread_gc_id is NULL then pthread_cancel   */
-      /* should return ESRCH.)                                          */
-      if (thread_gc_id != 0 && (thread_gc_id -> flags & DISABLED_GC) == 0) {
-        thread_gc_id -> flags |= DISABLED_GC;
-        GC_dont_gc++;
-      }
-      UNLOCK();
-#   endif
-    return REAL_FUNC(pthread_cancel)(thread);
-  }
+      INIT_REAL_SYMS();
+#     ifdef CANCEL_SAFE
+        LOCK();
+        thread_gc_id = GC_lookup_thread(thread);
+        /* We test DISABLED_GC because pthread_exit could be called at  */
+        /* the same time.  (If thread_gc_id is NULL then pthread_cancel */
+        /* should return ESRCH.)                                        */
+        if (thread_gc_id != 0
+            && (thread_gc_id -> flags & DISABLED_GC) == 0) {
+          thread_gc_id -> flags |= DISABLED_GC;
+          GC_dont_gc++;
+        }
+        UNLOCK();
+#     endif
+      return REAL_FUNC(pthread_cancel)(thread);
+    }
+# endif /* !NACL */
 
   GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void *retval)
   {
@@ -1276,6 +1308,12 @@ GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread)
     }
     UNLOCK();
 
+#   ifdef NACL
+      /* Native Client doesn't support pthread cleanup functions, */
+      /* so cleanup the thread here.                              */
+      GC_thread_exit_proc(0);
+#   endif
+
     REAL_FUNC(pthread_exit)(retval);
   }
 #endif /* GC_PTHREAD_EXIT_ATTRIBUTE */
@@ -1411,8 +1449,8 @@ STATIC void * GC_start_routine(void * arg)
 }
 
 GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread,
-                  const pthread_attr_t *attr,
-                  void *(*start_routine)(void *), void *arg)
+                     GC_PTHREAD_CONST pthread_attr_t *attr,
+                     void *(*start_routine)(void *), void *arg)
 {
     int result;
     int detachstate;
index c7dd600a6d89e50812ed50070547125d88ac1d8f..7872f4a520750865642d078ccc069c283dc4c00b 100644 (file)
@@ -2387,7 +2387,7 @@ GC_INNER void GC_thr_init(void)
   /* Cygwin-pthreads calls CreateThread internally, but it's not easily */
   /* interceptible by us..., so intercept pthread_create instead.       */
   GC_API int GC_pthread_create(pthread_t *new_thread,
-                               const pthread_attr_t *attr,
+                               GC_PTHREAD_CONST pthread_attr_t *attr,
                                void *(*start_routine)(void *), void *arg)
   {
     if (!parallel_initialized) GC_init_parallel();