Commit aa7a966b by Bryce McKinlay

This commit was generated by cvs2svn to compensate for changes in r85899,

which included commits to RCS files with non-trunk default branches.

From-SVN: r85900
parent 5c4dc108
......@@ -422,12 +422,7 @@ void GC_thr_init()
struct sigaction act;
if (GC_thr_initialized) return;
#if 0
/* unfortunately, GC_init_inner calls us without the lock, so
* this assertion is not always true. */
/* Why doesn't GC_init_inner hold the lock? - HB */
GC_ASSERT(I_HOLD_LOCK());
#endif
GC_thr_initialized = TRUE;
#ifndef GC_AIX_THREADS
(void) sigaction(SIG_SUSPEND, 0, &act);
......
Darwin/MacOSX Support - July 22, 2003
====================================
Darwin/MacOSX Support - December 16, 2003
=========================================
Important Usage Notes
=====================
......@@ -15,7 +15,7 @@ run and perhaps called GC_malloc(), create an initialization routine
for each library to call GC_init():
#include <gc/gc.h>
void my_library_init() { GC_init(); }
extern "C" void my_library_init() { GC_init(); }
Compile this code into a my_library_init.o, and link it into your
dylib. When you link the dylib, pass the -init argument with
......@@ -34,6 +34,12 @@ work reliably with workarounds for a few possible bugs in place however
these workaround may not work correctly in all cases. There may also
be additional problems that I have not found.
Thread-local GC allocation will not work with threads that are not
created using the GC-provided override of pthread_create(). Threads
created without the GC-provided pthread_create() do not have the
necessary data structures in the GC to store this data.
Implementation Information
==========================
Darwin/MacOSX support is nearly complete. Thread support is reliable on
......@@ -42,11 +48,27 @@ Darwin versions (MacOSX 10.1). Shared library support had also been
added and the gc can be run from a shared library. There is currently only
support for Darwin/PPC although adding x86 support should be trivial.
Thread support is implemented in terms of mach thread_suspend and
Thread support is implemented in terms of mach thread_suspend and
thread_resume calls. These provide a very clean interface to thread
suspension. This implementation doesn't rely on pthread_kill so the
code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop the
world is located in darwin_stop_world.c.
code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop and
start the world is located in darwin_stop_world.c.
Since not all uses of the GC enable clients to override pthread_create()
before threads have been created, the code for stopping the world has
been rewritten to look for threads using Mach kernel calls. Each
thread identified in this way is suspended and resumed as above. In
addition, since Mach kernel threads do not contain pointers to their
stacks, a stack-walking function has been written to find the stack
limits. Given an initial stack pointer (for the current thread, a
pointer to a stack-allocated local variable will do; for a non-active
thread, we grab the value of register 1 (on PowerPC)), it
will walk the PPC Mach-O-ABI compliant stack chain until it reaches the
top of the stack. This appears to work correctly for GCC-compiled C,
C++, Objective-C, and Objective-C++ code, as well as for Java
programs that use JNI. If you run code that does not follow the stack
layout or stack pointer conventions laid out in the PPC Mach-O ABI,
then this will likely crash the garbage collector.
The original incremental collector support unfortunatelly no longer works
on recent Darwin versions. It also relied on some undocumented kernel
......
......@@ -35,7 +35,17 @@
* library, which itself was derived from the SGI STL implementation.
*/
#include "gc.h" // For size_t
#ifndef GC_ALLOCATOR_H
#define GC_ALLOCATOR_H
#include "gc.h"
#if defined(__GNUC__)
# define GC_ATTR_UNUSED __attribute__((unused))
#else
# define GC_ATTR_UNUSED
#endif
/* First some helpers to allow us to dispatch on whether or not a type
* is known to be pointerfree.
......@@ -118,7 +128,7 @@ public:
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_n)
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
{ GC_FREE(__p); }
size_type max_size() const throw()
......@@ -194,7 +204,7 @@ public:
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type GC_n)
void deallocate(pointer __p, size_type GC_ATTR_UNUSED GC_n)
{ GC_FREE(__p); }
size_type max_size() const throw()
......@@ -230,3 +240,4 @@ inline bool operator!=(const traceable_allocator<GC_T1>&, const traceable_alloca
return false;
}
#endif /* GC_ALLOCATOR_H */
......@@ -97,7 +97,10 @@
# endif
#endif /* GC_THREADS */
#if defined(GC_THREADS) && !defined(GC_PTHREADS) && defined(MSWIN32)
#if defined(GC_THREADS) && !defined(GC_PTHREADS) && \
(defined(_WIN32) || defined(_MSC_VER) || defined(__CYGWIN__) \
|| defined(__MINGW32__) || defined(__BORLANDC__) \
|| defined(_WIN32_WCE))
# define GC_WIN32_THREADS
#endif
......@@ -106,8 +109,9 @@
#endif
# define __GC
# include <stddef.h>
# ifdef _WIN32_WCE
# ifndef _WIN32_WCE
# include <stddef.h>
# else /* ! _WIN32_WCE */
/* Yet more kluges for WinCE */
# include <stdlib.h> /* size_t is defined here */
typedef long ptrdiff_t; /* ptrdiff_t is not defined */
......
......@@ -12,4 +12,11 @@ struct thread_stop_info {
mach_port_t mach_thread;
};
struct GC_mach_thread {
thread_act_t thread;
int already_suspended;
};
void GC_darwin_register_mach_handler_thread(mach_port_t thread);
#endif
......@@ -93,5 +93,10 @@ GC_thread GC_lookup_thread(pthread_t id);
void GC_stop_init();
extern GC_bool GC_in_thread_creation;
/* We may currently be in thread creation or destruction. */
/* Only set to TRUE while allocation lock is held. */
/* When set, it is OK to run GC from unknown thread. */
#endif /* GC_PTHREADS && !GC_SOLARIS_THREADS.... etc */
#endif /* GC_PTHREAD_SUPPORT_H */
......@@ -39,6 +39,34 @@ void GC_print_sig_mask()
#endif
/* Remove the signals that we want to allow in thread stopping */
/* handler from a set. */
void GC_remove_allowed_signals(sigset_t *set)
{
# ifdef NO_SIGNALS
if (sigdelset(set, SIGINT) != 0
|| sigdelset(set, SIGQUIT) != 0
|| sigdelset(set, SIGABRT) != 0
|| sigdelset(set, SIGTERM) != 0) {
ABORT("sigdelset() failed");
}
# endif
# ifdef MPROTECT_VDB
/* Handlers write to the thread structure, which is in the heap, */
/* and hence can trigger a protection fault. */
if (sigdelset(set, SIGSEGV) != 0
# ifdef SIGBUS
|| sigdelset(set, SIGBUS) != 0
# endif
) {
ABORT("sigdelset() failed");
}
# endif
}
static sigset_t suspend_handler_mask;
word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
#ifdef GC_OSF1_THREADS
......@@ -78,7 +106,6 @@ void GC_suspend_handler(int sig)
int dummy;
pthread_t my_thread = pthread_self();
GC_thread me;
sigset_t mask;
# ifdef PARALLEL_MARK
word my_mark_no = GC_mark_no;
/* Marker can't proceed until we acknowledge. Thus this is */
......@@ -125,17 +152,9 @@ void GC_suspend_handler(int sig)
/* this thread a SIG_THR_RESTART signal. */
/* SIG_THR_RESTART should be masked at this point. Thus there */
/* is no race. */
if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
if (sigdelset(&mask, SIG_THR_RESTART) != 0) ABORT("sigdelset() failed");
# ifdef NO_SIGNALS
if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed");
if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed");
if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed");
if (sigdelset(&mask, SIGABRT) != 0) ABORT("sigdelset() failed");
# endif
do {
me->stop_info.signal = 0;
sigsuspend(&mask); /* Wait for signal */
sigsuspend(&suspend_handler_mask); /* Wait for signal */
} while (me->stop_info.signal != SIG_THR_RESTART);
/* 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 */
......@@ -186,6 +205,7 @@ void GC_restart_handler(int sig)
/* world is stopped. Should not fail if it isn't. */
void GC_push_all_stacks()
{
GC_bool found_me = FALSE;
int i;
GC_thread p;
ptr_t lo, hi;
......@@ -206,6 +226,7 @@ void GC_push_all_stacks()
# else
lo = GC_approx_sp();
# endif
found_me = TRUE;
IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
} else {
lo = p -> stop_info.stack_ptr;
......@@ -232,6 +253,11 @@ void GC_push_all_stacks()
GC_push_all_stack(lo, hi);
# endif
# ifdef IA64
# if DEBUG_THREADS
GC_printf3("Reg stack for thread 0x%lx = [%lx,%lx)\n",
(unsigned long) p -> id,
(unsigned long) bs_lo, (unsigned long) bs_hi);
# endif
if (pthread_equal(p -> id, me)) {
GC_push_all_eager(bs_lo, bs_hi);
} else {
......@@ -240,6 +266,8 @@ void GC_push_all_stacks()
# endif
}
}
if (!found_me && !GC_in_thread_creation)
ABORT("Collecting from unknown thread.");
}
/* There seems to be a very rare thread stopping problem. To help us */
......@@ -408,16 +436,9 @@ void GC_stop_init() {
if (sigfillset(&act.sa_mask) != 0) {
ABORT("sigfillset() failed");
}
# ifdef NO_SIGNALS
if (sigdelset(&act.sa_mask, SIGINT) != 0
|| sigdelset(&act.sa_mask, SIGQUIT != 0)
|| sigdelset(&act.sa_mask, SIGABRT != 0)
|| sigdelset(&act.sa_mask, SIGTERM != 0)) {
ABORT("sigdelset() failed");
}
# endif
/* SIG_THR_RESTART is unmasked by the handler when necessary. */
GC_remove_allowed_signals(&act.sa_mask);
/* SIG_THR_RESTART is set in the resulting mask. */
/* It is unmasked by the handler when necessary. */
act.sa_handler = GC_suspend_handler;
if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
ABORT("Cannot set SIG_SUSPEND handler");
......@@ -428,6 +449,12 @@ void GC_stop_init() {
ABORT("Cannot set SIG_THR_RESTART handler");
}
/* Inititialize 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");
/* Check for GC_RETRY_SIGNALS. */
if (0 != GETENV("GC_RETRY_SIGNALS")) {
GC_retry_signals = TRUE;
......
......@@ -54,8 +54,17 @@
&& !defined(GC_AIX_THREADS)
# if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
&& !defined(USE_HPUX_TLS)
# define USE_HPUX_TLS
&& !defined(USE_COMPILER_TLS)
# ifdef __GNUC__
# define USE_PTHREAD_SPECIFIC
/* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */
# else
# define USE_COMPILER_TLS
# endif
# endif
# if defined USE_HPUX_TLS
--> Macro replaced by USE_COMPILER_TLS
# endif
# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
......@@ -72,7 +81,7 @@
# endif
# ifdef THREAD_LOCAL_ALLOC
# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS)
# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_COMPILER_TLS)
# include "private/specific.h"
# endif
# if defined(USE_PTHREAD_SPECIFIC)
......@@ -81,7 +90,7 @@
# define GC_key_create pthread_key_create
typedef pthread_key_t GC_key_t;
# endif
# if defined(USE_HPUX_TLS)
# if defined(USE_COMPILER_TLS)
# define GC_getspecific(x) (x)
# define GC_setspecific(key, v) ((key) = (v), 0)
# define GC_key_create(key, d) 0
......@@ -99,6 +108,7 @@
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <signal.h>
#if defined(GC_DARWIN_THREADS)
# include "private/darwin_semaphore.h"
......@@ -158,7 +168,7 @@ void GC_init_parallel();
/* We don't really support thread-local allocation with DBG_HDRS_ALL */
#ifdef USE_HPUX_TLS
#ifdef USE_COMPILER_TLS
__thread
#endif
GC_key_t GC_thread_key;
......@@ -500,19 +510,6 @@ static __inline__ void start_mark_threads()
#endif /* !PARALLEL_MARK */
/* Defining INSTALL_LOOPING_SEGV_HANDLER causes SIGSEGV and SIGBUS to */
/* result in an infinite loop in a signal handler. This can be very */
/* useful for debugging, since (as of RH7) gdb still seems to have */
/* serious problems with threads. */
#ifdef INSTALL_LOOPING_SEGV_HANDLER
void GC_looping_handler(int sig)
{
GC_printf3("Signal %ld in thread %lx, pid %ld\n",
sig, pthread_self(), getpid());
for (;;);
}
#endif
GC_bool GC_thr_initialized = FALSE;
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
......@@ -622,7 +619,7 @@ void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
GC_INTERNAL_FREE(p);
}
/* Return a GC_thread corresponding to a given thread_t. */
/* Return a GC_thread corresponding to a given pthread_t. */
/* Returns 0 if it's not there. */
/* Caller holds allocation lock or otherwise inhibits */
/* updates. */
......@@ -747,7 +744,9 @@ void GC_wait_for_gc_completion(GC_bool wait_for_all)
while (GC_incremental && GC_collection_in_progress()
&& (wait_for_all || old_gc_no == GC_gc_no)) {
ENTER_GC();
GC_in_thread_creation = TRUE;
GC_collect_a_little_inner(1);
GC_in_thread_creation = FALSE;
EXIT_GC();
UNLOCK();
sched_yield();
......@@ -1055,9 +1054,10 @@ void GC_thread_exit_proc(void *arg)
me -> flags |= FINISHED;
}
# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \
&& !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL)
&& !defined(USE_COMPILER_TLS) && !defined(DBG_HDRS_ALL)
GC_remove_specific(GC_thread_key);
# endif
/* The following may run the GC from "nonexistent" thread. */
GC_wait_for_gc_completion(FALSE);
UNLOCK();
}
......@@ -1115,6 +1115,8 @@ WRAP_FUNC(pthread_detach)(pthread_t thread)
return result;
}
GC_bool GC_in_thread_creation = FALSE;
void * GC_start_routine(void * arg)
{
int dummy;
......@@ -1132,7 +1134,9 @@ void * GC_start_routine(void * arg)
GC_printf1("sp = 0x%lx\n", (long) &arg);
# endif
LOCK();
GC_in_thread_creation = TRUE;
me = GC_new_thread(my_pthread);
GC_in_thread_creation = FALSE;
#ifdef GC_DARWIN_THREADS
me -> stop_info.mach_thread = mach_thread_self();
#else
......@@ -1301,12 +1305,12 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread,
void GC_pause()
{
int i;
# ifndef __GNUC__
volatile word dummy = 0;
# endif
# if !defined(__GNUC__) || defined(__INTEL_COMPILER)
volatile word dummy = 0;
# endif
for (i = 0; i < 10; ++i) {
# ifdef __GNUC__
# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
__asm__ __volatile__ (" " : : : "memory");
# else
/* Something that's unlikely to be optimized away. */
......@@ -1315,7 +1319,7 @@ void GC_pause()
}
}
#define SPIN_MAX 1024 /* Maximum number of calls to GC_pause before */
#define SPIN_MAX 128 /* Maximum number of calls to GC_pause before */
/* give up. */
VOLATILE GC_bool GC_collecting = 0;
......@@ -1340,19 +1344,34 @@ VOLATILE GC_bool GC_collecting = 0;
/* yield by calling pthread_mutex_lock(); it never makes sense to */
/* explicitly sleep. */
#define LOCK_STATS
#ifdef LOCK_STATS
unsigned long GC_spin_count = 0;
unsigned long GC_block_count = 0;
unsigned long GC_unlocked_count = 0;
#endif
void GC_generic_lock(pthread_mutex_t * lock)
{
#ifndef NO_PTHREAD_TRYLOCK
unsigned pause_length = 1;
unsigned i;
if (0 == pthread_mutex_trylock(lock)) return;
if (0 == pthread_mutex_trylock(lock)) {
# ifdef LOCK_STATS
++GC_unlocked_count;
# endif
return;
}
for (; pause_length <= SPIN_MAX; pause_length <<= 1) {
for (i = 0; i < pause_length; ++i) {
GC_pause();
}
switch(pthread_mutex_trylock(lock)) {
case 0:
# ifdef LOCK_STATS
++GC_spin_count;
# endif
return;
case EBUSY:
break;
......@@ -1361,6 +1380,9 @@ void GC_generic_lock(pthread_mutex_t * lock)
}
}
#endif /* !NO_PTHREAD_TRYLOCK */
# ifdef LOCK_STATS
++GC_block_count;
# endif
pthread_mutex_lock(lock);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment