diff options
-rw-r--r-- | ChangeLog | 64 | ||||
-rw-r--r-- | gc_dlopen.c | 2 | ||||
-rw-r--r-- | include/gc.h | 4 | ||||
-rw-r--r-- | include/gc_pthread_redirects.h | 22 | ||||
-rw-r--r-- | include/private/gc_priv.h | 36 | ||||
-rw-r--r-- | include/private/gcconfig.h | 29 | ||||
-rw-r--r-- | include/private/pthread_stop_world.h | 11 | ||||
-rw-r--r-- | include/private/pthread_support.h | 4 | ||||
-rw-r--r-- | os_dep.c | 27 | ||||
-rw-r--r-- | pthread_start.c | 12 | ||||
-rw-r--r-- | pthread_stop_world.c | 634 | ||||
-rw-r--r-- | pthread_support.c | 178 | ||||
-rw-r--r-- | win32_threads.c | 2 |
13 files changed, 690 insertions, 335 deletions
@@ -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, diff --git a/gc_dlopen.c b/gc_dlopen.c index 6d886fd6..39074f76 100644 --- a/gc_dlopen.c +++ b/gc_dlopen.c @@ -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) diff --git a/include/gc.h b/include/gc.h index d90ba5c5..d891cf1d 100644 --- a/include/gc.h +++ b/include/gc.h @@ -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) diff --git a/include/gc_pthread_redirects.h b/include/gc_pthread_redirects.h index dfb014c5..8b2c47d0 100644 --- a/include/gc_pthread_redirects.h +++ b/include/gc_pthread_redirects.h @@ -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> @@ -41,9 +42,13 @@ 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 diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index f39d0e21..ac169595 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -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 */ diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index 53feea1d..02323a5d 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -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 @@ -65,6 +65,11 @@ # 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) \ @@ -1176,6 +1181,21 @@ # 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 @@ -2310,9 +2330,8 @@ # 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 @@ -2431,7 +2450,7 @@ # 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) diff --git a/include/private/pthread_stop_world.h b/include/private/pthread_stop_world.h index 6690c58e..c883c3c8 100644 --- a/include/private/pthread_stop_world.h +++ b/include/private/pthread_stop_world.h @@ -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 diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h index 5251f604..73fa8fee 100644 --- a/include/private/pthread_support.h +++ b/include/private/pthread_support.h @@ -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 */ @@ -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) { diff --git a/pthread_start.c b/pthread_start.c index a6e1f72d..cbe5a857 100644 --- a/pthread_start.c +++ b/pthread_start.c @@ -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; } diff --git a/pthread_stop_world.c b/pthread_stop_world.c index 64789803..53bc3b62 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -20,7 +20,22 @@ #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> @@ -29,24 +44,23 @@ #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 diff --git a/pthread_support.c b/pthread_support.c index 37e5d95f..11673301 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -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; diff --git a/win32_threads.c b/win32_threads.c index c7dd600a..7872f4a5 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -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(); |