mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-24 21:11:19 +08:00
Initial revision
From-SVN: r69872
This commit is contained in:
parent
b4acb5ef4b
commit
6991c6c926
693
boehm-gc/aix_irix_threads.c
Normal file
693
boehm-gc/aix_irix_threads.c
Normal file
@ -0,0 +1,693 @@
|
||||
/*
|
||||
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
|
||||
* Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
|
||||
* Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved.
|
||||
*
|
||||
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
||||
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
||||
*
|
||||
* Permission is hereby granted to use or copy this program
|
||||
* for any purpose, provided the above notices are retained on all copies.
|
||||
* Permission to modify the code and to distribute modified code is granted,
|
||||
* provided the above notices are retained, and a notice that the code was
|
||||
* modified is included with the above copyright notice.
|
||||
*/
|
||||
/*
|
||||
* Support code for Irix (>=6.2) Pthreads and for AIX pthreads.
|
||||
* This relies on properties
|
||||
* not guaranteed by the Pthread standard. It may or may not be portable
|
||||
* to other implementations.
|
||||
*
|
||||
* Note that there is a lot of code duplication between this file and
|
||||
* (pthread_support.c, pthread_stop_world.c). They should be merged.
|
||||
* Pthread_support.c should be directly usable.
|
||||
*
|
||||
* Please avoid adding new ports here; use the generic pthread support
|
||||
* as a base instead.
|
||||
*/
|
||||
|
||||
# if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
|
||||
|
||||
# include "private/gc_priv.h"
|
||||
# include <pthread.h>
|
||||
# include <assert.h>
|
||||
# include <semaphore.h>
|
||||
# include <time.h>
|
||||
# include <errno.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/mman.h>
|
||||
# include <sys/time.h>
|
||||
|
||||
#undef pthread_create
|
||||
#undef pthread_sigmask
|
||||
#undef pthread_join
|
||||
|
||||
#if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
|
||||
#define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
|
||||
#endif
|
||||
|
||||
void GC_thr_init();
|
||||
|
||||
#if 0
|
||||
void GC_print_sig_mask()
|
||||
{
|
||||
sigset_t blocked;
|
||||
int i;
|
||||
|
||||
if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
|
||||
ABORT("pthread_sigmask");
|
||||
GC_printf0("Blocked: ");
|
||||
for (i = 1; i <= MAXSIG; i++) {
|
||||
if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
|
||||
}
|
||||
GC_printf0("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We use the allocation lock to protect thread-related data structures. */
|
||||
|
||||
/* The set of all known threads. We intercept thread creation and */
|
||||
/* joins. We never actually create detached threads. We allocate all */
|
||||
/* new thread stacks ourselves. These allow us to maintain this */
|
||||
/* data structure. */
|
||||
/* Protected by GC_thr_lock. */
|
||||
/* Some of this should be declared volatile, but that's incosnsistent */
|
||||
/* with some library routine declarations. */
|
||||
typedef struct GC_Thread_Rep {
|
||||
struct GC_Thread_Rep * next; /* More recently allocated threads */
|
||||
/* with a given pthread id come */
|
||||
/* first. (All but the first are */
|
||||
/* guaranteed to be dead, but we may */
|
||||
/* not yet have registered the join.) */
|
||||
pthread_t id;
|
||||
word stop;
|
||||
# define NOT_STOPPED 0
|
||||
# define PLEASE_STOP 1
|
||||
# define STOPPED 2
|
||||
word flags;
|
||||
# define FINISHED 1 /* Thread has exited. */
|
||||
# define DETACHED 2 /* Thread is intended to be detached. */
|
||||
ptr_t stack_cold; /* cold end of the stack */
|
||||
ptr_t stack_hot; /* Valid only when stopped. */
|
||||
/* But must be within stack region at */
|
||||
/* all times. */
|
||||
void * status; /* Used only to avoid premature */
|
||||
/* reclamation of any data it might */
|
||||
/* reference. */
|
||||
} * GC_thread;
|
||||
|
||||
GC_thread GC_lookup_thread(pthread_t id);
|
||||
|
||||
/*
|
||||
* The only way to suspend threads given the pthread interface is to send
|
||||
* signals. Unfortunately, this means we have to reserve
|
||||
* a signal, and intercept client calls to change the signal mask.
|
||||
*/
|
||||
#if 0 /* DOB: 6.1 */
|
||||
# if defined(GC_AIX_THREADS)
|
||||
# define SIG_SUSPEND SIGUSR1
|
||||
# else
|
||||
# define SIG_SUSPEND (SIGRTMIN + 6)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
/* Number of threads stopped so far */
|
||||
pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
|
||||
pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
void GC_suspend_handler(int sig)
|
||||
{
|
||||
int dummy;
|
||||
GC_thread me;
|
||||
sigset_t all_sigs;
|
||||
sigset_t old_sigs;
|
||||
int i;
|
||||
|
||||
if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
|
||||
me = GC_lookup_thread(pthread_self());
|
||||
/* 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 (PLEASE_STOP != me -> stop) {
|
||||
/* Misdirected signal. */
|
||||
pthread_mutex_unlock(&GC_suspend_lock);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&GC_suspend_lock);
|
||||
me -> stack_hot = (ptr_t)(&dummy);
|
||||
me -> stop = STOPPED;
|
||||
pthread_cond_signal(&GC_suspend_ack_cv);
|
||||
pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
|
||||
pthread_mutex_unlock(&GC_suspend_lock);
|
||||
/* GC_printf1("Continuing 0x%x\n", pthread_self()); */
|
||||
}
|
||||
|
||||
|
||||
GC_bool GC_thr_initialized = FALSE;
|
||||
|
||||
|
||||
# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
|
||||
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
|
||||
|
||||
void GC_push_thread_structures GC_PROTO((void))
|
||||
{
|
||||
GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
|
||||
}
|
||||
|
||||
/* Add a thread to GC_threads. We assume it wasn't already there. */
|
||||
/* Caller holds allocation lock. */
|
||||
GC_thread GC_new_thread(pthread_t id)
|
||||
{
|
||||
int hv = ((word)id) % THREAD_TABLE_SZ;
|
||||
GC_thread result;
|
||||
static struct GC_Thread_Rep first_thread;
|
||||
static GC_bool first_thread_used = FALSE;
|
||||
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
if (!first_thread_used) {
|
||||
result = &first_thread;
|
||||
first_thread_used = TRUE;
|
||||
/* Dont acquire allocation lock, since we may already hold it. */
|
||||
} else {
|
||||
result = (struct GC_Thread_Rep *)
|
||||
GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
|
||||
}
|
||||
if (result == 0) return(0);
|
||||
result -> id = id;
|
||||
result -> next = GC_threads[hv];
|
||||
GC_threads[hv] = result;
|
||||
/* result -> flags = 0; */
|
||||
/* result -> stop = 0; */
|
||||
return(result);
|
||||
}
|
||||
|
||||
/* Delete a thread from GC_threads. We assume it is there. */
|
||||
/* (The code intentionally traps if it wasn't.) */
|
||||
/* Caller holds allocation lock. */
|
||||
/* We explicitly pass in the GC_thread we're looking for, since */
|
||||
/* if a thread has been joined, but we have not yet */
|
||||
/* been notified, then there may be more than one thread */
|
||||
/* in the table with the same pthread id. */
|
||||
/* This is OK, but we need a way to delete a specific one. */
|
||||
void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
|
||||
{
|
||||
int hv = ((word)id) % THREAD_TABLE_SZ;
|
||||
register GC_thread p = GC_threads[hv];
|
||||
register GC_thread prev = 0;
|
||||
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
while (p != gc_id) {
|
||||
prev = p;
|
||||
p = p -> next;
|
||||
}
|
||||
if (prev == 0) {
|
||||
GC_threads[hv] = p -> next;
|
||||
} else {
|
||||
prev -> next = p -> next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a GC_thread corresponding to a given thread_t. */
|
||||
/* Returns 0 if it's not there. */
|
||||
/* Caller holds allocation lock or otherwise inhibits */
|
||||
/* updates. */
|
||||
/* If there is more than one thread with the given id we */
|
||||
/* return the most recent one. */
|
||||
GC_thread GC_lookup_thread(pthread_t id)
|
||||
{
|
||||
int hv = ((word)id) % THREAD_TABLE_SZ;
|
||||
register GC_thread p = GC_threads[hv];
|
||||
|
||||
/* I either hold the lock, or i'm being called from the stop-the-world
|
||||
* handler. */
|
||||
#if defined(GC_AIX_THREADS)
|
||||
GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
|
||||
#endif
|
||||
while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
|
||||
return(p);
|
||||
}
|
||||
|
||||
#if defined(GC_AIX_THREADS)
|
||||
void GC_stop_world()
|
||||
{
|
||||
pthread_t my_thread = pthread_self();
|
||||
register int i;
|
||||
register GC_thread p;
|
||||
register int result;
|
||||
struct timespec timeout;
|
||||
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id != my_thread) {
|
||||
pthread_suspend_np(p->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* GC_printf1("World stopped 0x%x\n", pthread_self()); */
|
||||
}
|
||||
|
||||
void GC_start_world()
|
||||
{
|
||||
GC_thread p;
|
||||
unsigned i;
|
||||
pthread_t my_thread = pthread_self();
|
||||
|
||||
/* GC_printf0("World starting\n"); */
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id != my_thread) {
|
||||
pthread_continue_np(p->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* GC_AIX_THREADS */
|
||||
|
||||
/* Caller holds allocation lock. */
|
||||
void GC_stop_world()
|
||||
{
|
||||
pthread_t my_thread = pthread_self();
|
||||
register int i;
|
||||
register GC_thread p;
|
||||
register int result;
|
||||
struct timespec timeout;
|
||||
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id != my_thread) {
|
||||
if (p -> flags & FINISHED) {
|
||||
p -> stop = STOPPED;
|
||||
continue;
|
||||
}
|
||||
p -> stop = PLEASE_STOP;
|
||||
result = pthread_kill(p -> id, SIG_SUSPEND);
|
||||
/* GC_printf1("Sent signal to 0x%x\n", p -> id); */
|
||||
switch(result) {
|
||||
case ESRCH:
|
||||
/* Not really there anymore. Possible? */
|
||||
p -> stop = STOPPED;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
ABORT("pthread_kill failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&GC_suspend_lock);
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
while (p -> id != my_thread && p -> stop != STOPPED) {
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout.tv_nsec += 50000000; /* 50 msecs */
|
||||
if (timeout.tv_nsec >= 1000000000) {
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
++timeout.tv_sec;
|
||||
}
|
||||
result = pthread_cond_timedwait(&GC_suspend_ack_cv,
|
||||
&GC_suspend_lock,
|
||||
&timeout);
|
||||
if (result == ETIMEDOUT) {
|
||||
/* Signal was lost or misdirected. Try again. */
|
||||
/* Duplicate signals should be benign. */
|
||||
result = pthread_kill(p -> id, SIG_SUSPEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&GC_suspend_lock);
|
||||
/* GC_printf1("World stopped 0x%x\n", pthread_self()); */
|
||||
}
|
||||
|
||||
/* Caller holds allocation lock. */
|
||||
void GC_start_world()
|
||||
{
|
||||
GC_thread p;
|
||||
unsigned i;
|
||||
|
||||
/* GC_printf0("World starting\n"); */
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
p -> stop = NOT_STOPPED;
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&GC_suspend_lock);
|
||||
/* All other threads are at pthread_cond_wait in signal handler. */
|
||||
/* Otherwise we couldn't have acquired the lock. */
|
||||
pthread_mutex_unlock(&GC_suspend_lock);
|
||||
pthread_cond_broadcast(&GC_continue_cv);
|
||||
}
|
||||
|
||||
#endif /* GC_AIX_THREADS */
|
||||
|
||||
|
||||
/* We hold allocation lock. Should do exactly the right thing if the */
|
||||
/* world is stopped. Should not fail if it isn't. */
|
||||
void GC_push_all_stacks()
|
||||
{
|
||||
register int i;
|
||||
register GC_thread p;
|
||||
register ptr_t hot, cold;
|
||||
pthread_t me = pthread_self();
|
||||
|
||||
/* GC_init() should have been called before GC_push_all_stacks is
|
||||
* invoked, and GC_init calls GC_thr_init(), which sets
|
||||
* GC_thr_initialized. */
|
||||
GC_ASSERT(GC_thr_initialized);
|
||||
|
||||
/* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
|
||||
GC_ASSERT(I_HOLD_LOCK());
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> flags & FINISHED) continue;
|
||||
cold = p->stack_cold;
|
||||
if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */
|
||||
if (pthread_equal(p -> id, me)) {
|
||||
hot = GC_approx_sp();
|
||||
} else {
|
||||
# ifdef GC_AIX_THREADS
|
||||
/* AIX doesn't use signals to suspend, so we need to get an */
|
||||
/* accurate hot stack pointer. */
|
||||
/* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
|
||||
pthread_t id = p -> id;
|
||||
struct __pthrdsinfo pinfo;
|
||||
int regbuf[64];
|
||||
int val = sizeof(regbuf);
|
||||
int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo,
|
||||
sizeof(pinfo), regbuf, &val);
|
||||
if (retval != 0) {
|
||||
printf("ERROR: pthread_getthrds_np() failed in GC\n");
|
||||
abort();
|
||||
}
|
||||
/* according to the AIX ABI,
|
||||
"the lowest possible valid stack address is 288 bytes (144 + 144)
|
||||
less than the current value of the stack pointer. Functions may
|
||||
use this stack space as volatile storage which is not preserved
|
||||
across function calls."
|
||||
ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
|
||||
*/
|
||||
hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
|
||||
cold = (ptr_t)pinfo.__pi_stackend; /* more precise */
|
||||
/* push the registers too, because they won't be on stack */
|
||||
GC_push_all_eager((ptr_t)&pinfo.__pi_context,
|
||||
(ptr_t)((&pinfo.__pi_context)+1));
|
||||
GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);
|
||||
# else
|
||||
hot = p -> stack_hot;
|
||||
# endif
|
||||
}
|
||||
# ifdef STACK_GROWS_UP
|
||||
GC_push_all_stack(cold, hot);
|
||||
# else
|
||||
/* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
|
||||
GC_push_all_stack(hot, cold);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* We hold the allocation lock. */
|
||||
void GC_thr_init()
|
||||
{
|
||||
GC_thread t;
|
||||
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);
|
||||
if (act.sa_handler != SIG_DFL)
|
||||
ABORT("Previously installed SIG_SUSPEND handler");
|
||||
/* Install handler. */
|
||||
act.sa_handler = GC_suspend_handler;
|
||||
act.sa_flags = SA_RESTART;
|
||||
(void) sigemptyset(&act.sa_mask);
|
||||
if (0 != sigaction(SIG_SUSPEND, &act, 0))
|
||||
ABORT("Failed to install SIG_SUSPEND handler");
|
||||
#endif
|
||||
/* Add the initial thread, so we can stop it. */
|
||||
t = GC_new_thread(pthread_self());
|
||||
/* use '0' to indicate GC_stackbottom, since GC_init() has not
|
||||
* completed by the time we are called (from GC_init_inner()) */
|
||||
t -> stack_cold = 0; /* the original stack. */
|
||||
t -> stack_hot = (ptr_t)(&t);
|
||||
t -> flags = DETACHED;
|
||||
}
|
||||
|
||||
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
|
||||
{
|
||||
sigset_t fudged_set;
|
||||
|
||||
#ifdef GC_AIX_THREADS
|
||||
return(pthread_sigmask(how, set, oset));
|
||||
#endif
|
||||
|
||||
if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
|
||||
fudged_set = *set;
|
||||
sigdelset(&fudged_set, SIG_SUSPEND);
|
||||
set = &fudged_set;
|
||||
}
|
||||
return(pthread_sigmask(how, set, oset));
|
||||
}
|
||||
|
||||
struct start_info {
|
||||
void *(*start_routine)(void *);
|
||||
void *arg;
|
||||
word flags;
|
||||
pthread_mutex_t registeredlock;
|
||||
pthread_cond_t registered;
|
||||
int volatile registereddone;
|
||||
};
|
||||
|
||||
void GC_thread_exit_proc(void *arg)
|
||||
{
|
||||
GC_thread me;
|
||||
|
||||
LOCK();
|
||||
me = GC_lookup_thread(pthread_self());
|
||||
me -> flags |= FINISHED;
|
||||
/* reclaim DETACHED thread right away; otherwise wait until join() */
|
||||
if (me -> flags & DETACHED) {
|
||||
GC_delete_gc_thread(pthread_self(), me);
|
||||
}
|
||||
UNLOCK();
|
||||
}
|
||||
|
||||
int GC_pthread_join(pthread_t thread, void **retval)
|
||||
{
|
||||
int result;
|
||||
GC_thread thread_gc_id;
|
||||
|
||||
LOCK();
|
||||
thread_gc_id = GC_lookup_thread(thread);
|
||||
/* This is guaranteed to be the intended one, since the thread id */
|
||||
/* cant have been recycled by pthreads. */
|
||||
UNLOCK();
|
||||
GC_ASSERT(!(thread_gc_id->flags & DETACHED));
|
||||
result = pthread_join(thread, retval);
|
||||
/* Some versions of the Irix pthreads library can erroneously */
|
||||
/* return EINTR when the call succeeds. */
|
||||
if (EINTR == result) result = 0;
|
||||
GC_ASSERT(thread_gc_id->flags & FINISHED);
|
||||
LOCK();
|
||||
/* Here the pthread thread id may have been recycled. */
|
||||
GC_delete_gc_thread(thread, thread_gc_id);
|
||||
UNLOCK();
|
||||
return result;
|
||||
}
|
||||
|
||||
void * GC_start_routine(void * arg)
|
||||
{
|
||||
int dummy;
|
||||
struct start_info * si = arg;
|
||||
void * result;
|
||||
GC_thread me;
|
||||
pthread_t my_pthread;
|
||||
void *(*start)(void *);
|
||||
void *start_arg;
|
||||
|
||||
my_pthread = pthread_self();
|
||||
/* If a GC occurs before the thread is registered, that GC will */
|
||||
/* ignore this thread. That's fine, since it will block trying to */
|
||||
/* acquire the allocation lock, and won't yet hold interesting */
|
||||
/* pointers. */
|
||||
LOCK();
|
||||
/* We register the thread here instead of in the parent, so that */
|
||||
/* we don't need to hold the allocation lock during pthread_create. */
|
||||
/* Holding the allocation lock there would make REDIRECT_MALLOC */
|
||||
/* impossible. It probably still doesn't work, but we're a little */
|
||||
/* closer ... */
|
||||
/* This unfortunately means that we have to be careful the parent */
|
||||
/* doesn't try to do a pthread_join before we're registered. */
|
||||
me = GC_new_thread(my_pthread);
|
||||
me -> flags = si -> flags;
|
||||
me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */
|
||||
me -> stack_hot = me->stack_cold;/* this field should always be sensible */
|
||||
UNLOCK();
|
||||
start = si -> start_routine;
|
||||
start_arg = si -> arg;
|
||||
|
||||
pthread_mutex_lock(&(si->registeredlock));
|
||||
si->registereddone = 1;
|
||||
pthread_cond_signal(&(si->registered));
|
||||
pthread_mutex_unlock(&(si->registeredlock));
|
||||
/* si went away as soon as we did this unlock */
|
||||
|
||||
pthread_cleanup_push(GC_thread_exit_proc, 0);
|
||||
result = (*start)(start_arg);
|
||||
me -> status = result;
|
||||
pthread_cleanup_pop(1);
|
||||
/* This involves acquiring the lock, ensuring that we can't exit */
|
||||
/* while a collection that thinks we're alive is trying to stop */
|
||||
/* us. */
|
||||
return(result);
|
||||
}
|
||||
|
||||
int
|
||||
GC_pthread_create(pthread_t *new_thread,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
int result;
|
||||
GC_thread t;
|
||||
int detachstate;
|
||||
word my_flags = 0;
|
||||
struct start_info * si;
|
||||
/* This is otherwise saved only in an area mmapped by the thread */
|
||||
/* library, which isn't visible to the collector. */
|
||||
|
||||
LOCK();
|
||||
/* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */
|
||||
si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
|
||||
NORMAL);
|
||||
GC_ASSERT(GC_thr_initialized); /* initialized by GC_init() */
|
||||
UNLOCK();
|
||||
if (0 == si) return(ENOMEM);
|
||||
pthread_mutex_init(&(si->registeredlock), NULL);
|
||||
pthread_cond_init(&(si->registered),NULL);
|
||||
pthread_mutex_lock(&(si->registeredlock));
|
||||
si -> start_routine = start_routine;
|
||||
si -> arg = arg;
|
||||
|
||||
pthread_attr_getdetachstate(attr, &detachstate);
|
||||
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
|
||||
si -> flags = my_flags;
|
||||
result = pthread_create(new_thread, attr, GC_start_routine, si);
|
||||
|
||||
/* Wait until child has been added to the thread table. */
|
||||
/* This also ensures that we hold onto si until the child is done */
|
||||
/* with it. Thus it doesn't matter whether it is otherwise */
|
||||
/* visible to the collector. */
|
||||
|
||||
if (0 == result) {
|
||||
si->registereddone = 0;
|
||||
while (!si->registereddone)
|
||||
pthread_cond_wait(&(si->registered), &(si->registeredlock));
|
||||
}
|
||||
pthread_mutex_unlock(&(si->registeredlock));
|
||||
|
||||
pthread_cond_destroy(&(si->registered));
|
||||
pthread_mutex_destroy(&(si->registeredlock));
|
||||
LOCK();
|
||||
GC_INTERNAL_FREE(si);
|
||||
UNLOCK();
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/* For now we use the pthreads locking primitives on HP/UX */
|
||||
|
||||
VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and */
|
||||
/* holding the allocation lock for an */
|
||||
/* extended period. */
|
||||
|
||||
/* Reasonably fast spin locks. Basically the same implementation */
|
||||
/* as STL alloc.h. */
|
||||
|
||||
#define SLEEP_THRESHOLD 3
|
||||
|
||||
volatile unsigned int GC_allocate_lock = 0;
|
||||
#define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
|
||||
#define GC_LOCK_TAKEN GC_allocate_lock
|
||||
|
||||
void GC_lock()
|
||||
{
|
||||
# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
|
||||
# define high_spin_max 1000 /* spin cycles for multiprocessor */
|
||||
static unsigned spin_max = low_spin_max;
|
||||
unsigned my_spin_max;
|
||||
static unsigned last_spins = 0;
|
||||
unsigned my_last_spins;
|
||||
volatile unsigned junk;
|
||||
# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
|
||||
int i;
|
||||
|
||||
if (GC_TRY_LOCK()) {
|
||||
return;
|
||||
}
|
||||
junk = 0;
|
||||
my_spin_max = spin_max;
|
||||
my_last_spins = last_spins;
|
||||
for (i = 0; i < my_spin_max; i++) {
|
||||
if (GC_collecting) goto yield;
|
||||
if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
|
||||
PAUSE;
|
||||
continue;
|
||||
}
|
||||
if (GC_TRY_LOCK()) {
|
||||
/*
|
||||
* got it!
|
||||
* Spinning worked. Thus we're probably not being scheduled
|
||||
* against the other process with which we were contending.
|
||||
* Thus it makes sense to spin longer the next time.
|
||||
*/
|
||||
last_spins = i;
|
||||
spin_max = high_spin_max;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* We are probably being scheduled against the other process. Sleep. */
|
||||
spin_max = low_spin_max;
|
||||
yield:
|
||||
for (i = 0;; ++i) {
|
||||
if (GC_TRY_LOCK()) {
|
||||
return;
|
||||
}
|
||||
if (i < SLEEP_THRESHOLD) {
|
||||
sched_yield();
|
||||
} else {
|
||||
struct timespec ts;
|
||||
|
||||
if (i > 26) i = 26;
|
||||
/* Don't wait for more than about 60msecs, even */
|
||||
/* under extreme contention. */
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1 << i;
|
||||
nanosleep(&ts, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
|
||||
|
||||
#ifndef LINT
|
||||
int GC_no_Irix_threads;
|
||||
#endif
|
||||
|
||||
# endif /* IRIX_THREADS */
|
||||
|
87
boehm-gc/alpha_mach_dep.S
Normal file
87
boehm-gc/alpha_mach_dep.S
Normal file
@ -0,0 +1,87 @@
|
||||
# $Id: alpha_mach_dep.s,v 1.2 1993/01/18 22:54:51 dosser Exp $
|
||||
.arch ev6
|
||||
|
||||
.text
|
||||
.align 4
|
||||
.globl GC_push_regs
|
||||
.ent GC_push_regs 2
|
||||
GC_push_regs:
|
||||
ldgp $gp, 0($27)
|
||||
lda $sp, -16($sp)
|
||||
stq $26, 0($sp)
|
||||
.mask 0x04000000, 0
|
||||
.frame $sp, 16, $26, 0
|
||||
|
||||
# $0 integer result
|
||||
# $1-$8 temp regs - not preserved cross calls
|
||||
# $9-$15 call saved regs
|
||||
# $16-$21 argument regs - not preserved cross calls
|
||||
# $22-$28 temp regs - not preserved cross calls
|
||||
# $29 global pointer - not preserved cross calls
|
||||
# $30 stack pointer
|
||||
|
||||
# define call_push(x) \
|
||||
mov x, $16; \
|
||||
jsr $26, GC_push_one; \
|
||||
ldgp $gp, 0($26)
|
||||
|
||||
call_push($9)
|
||||
call_push($10)
|
||||
call_push($11)
|
||||
call_push($12)
|
||||
call_push($13)
|
||||
call_push($14)
|
||||
call_push($15)
|
||||
|
||||
# $f0-$f1 floating point results
|
||||
# $f2-$f9 call saved regs
|
||||
# $f10-$f30 temp regs - not preserved cross calls
|
||||
|
||||
# Use the most efficient transfer method for this hardware.
|
||||
# Bit 1 detects the FIX extension, which includes ftoit.
|
||||
amask 2, $0
|
||||
bne $0, $use_stack
|
||||
|
||||
#undef call_push
|
||||
#define call_push(x) \
|
||||
ftoit x, $16; \
|
||||
jsr $26, GC_push_one; \
|
||||
ldgp $gp, 0($26)
|
||||
|
||||
call_push($f2)
|
||||
call_push($f3)
|
||||
call_push($f4)
|
||||
call_push($f5)
|
||||
call_push($f6)
|
||||
call_push($f7)
|
||||
call_push($f8)
|
||||
call_push($f9)
|
||||
|
||||
ldq $26, 0($sp)
|
||||
lda $sp, 16($sp)
|
||||
ret $31, ($26), 1
|
||||
|
||||
.align 4
|
||||
$use_stack:
|
||||
|
||||
#undef call_push
|
||||
#define call_push(x) \
|
||||
stt x, 8($sp); \
|
||||
ldq $16, 8($sp); \
|
||||
jsr $26, GC_push_one; \
|
||||
ldgp $gp, 0($26)
|
||||
|
||||
call_push($f2)
|
||||
call_push($f3)
|
||||
call_push($f4)
|
||||
call_push($f5)
|
||||
call_push($f6)
|
||||
call_push($f7)
|
||||
call_push($f8)
|
||||
call_push($f9)
|
||||
|
||||
ldq $26, 0($sp)
|
||||
lda $sp, 16($sp)
|
||||
ret $31, ($26), 1
|
||||
|
||||
.end GC_push_regs
|
209
boehm-gc/darwin_stop_world.c
Normal file
209
boehm-gc/darwin_stop_world.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include "private/pthread_support.h"
|
||||
|
||||
# if defined(GC_DARWIN_THREADS)
|
||||
|
||||
#define DEBUG_THREADS 0
|
||||
|
||||
/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
|
||||
Page 49:
|
||||
"The space beneath the stack pointer, where a new stack frame would normally
|
||||
be allocated, is called the red zone. This area as shown in Figure 3-2 may
|
||||
be used for any purpose as long as a new stack frame does not need to be
|
||||
added to the stack."
|
||||
|
||||
Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
|
||||
it must set up a stack frame just like routines that call other routines."
|
||||
*/
|
||||
#define PPC_RED_ZONE_SIZE 224
|
||||
|
||||
void GC_push_all_stacks() {
|
||||
int i;
|
||||
kern_return_t r;
|
||||
GC_thread p;
|
||||
pthread_t me;
|
||||
ptr_t lo, hi;
|
||||
# if defined(POWERPC)
|
||||
ppc_thread_state_t state;
|
||||
# else
|
||||
# error FIXME for non-ppc OS X
|
||||
# endif
|
||||
mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
|
||||
|
||||
me = pthread_self();
|
||||
if (!GC_thr_initialized) GC_thr_init();
|
||||
|
||||
for(i=0;i<THREAD_TABLE_SZ;i++) {
|
||||
for(p=GC_threads[i];p!=0;p=p->next) {
|
||||
if(p -> flags & FINISHED) continue;
|
||||
if(pthread_equal(p->id,me)) {
|
||||
lo = GC_approx_sp();
|
||||
} else {
|
||||
/* Get the thread state (registers, etc) */
|
||||
r = thread_get_state(
|
||||
p->stop_info.mach_thread,
|
||||
MACHINE_THREAD_STATE,
|
||||
(natural_t*)&state,
|
||||
&thread_state_count);
|
||||
if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
|
||||
|
||||
#ifdef POWERPC
|
||||
lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
|
||||
|
||||
GC_push_one(state.r0);
|
||||
GC_push_one(state.r2);
|
||||
GC_push_one(state.r3);
|
||||
GC_push_one(state.r4);
|
||||
GC_push_one(state.r5);
|
||||
GC_push_one(state.r6);
|
||||
GC_push_one(state.r7);
|
||||
GC_push_one(state.r8);
|
||||
GC_push_one(state.r9);
|
||||
GC_push_one(state.r10);
|
||||
GC_push_one(state.r11);
|
||||
GC_push_one(state.r12);
|
||||
GC_push_one(state.r13);
|
||||
GC_push_one(state.r14);
|
||||
GC_push_one(state.r15);
|
||||
GC_push_one(state.r16);
|
||||
GC_push_one(state.r17);
|
||||
GC_push_one(state.r18);
|
||||
GC_push_one(state.r19);
|
||||
GC_push_one(state.r20);
|
||||
GC_push_one(state.r21);
|
||||
GC_push_one(state.r22);
|
||||
GC_push_one(state.r23);
|
||||
GC_push_one(state.r24);
|
||||
GC_push_one(state.r25);
|
||||
GC_push_one(state.r26);
|
||||
GC_push_one(state.r27);
|
||||
GC_push_one(state.r28);
|
||||
GC_push_one(state.r29);
|
||||
GC_push_one(state.r30);
|
||||
GC_push_one(state.r31);
|
||||
#else
|
||||
# error FIXME for non-PPC darwin
|
||||
#endif /* !POWERPC */
|
||||
} /* p != me */
|
||||
if(p->flags & MAIN_THREAD)
|
||||
hi = GC_stackbottom;
|
||||
else
|
||||
hi = p->stack_end;
|
||||
#if DEBUG_THREADS
|
||||
GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
|
||||
(unsigned long) p -> id,
|
||||
(unsigned long) lo,
|
||||
(unsigned long) hi
|
||||
);
|
||||
#endif
|
||||
GC_push_all_stack(lo,hi);
|
||||
} /* for(p=GC_threads[i]...) */
|
||||
} /* for(i=0;i<THREAD_TABLE_SZ...) */
|
||||
}
|
||||
|
||||
/* Caller holds allocation lock. */
|
||||
void GC_stop_world()
|
||||
{
|
||||
int i;
|
||||
GC_thread p;
|
||||
pthread_t my_thread = pthread_self();
|
||||
kern_return_t kern_result;
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
|
||||
#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
|
||||
GC_acquire_mark_lock();
|
||||
GC_ASSERT(GC_fl_builder_count == 0);
|
||||
/* We should have previously waited for it to become zero. */
|
||||
# endif /* PARALLEL_MARK */
|
||||
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id == my_thread) continue;
|
||||
if (p -> flags & FINISHED) continue;
|
||||
if (p -> thread_blocked) /* Will wait */ continue;
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Suspending thread 0x%lx\n", p -> id);
|
||||
#endif
|
||||
|
||||
/* Suspend the thread */
|
||||
kern_result = thread_suspend(p->stop_info.mach_thread);
|
||||
if(kern_result != KERN_SUCCESS) ABORT("thread_suspend failed");
|
||||
|
||||
/* This is only needed if we are modifying the threads
|
||||
state. thread_abort_safely should also be used
|
||||
if this code is ever added in again.
|
||||
|
||||
kern_result = thread_abort(p->stop_info.mach_thread);
|
||||
if(kern_result != KERN_SUCCESS)
|
||||
ABORT("thread_abort failed (%ul)",kern_result);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
# ifdef MPROTECT_VDB
|
||||
if(GC_incremental) {
|
||||
extern void GC_mprotect_stop();
|
||||
GC_mprotect_stop();
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifdef PARALLEL_MARK
|
||||
GC_release_mark_lock();
|
||||
# endif
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("World stopped from 0x%lx\n", pthread_self());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Caller holds allocation lock, and has held it continuously since */
|
||||
/* the world stopped. */
|
||||
void GC_start_world()
|
||||
{
|
||||
pthread_t my_thread = pthread_self();
|
||||
int i;
|
||||
GC_thread p;
|
||||
kern_return_t kern_result;
|
||||
|
||||
# if DEBUG_THREADS
|
||||
GC_printf0("World starting\n");
|
||||
# endif
|
||||
|
||||
# ifdef MPROTECT_VDB
|
||||
if(GC_incremental) {
|
||||
extern void GC_mprotect_resume();
|
||||
GC_mprotect_resume();
|
||||
}
|
||||
# endif
|
||||
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id == my_thread) continue;
|
||||
if (p -> flags & FINISHED) continue;
|
||||
if (p -> thread_blocked) continue;
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Resuming 0x%lx\n", p -> id);
|
||||
#endif
|
||||
|
||||
/* Resume the thread */
|
||||
kern_result = thread_resume(p->stop_info.mach_thread);
|
||||
if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
|
||||
}
|
||||
}
|
||||
#if DEBUG_THREADS
|
||||
GC_printf0("World started\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void GC_stop_init() {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
436
boehm-gc/depcomp
Executable file
436
boehm-gc/depcomp
Executable file
@ -0,0 +1,436 @@
|
||||
#! /bin/sh
|
||||
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
# Copyright 1999, 2000 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
# 02111-1307, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
# `libtool' can also be set to `yes' or `no'.
|
||||
|
||||
if test -z "$depfile"; then
|
||||
base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'`
|
||||
dir=`echo "$object" | sed 's,/.*$,/,'`
|
||||
if test "$dir" = "$object"; then
|
||||
dir=
|
||||
fi
|
||||
# FIXME: should be _deps on DOS.
|
||||
depfile="$dir.deps/$base"
|
||||
fi
|
||||
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
"$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the `deleted header file' problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the `:'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like `#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr '
|
||||
' ' ' >> $depfile
|
||||
echo >> $depfile
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> $depfile
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. This file always lives in the current directory.
|
||||
# Also, the AIX compiler puts `$object:' at the start of each line;
|
||||
# $object doesn't have directory information.
|
||||
stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'`
|
||||
tmpdepfile="$stripped.u"
|
||||
outname="$stripped.o"
|
||||
if test "$libtool" = yes; then
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
"$@" -M
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form `foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in `foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
|
||||
base=`echo "$object" | sed -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
tmpdepfile1="$base.o.d"
|
||||
tmpdepfile2="$base.d"
|
||||
if test "$libtool" = yes; then
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
if test -f "$tmpdepfile1"; then
|
||||
tmpdepfile="$tmpdepfile1"
|
||||
else
|
||||
tmpdepfile="$tmpdepfile2"
|
||||
fi
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a space and a tab in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the proprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
( IFS=" "
|
||||
case " $* " in
|
||||
*" --mode=compile "*) # this is libtool, let us make it quiet
|
||||
for arg
|
||||
do # cycle over the arguments
|
||||
case "$arg" in
|
||||
"--mode=compile")
|
||||
# insert --quiet before "--mode=compile"
|
||||
set fnord "$@" --quiet
|
||||
shift # fnord
|
||||
;;
|
||||
esac
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # "$arg"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
"$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
) &
|
||||
proc=$!
|
||||
"$@"
|
||||
stat=$?
|
||||
wait "$proc"
|
||||
if test "$stat" != 0; then exit $stat; fi
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
# X makedepend
|
||||
(
|
||||
shift
|
||||
cleared=no
|
||||
for arg in "$@"; do
|
||||
case $cleared in no)
|
||||
set ""; shift
|
||||
cleared=yes
|
||||
esac
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift;;
|
||||
esac
|
||||
done
|
||||
obj_suffix="`echo $object | sed 's/^.*\././'`"
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
) &
|
||||
proc=$!
|
||||
"$@"
|
||||
stat=$?
|
||||
wait "$proc"
|
||||
if test "$stat" != 0; then exit $stat; fi
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
||||
' | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the proprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
( IFS=" "
|
||||
case " $* " in
|
||||
*" --mode=compile "*)
|
||||
for arg
|
||||
do # cycle over the arguments
|
||||
case $arg in
|
||||
"--mode=compile")
|
||||
# insert --quiet before "--mode=compile"
|
||||
set fnord "$@" --quiet
|
||||
shift # fnord
|
||||
;;
|
||||
esac
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # "$arg"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
"$@" -E |
|
||||
sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
) &
|
||||
proc=$!
|
||||
"$@"
|
||||
stat=$?
|
||||
wait "$proc"
|
||||
if test "$stat" != 0; then exit $stat; fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the proprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
( IFS=" "
|
||||
case " $* " in
|
||||
*" --mode=compile "*)
|
||||
for arg
|
||||
do # cycle over the arguments
|
||||
case $arg in
|
||||
"--mode=compile")
|
||||
# insert --quiet before "--mode=compile"
|
||||
set fnord "$@" --quiet
|
||||
shift # fnord
|
||||
;;
|
||||
esac
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # "$arg"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
||||
) &
|
||||
proc=$!
|
||||
"$@"
|
||||
stat=$?
|
||||
wait "$proc"
|
||||
if test "$stat" != 0; then exit $stat; fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
||||
echo " " >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
27
boehm-gc/doc/Makefile.am
Normal file
27
boehm-gc/doc/Makefile.am
Normal file
@ -0,0 +1,27 @@
|
||||
#
|
||||
#
|
||||
# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
||||
# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
||||
#
|
||||
# Permission is hereby granted to use or copy this program
|
||||
# for any purpose, provided the above notices are retained on all copies.
|
||||
# Permission to modify the code and to distribute modified code is granted,
|
||||
# provided the above notices are retained, and a notice that the code was
|
||||
# modified is included with the above copyright notice.
|
||||
#
|
||||
# Modified by: Grzegorz Jakacki <jakacki at acm dot org>
|
||||
|
||||
## Process this file with automake to produce Makefile.in.
|
||||
|
||||
# installed documentation
|
||||
#
|
||||
dist_pkgdata_DATA = barrett_diagram debugging.html gc.man \
|
||||
gcdescr.html README README.amiga README.arm.cross \
|
||||
README.autoconf README.changes README.contributors \
|
||||
README.cords README.DGUX386 README.dj README.environment \
|
||||
README.ews4800 README.hp README.linux README.Mac \
|
||||
README.MacOSX README.macros README.OS2 README.rs6000 \
|
||||
README.sgi README.solaris2 README.uts README.win32 \
|
||||
tree.html leak.html gcinterface.html scale.html \
|
||||
README.darwin
|
||||
|
282
boehm-gc/doc/Makefile.in
Normal file
282
boehm-gc/doc/Makefile.in
Normal file
@ -0,0 +1,282 @@
|
||||
# Makefile.in generated by automake 1.6.3 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
|
||||
# Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
#
|
||||
# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
||||
# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
||||
#
|
||||
# Permission is hereby granted to use or copy this program
|
||||
# for any purpose, provided the above notices are retained on all copies.
|
||||
# Permission to modify the code and to distribute modified code is granted,
|
||||
# provided the above notices are retained, and a notice that the code was
|
||||
# modified is included with the above copyright notice.
|
||||
#
|
||||
# Modified by: Grzegorz Jakacki <jakacki at acm dot org>
|
||||
SHELL = @SHELL@
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
bindir = @bindir@
|
||||
sbindir = @sbindir@
|
||||
libexecdir = @libexecdir@
|
||||
datadir = @datadir@
|
||||
sysconfdir = @sysconfdir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
localstatedir = @localstatedir@
|
||||
libdir = @libdir@
|
||||
infodir = @infodir@
|
||||
mandir = @mandir@
|
||||
includedir = @includedir@
|
||||
oldincludedir = /usr/include
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = ..
|
||||
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = @program_transform_name@
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
host_alias = @host_alias@
|
||||
host_triplet = @host@
|
||||
|
||||
EXEEXT = @EXEEXT@
|
||||
OBJEXT = @OBJEXT@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CXXINCLUDES = @CXXINCLUDES@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
ECHO = @ECHO@
|
||||
EXTRA_TEST_LIBS = @EXTRA_TEST_LIBS@
|
||||
GC_CFLAGS = @GC_CFLAGS@
|
||||
GC_VERSION = @GC_VERSION@
|
||||
INCLUDES = @INCLUDES@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LN_S = @LN_S@
|
||||
MAINT = @MAINT@
|
||||
MY_CFLAGS = @MY_CFLAGS@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
PACKAGE = @PACKAGE@
|
||||
RANLIB = @RANLIB@
|
||||
STRIP = @STRIP@
|
||||
THREADLIBS = @THREADLIBS@
|
||||
VERSION = @VERSION@
|
||||
addincludes = @addincludes@
|
||||
addlibs = @addlibs@
|
||||
addobjs = @addobjs@
|
||||
addtests = @addtests@
|
||||
am__include = @am__include@
|
||||
am__quote = @am__quote@
|
||||
install_sh = @install_sh@
|
||||
target_all = @target_all@
|
||||
|
||||
# installed documentation
|
||||
#
|
||||
dist_pkgdata_DATA = barrett_diagram debugging.html gc.man \
|
||||
gcdescr.html README README.amiga README.arm.cross \
|
||||
README.autoconf README.changes README.contributors \
|
||||
README.cords README.DGUX386 README.dj README.environment \
|
||||
README.ews4800 README.hp README.linux README.Mac \
|
||||
README.MacOSX README.macros README.OS2 README.rs6000 \
|
||||
README.sgi README.solaris2 README.uts README.win32 \
|
||||
tree.html leak.html gcinterface.html scale.html \
|
||||
README.darwin
|
||||
|
||||
subdir = doc
|
||||
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
||||
CONFIG_CLEAN_FILES =
|
||||
DIST_SOURCES =
|
||||
DATA = $(dist_pkgdata_DATA)
|
||||
|
||||
DIST_COMMON = README $(dist_pkgdata_DATA) Makefile.am Makefile.in
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
|
||||
cd $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu doc/Makefile
|
||||
Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
dist_pkgdataDATA_INSTALL = $(INSTALL_DATA)
|
||||
install-dist_pkgdataDATA: $(dist_pkgdata_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
|
||||
@list='$(dist_pkgdata_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f="`echo $$p | sed -e 's|^.*/||'`"; \
|
||||
echo " $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \
|
||||
$(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \
|
||||
done
|
||||
|
||||
uninstall-dist_pkgdataDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_pkgdata_DATA)'; for p in $$list; do \
|
||||
f="`echo $$p | sed -e 's|^.*/||'`"; \
|
||||
echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \
|
||||
rm -f $(DESTDIR)$(pkgdatadir)/$$f; \
|
||||
done
|
||||
tags: TAGS
|
||||
TAGS:
|
||||
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
|
||||
top_distdir = ..
|
||||
distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@list='$(DISTFILES)'; for file in $$list; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkinstalldirs) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
|
||||
else \
|
||||
test -f $(distdir)/$$file \
|
||||
|| cp -p $$d/$$file $(distdir)/$$file \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(DATA)
|
||||
|
||||
installdirs:
|
||||
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
|
||||
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
INSTALL_STRIP_FLAG=-s \
|
||||
`test -z '$(STRIP)' || \
|
||||
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-rm -f Makefile $(CONFIG_CLEAN_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
|
||||
distclean-am: clean-am distclean-generic distclean-libtool
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am: install-dist_pkgdataDATA
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
|
||||
|
||||
uninstall-am: uninstall-dist_pkgdataDATA uninstall-info-am
|
||||
|
||||
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
|
||||
distclean distclean-generic distclean-libtool distdir dvi \
|
||||
dvi-am info info-am install install-am install-data \
|
||||
install-data-am install-dist_pkgdataDATA install-exec \
|
||||
install-exec-am install-info install-info-am install-man \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-generic mostlyclean-libtool uninstall uninstall-am \
|
||||
uninstall-dist_pkgdataDATA uninstall-info-am
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
215
boehm-gc/doc/README.DGUX386
Normal file
215
boehm-gc/doc/README.DGUX386
Normal file
@ -0,0 +1,215 @@
|
||||
Garbage Collector (parallel iversion) for ix86 DG/UX Release R4.20MU07
|
||||
|
||||
|
||||
*READ* the file README.QUICK.
|
||||
|
||||
You need the GCC-3.0.3 rev (DG/UX) compiler to build this tree.
|
||||
This compiler has the new "dgux386" threads package implemented.
|
||||
It also supports the switch "-pthread" needed to link correctly
|
||||
the DG/UX's -lrte -lthread with -lgcc and the system's -lc.
|
||||
Finally we support parralleli-mark for the SMP DG/UX machines.
|
||||
To build the garbage collector do:
|
||||
|
||||
./configure --enable-parallel-mark
|
||||
make
|
||||
make gctest
|
||||
|
||||
Before you run "gctest" you need to set your LD_LIBRARY_PATH
|
||||
correctly so that "gctest" can find the shared library libgc.
|
||||
Alternatively you can do a configuration
|
||||
|
||||
./configure --enable-parallel-mark --disable-shared
|
||||
|
||||
to build only the static version of libgc.
|
||||
|
||||
To enable debugging messages please do:
|
||||
1) Add the "--enable-full-debug" flag during configuration.
|
||||
2) Edit the file linux-threads.c and uncommnect the line:
|
||||
|
||||
/* #define DEBUG_THREADS 1 */ to --->
|
||||
|
||||
#define DEBUG_THREADS 1
|
||||
|
||||
Then give "make" as usual.
|
||||
|
||||
In a machine with 4 CPUs (my own machine) the option parallel
|
||||
mark (aka --enable-parallel-mark) makes a BIG difference.
|
||||
|
||||
Takis Psarogiannakopoulos
|
||||
University of Cambridge
|
||||
Centre for Mathematical Sciences
|
||||
Department of Pure Mathematics
|
||||
Wilberforce Road
|
||||
Cambridge CB3 0WB ,UK , <takis@XFree86.Org>
|
||||
January 2002
|
||||
|
||||
|
||||
Note (HB):
|
||||
The integration of this patch is currently not complete.
|
||||
The following patches against 6.1alpha3 where hard to move
|
||||
to alpha4, and are not integrated. There may also be minor
|
||||
problems with stylistic corrections made by me.
|
||||
|
||||
|
||||
--- ltconfig.ORIG Mon Jan 28 20:22:18 2002
|
||||
+++ ltconfig Mon Jan 28 20:44:00 2002
|
||||
@@ -689,6 +689,11 @@
|
||||
pic_flag=-Kconform_pic
|
||||
fi
|
||||
;;
|
||||
+ dgux*)
|
||||
+ pic_flag='-fPIC'
|
||||
+ link_static='-Bstatic'
|
||||
+ wl='-Wl,'
|
||||
+ ;;
|
||||
*)
|
||||
pic_flag='-fPIC'
|
||||
;;
|
||||
@@ -718,6 +723,12 @@
|
||||
# We can build DLLs from non-PIC.
|
||||
;;
|
||||
|
||||
+ dgux*)
|
||||
+ pic_flag='-KPIC'
|
||||
+ link_static='-Bstatic'
|
||||
+ wl='-Wl,'
|
||||
+ ;;
|
||||
+
|
||||
osf3* | osf4* | osf5*)
|
||||
# All OSF/1 code is PIC.
|
||||
wl='-Wl,'
|
||||
@@ -1154,6 +1165,22 @@
|
||||
fi
|
||||
;;
|
||||
|
||||
+ dgux*)
|
||||
+ ld_shlibs=yes
|
||||
+ # For both C/C++ ommit the deplibs. This is because we relying on the fact
|
||||
+ # that compilation of execitables will put them in correct order
|
||||
+ # in any case and sometimes are wrong when listed as deplibs (or missing some deplibs)
|
||||
+ # However when GNU ld and --whole-archive needs to be used we have the problem
|
||||
+ # that if the -fPIC *_s.a archive is linked through deplibs list we ommiting crucial
|
||||
+ # .lo/.o files from the created shared lib. This I think is not the case here.
|
||||
+ archive_cmds='$CC -shared -h $soname -o $lib $libobjs $linkopts'
|
||||
+ thread_safe_flag_spec='-pthread'
|
||||
+ wlarc=
|
||||
+ hardcode_libdir_flag_spec='-L$libdir'
|
||||
+ hardcode_shlibpath_var=no
|
||||
+ ac_cv_archive_cmds_needs_lc=no
|
||||
+ ;;
|
||||
+
|
||||
cygwin* | mingw*)
|
||||
# hardcode_libdir_flag_spec is actually meaningless, as there is
|
||||
# no search path for DLLs.
|
||||
@@ -1497,7 +1524,7 @@
|
||||
;;
|
||||
|
||||
dgux*)
|
||||
- archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linkopts'
|
||||
+ archive_cmds='$CC -shared -h $soname -o $lib $libobjs $linkopts'
|
||||
hardcode_libdir_flag_spec='-L$libdir'
|
||||
hardcode_shlibpath_var=no
|
||||
;;
|
||||
@@ -2092,12 +2119,17 @@
|
||||
;;
|
||||
|
||||
dgux*)
|
||||
- version_type=linux
|
||||
+ version_type=dgux
|
||||
need_lib_prefix=no
|
||||
need_version=no
|
||||
- library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
|
||||
- soname_spec='${libname}${release}.so$major'
|
||||
+ library_names_spec='$libname.so$versuffix'
|
||||
+ soname_spec='$libname.so$versuffix'
|
||||
shlibpath_var=LD_LIBRARY_PATH
|
||||
+ thread_safe_flag_spec='-pthread'
|
||||
+ wlarc=
|
||||
+ hardcode_libdir_flag_spec='-L$libdir'
|
||||
+ hardcode_shlibpath_var=no
|
||||
+ ac_cv_archive_cmds_needs_lc=no
|
||||
;;
|
||||
|
||||
sysv4*MP*)
|
||||
|
||||
|
||||
--- ltmain.sh.ORIG Mon Jan 28 20:31:18 2002
|
||||
+++ ltmain.sh Tue Jan 29 00:11:29 2002
|
||||
@@ -1072,11 +1072,38 @@
|
||||
esac
|
||||
;;
|
||||
|
||||
+ -thread*)
|
||||
+ # DG/UX GCC 2.95.x, 3.x.x rev (DG/UX) links -lthread
|
||||
+ # with the switch -threads
|
||||
+ if test "$arg" = "-threads"; then
|
||||
+ case "$host" in
|
||||
+ i[3456]86-*-dgux*)
|
||||
+ deplibs="$deplibs $arg"
|
||||
+ continue
|
||||
+ ;;
|
||||
+ esac
|
||||
+ fi
|
||||
+ ;;
|
||||
+
|
||||
+ -pthread*)
|
||||
+ # DG/UX GCC 2.95.x, 3.x.x rev (DG/UX) links -lthread
|
||||
+ # with the switch -pthread
|
||||
+ if test "$arg" = "-pthread"; then
|
||||
+ case "$host" in
|
||||
+ i[3456]86-*-dgux*)
|
||||
+ deplibs="$deplibs $arg"
|
||||
+ continue
|
||||
+ ;;
|
||||
+ esac
|
||||
+ fi
|
||||
+ ;;
|
||||
+
|
||||
-l*)
|
||||
if test "$arg" = "-lc"; then
|
||||
case "$host" in
|
||||
- *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
|
||||
+ *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos* | i[3456]86-*-dgux*)
|
||||
# These systems don't actually have c library (as such)
|
||||
+ # It is wrong in DG/UX to add -lc when creating shared/dynamic objs/libs
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
@@ -1248,6 +1275,12 @@
|
||||
temp_deplibs=
|
||||
for deplib in $dependency_libs; do
|
||||
case "$deplib" in
|
||||
+ -thread*)
|
||||
+ temp_deplibs="$temp_deplibs $deplib"
|
||||
+ ;;
|
||||
+ -pthread)
|
||||
+ temp_deplibs="$temp_deplibs $deplib"
|
||||
+ ;;
|
||||
-R*) temp_xrpath=`$echo "X$deplib" | $Xsed -e 's/^-R//'`
|
||||
case " $rpath $xrpath " in
|
||||
*" $temp_xrpath "*) ;;
|
||||
@@ -1709,6 +1742,13 @@
|
||||
done
|
||||
;;
|
||||
|
||||
+ dgux)
|
||||
+ # Leave mostly blank for DG/UX
|
||||
+ major=
|
||||
+ versuffix=".$current.$revision";
|
||||
+ verstring=
|
||||
+ ;;
|
||||
+
|
||||
linux)
|
||||
major=.`expr $current - $age`
|
||||
versuffix="$major.$age.$revision"
|
||||
@@ -1792,8 +1832,9 @@
|
||||
|
||||
dependency_libs="$deplibs"
|
||||
case "$host" in
|
||||
- *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos*)
|
||||
+ *-*-cygwin* | *-*-mingw* | *-*-os2* | *-*-beos* | i[3456]86-*-dgux*)
|
||||
# these systems don't actually have a c library (as such)!
|
||||
+ # It is wrong in DG/UX to add -lc when creating shared/dynamic objs/libs
|
||||
;;
|
||||
*)
|
||||
# Add libc to deplibs on all other systems.
|
68
boehm-gc/doc/README.arm.cross
Normal file
68
boehm-gc/doc/README.arm.cross
Normal file
@ -0,0 +1,68 @@
|
||||
From: Margaret Fleck
|
||||
|
||||
Here's the key details of what worked for me, in case anyone else needs them.
|
||||
There may well be better ways to do some of this, but ....
|
||||
-- Margaret
|
||||
|
||||
|
||||
The badge4 has a StrongArm-1110 processor and a StrongArm-1111 coprocessor.
|
||||
|
||||
Assume that the garbage collector distribution is unpacked into /home/arm/gc6.0,
|
||||
which is visible to both the ARM machine and a linux desktop (e.g. via NFS mounting).
|
||||
|
||||
Assume that you have a file /home/arm/config.site with contents something like the
|
||||
example attached below. Notice that our local ARM toolchain lives in
|
||||
/skiff/local.
|
||||
|
||||
Go to /home/arm/gc6.0 directory. Do
|
||||
CONFIG_SITE=/home/arm/config.site ./configure --target=arm-linux
|
||||
--prefix=/home/arm/gc6.0
|
||||
|
||||
On your desktop, do:
|
||||
make
|
||||
make install
|
||||
The main garbage collector library should now be in ../gc6.0/lib/libgc.so.
|
||||
|
||||
To test the garbage collector, first do the following on your desktop
|
||||
make gctest
|
||||
./gctest
|
||||
Then do the following on the ARM machine
|
||||
cd .libs
|
||||
./lt-gctest
|
||||
|
||||
Do not try to do "make test" (the usual way of running the test
|
||||
program). This does not work and seems to erase some of the important
|
||||
files.
|
||||
|
||||
The gctest program claims to have succeeded. Haven't run any further tests
|
||||
with it, though I'll be doing so in the near future.
|
||||
|
||||
-------------------------------
|
||||
# config.site for configure
|
||||
|
||||
# Modified from the one provided by Bradley D. LaRonde
|
||||
# Edited by Andrej Cedilnik <acedil1@csee.umbc.edu>
|
||||
# Used some of solutions by Tilman Vogel <Tilman.Vogel@web.de>
|
||||
# Ported for iPAQ Familiar by Oliver Kurth <oliver.kurth@innominate.com>
|
||||
# Further modified by Margaret Fleck for the badge4
|
||||
|
||||
HOSTCC=gcc
|
||||
|
||||
# Names of the cross-compilers
|
||||
CC=/skiff/local/bin/arm-linux-gcc
|
||||
CXX=/skiff/local/bin/arm-linux-gcc
|
||||
|
||||
# The cross compiler specific options
|
||||
CFLAGS="-O2 -fno-exceptions"
|
||||
CXXFLAGS="-O2 -fno-exceptions"
|
||||
CPPFLAGS="-O2 -fno-exceptions"
|
||||
LDFLAGS=""
|
||||
|
||||
# Some other programs
|
||||
AR=/skiff/local/bin/arm-linux-ar
|
||||
RANLIB=/skiff/local/bin/arm-linux-ranlib
|
||||
NM=/skiff/local/bin/arm-linux-nm
|
||||
ac_cv_path_NM=/skiff/local/bin/arm-linux-nm
|
||||
ac_cv_func_setpgrp_void=yes
|
||||
x_includes=/skiff/local/arm-linux/include/X11
|
||||
x_libraries=/skiff/local/arm-linux/lib/X11
|
106
boehm-gc/doc/README.darwin
Normal file
106
boehm-gc/doc/README.darwin
Normal file
@ -0,0 +1,106 @@
|
||||
Darwin/MacOSX Support - July 22, 2003
|
||||
====================================
|
||||
|
||||
Important Usage Notes
|
||||
=====================
|
||||
|
||||
GC_init() MUST be called before calling any other GC functions. This
|
||||
is necessary to properly register segments in dynamic libraries. This
|
||||
call is required even if you code does not use dynamic libraries as the
|
||||
dyld code handles registering all data segments.
|
||||
|
||||
When your use of the garbage collector is confined to dylibs and you
|
||||
cannot call GC_init() before your libraries' static initializers have
|
||||
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(); }
|
||||
|
||||
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
|
||||
_my_library_init (e.g. gcc -dynamiclib -o my_library.dylib a.o b.o c.o
|
||||
my_library_init.o -init _my_library_init). This causes
|
||||
my_library_init() to be called before any static initializers, and
|
||||
will initialize the garbage collector properly.
|
||||
|
||||
Note: It doesn't hurt to call GC_init() more than once, so it's best,
|
||||
if you have an application or set of libraries that all use the
|
||||
garbage collector, to create an initialization routine for each of
|
||||
them that calls GC_init(). Better safe than sorry.
|
||||
|
||||
The incremental collector is still a bit flaky on darwin. It seems to
|
||||
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.
|
||||
|
||||
Implementation Information
|
||||
==========================
|
||||
Darwin/MacOSX support is nearly complete. Thread support is reliable on
|
||||
Darwin 6.x (MacOSX 10.2) and there have been reports of success on older
|
||||
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_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.
|
||||
|
||||
The original incremental collector support unfortunatelly no longer works
|
||||
on recent Darwin versions. It also relied on some undocumented kernel
|
||||
structures. Mach, however, does have a very clean interface to exception
|
||||
handing. The current implementation uses Mach's exception handling.
|
||||
|
||||
Much thanks goes to Andrew Stone, Dietmar Planitzer, Andrew Begel,
|
||||
Jeff Sturm, and Jesse Rosenstock for all their work on the
|
||||
Darwin/OS X port.
|
||||
|
||||
-Brian Alliet
|
||||
brian@brianweb.net
|
||||
|
||||
|
||||
Older Information (Most of this no longer applies to the current code)
|
||||
======================================================================
|
||||
|
||||
While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested
|
||||
it on MacOS X Server.
|
||||
I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is
|
||||
no longer necessary. Incremental collection is supported via mprotect/signal.
|
||||
The current solution isn't really optimal because the signal handler must decode
|
||||
the faulting PPC machine instruction in order to find the correct heap address.
|
||||
Further, it must poke around in the register state which the kernel saved away
|
||||
in some obscure register state structure before it calls the signal handler -
|
||||
needless to say the layout of this structure is no where documented.
|
||||
Threads and dynamic libraries are not yet supported (adding dynamic library
|
||||
support via the low-level dyld API shouldn't be that hard).
|
||||
|
||||
The original MacOS X port was brought to you by Andrew Stone.
|
||||
|
||||
|
||||
June, 1 2000
|
||||
|
||||
Dietmar Planitzer
|
||||
dave.pl@ping.at
|
||||
|
||||
Note from Andrew Begel:
|
||||
|
||||
One more fix to enable gc.a to link successfully into a shared library for
|
||||
MacOS X. You have to add -fno-common to the CFLAGS in the Makefile. MacOSX
|
||||
disallows common symbols in anything that eventually finds its way into a
|
||||
shared library. (I don't completely understand why, but -fno-common seems to
|
||||
work and doesn't mess up the garbage collector's functionality).
|
||||
|
||||
Feb 26, 2003
|
||||
|
||||
Jeff Sturm and Jesse Rosenstock provided a patch that adds thread support.
|
||||
GC_MACOSX_THREADS should be defined in the build and in clients. Real
|
||||
dynamic library support is still missing, i.e. dynamic library data segments
|
||||
are still not scanned. Code that stores pointers to the garbage collected
|
||||
heap in statically allocated variables should not reside in a dynamic
|
||||
library. This still doesn't appear to be 100% reliable.
|
||||
|
||||
Mar 10, 2003
|
||||
Brian Alliet contributed dynamic library support for MacOSX. It could also
|
||||
use more testing.
|
203
boehm-gc/doc/gcinterface.html
Normal file
203
boehm-gc/doc/gcinterface.html
Normal file
@ -0,0 +1,203 @@
|
||||
<!DOCTYPE HTML>
|
||||
<HEAD>
|
||||
<TITLE>Garbage Collector Interface</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<H1>C Interface</h1>
|
||||
On many platforms, a single-threaded garbage collector library can be built
|
||||
to act as a plug-in malloc replacement. (Build with -DREDIRECT_MALLOC=GC_malloc
|
||||
-DIGNORE_FREE.) This is often the best way to deal with third-party libraries
|
||||
which leak or prematurely free objects. -DREDIRECT_MALLOC is intended
|
||||
primarily as an easy way to adapt old code, not for new development.
|
||||
<P>
|
||||
New code should use the interface discussed below.
|
||||
<P>
|
||||
Code must be linked against the GC library. On most UNIX platforms,
|
||||
this will be gc.a.
|
||||
<P>
|
||||
The following describes the standard C interface to the garbage collector.
|
||||
It is not a complete definition of the interface. It describes only the
|
||||
most commonly used functionality, approximately in decreasing order of
|
||||
frequency of use. The description assumes an ANSI C compiler.
|
||||
The full interface is described in
|
||||
<A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>
|
||||
or <TT>gc.h</tt> in the distribution.
|
||||
<P>
|
||||
Clients should include gc.h.
|
||||
<P>
|
||||
In the case of multithreaded code,
|
||||
gc.h should be included after the threads header file, and
|
||||
after defining the appropriate GC_XXXX_THREADS macro.
|
||||
(For 6.2alpha4 and later, simply defining GC_THREADS should suffice.)
|
||||
Gc.h must be included
|
||||
in files that use either GC or threads primitives, since threads primitives
|
||||
will be redefined to cooperate with the GC on many platforms.
|
||||
<DL>
|
||||
<DT> <B>void * GC_MALLOC(size_t <I>nbytes</i>)</b>
|
||||
<DD>
|
||||
Allocates and clears <I>nbytes</i> of storage.
|
||||
Requires (amortized) time proportional to <I>nbytes</i>.
|
||||
The resulting object will be automatically deallocated when unreferenced.
|
||||
References from objects allocated with the system malloc are usually not
|
||||
considered by the collector. (See GC_MALLOC_UNCOLLECTABLE, however.)
|
||||
GC_MALLOC is a macro which invokes GC_malloc by default or, if GC_DEBUG
|
||||
is defined before gc.h is included, a debugging version that checks
|
||||
occasionally for overwrite errors, and the like.
|
||||
<DT> <B>void * GC_MALLOC_ATOMIC(size_t <I>nbytes</i>)</b>
|
||||
<DD>
|
||||
Allocates <I>nbytes</i> of storage.
|
||||
Requires (amortized) time proportional to <I>nbytes</i>.
|
||||
The resulting object will be automatically deallocated when unreferenced.
|
||||
The client promises that the resulting object will never contain any pointers.
|
||||
The memory is not cleared.
|
||||
This is the preferred way to allocate strings, floating point arrays,
|
||||
bitmaps, etc.
|
||||
More precise information about pointer locations can be communicated to the
|
||||
collector using the interface in
|
||||
<A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_typedh.txt">gc_typed.h</a> in the distribution.
|
||||
<DT> <B>void * GC_MALLOC_UNCOLLECTABLE(size_t <I>nbytes</i>)</b>
|
||||
<DD>
|
||||
Identical to GC_MALLOC, except that the resulting object is not automatically
|
||||
deallocated. Unlike the system-provided malloc, the collector does
|
||||
scan the object for pointers to garbage-collectable memory, even if the
|
||||
block itself does not appear to be reachable. (Objects allocated in this way
|
||||
are effectively treated as roots by the collector.)
|
||||
<DT> <B> void * GC_REALLOC(void *old, size_t new_size) </b>
|
||||
<DD>
|
||||
Allocate a new object of the indicated size and copy (a prefix of) the
|
||||
old object into the new object. The old object is reused in place if
|
||||
convenient. If the original object was allocated with GC_malloc_atomic,
|
||||
the new object is subject to the same constraints. If it was allocated
|
||||
as an uncollectable object, then the new object is uncollectable, and
|
||||
the old object (if different) is deallocated.
|
||||
(Use GC_REALLOC with GC_MALLOC, etc.)
|
||||
<DT> <B> void GC_FREE(void *dead) </b>
|
||||
<DD>
|
||||
Explicitly deallocate an object. Typically not useful for small
|
||||
collectable objects. (Use GC_FREE with GC_MALLOC, etc.)
|
||||
<DT> <B> void * GC_MALLOC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
|
||||
<DD>
|
||||
<DT> <B> void * GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
|
||||
<DD>
|
||||
Analogous to GC_MALLOC and GC_MALLOC_ATOMIC, except that the client
|
||||
guarantees that as long
|
||||
as the resulting object is of use, a pointer is maintained to someplace
|
||||
inside the first 512 bytes of the object. This pointer should be declared
|
||||
volatile to avoid interference from compiler optimizations.
|
||||
(Other nonvolatile pointers to the object may exist as well.)
|
||||
This is the
|
||||
preferred way to allocate objects that are likely to be > 100KBytes in size.
|
||||
It greatly reduces the risk that such objects will be accidentally retained
|
||||
when they are no longer needed. Thus space usage may be significantly reduced.
|
||||
<DT> <B> void GC_gcollect(void) </b>
|
||||
<DD>
|
||||
Explicitly force a garbage collection.
|
||||
<DT> <B> void GC_enable_incremental(void) </b>
|
||||
<DD>
|
||||
Cause the garbage collector to perform a small amount of work
|
||||
every few invocations of GC_malloc or the like, instead of performing
|
||||
an entire collection at once. This is likely to increase total
|
||||
running time. It will improve response on a platform that either has
|
||||
suitable support in the garbage collector (Irix and most other Unix
|
||||
versions, win32 if the collector was suitably built) or if "stubborn"
|
||||
allocation is used (see <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>).
|
||||
On many platforms this interacts poorly with system calls
|
||||
that write to the garbage collected heap.
|
||||
<DT> <B> GC_warn_proc GC_set_warn_proc(GC_warn_proc p) </b>
|
||||
<DD>
|
||||
Replace the default procedure used by the collector to print warnings.
|
||||
The collector
|
||||
may otherwise write to sterr, most commonly because GC_malloc was used
|
||||
in a situation in which GC_malloc_ignore_off_page would have been more
|
||||
appropriate. See <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a> for details.
|
||||
<DT> <B> void GC_register_finalizer(...) </b>
|
||||
<DD>
|
||||
Register a function to be called when an object becomes inaccessible.
|
||||
This is often useful as a backup method for releasing system resources
|
||||
(<I>e.g.</i> closing files) when the object referencing them becomes
|
||||
inaccessible.
|
||||
It is not an acceptable method to perform actions that must be performed
|
||||
in a timely fashion.
|
||||
See <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a> for details of the interface.
|
||||
See <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/finalization.html">here</a> for a more detailed discussion
|
||||
of the design.
|
||||
<P>
|
||||
Note that an object may become inaccessible before client code is done
|
||||
operating on its fields. Suitable synchronization is usually required.
|
||||
See <A HREF="http://portal.acm.org/citation.cfm?doid=604131.604153">here</a>
|
||||
or <A HREF="http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html">here</a>
|
||||
for details.
|
||||
</dl>
|
||||
<P>
|
||||
If you are concerned with multiprocessor performance and scalability,
|
||||
you should consider enabling and using thread local allocation (<I>e.g.</i>
|
||||
GC_LOCAL_MALLOC, see <TT>gc_local_alloc.h</tt>. If your platform
|
||||
supports it, you should build the collector with parallel marking support
|
||||
(-DPARALLEL_MARK, or --enable-parallel-mark).
|
||||
<P>
|
||||
If the collector is used in an environment in which pointer location
|
||||
information for heap objects is easily available, this can be passed on
|
||||
to the colllector using the interfaces in either <TT>gc_typed.h</tt>
|
||||
or <TT>gc_gcj.h</tt>.
|
||||
<P>
|
||||
The collector distribution also includes a <B>string package</b> that takes
|
||||
advantage of the collector. For details see
|
||||
<A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/cordh.txt">cord.h</a>
|
||||
|
||||
<H1>C++ Interface</h1>
|
||||
There are three distinct ways to use the collector from C++:
|
||||
<DL>
|
||||
<DT> <B> STL allocators </b>
|
||||
<DD>
|
||||
Users of the <A HREF="http://www.sgi.com/tech/stl">SGI extended STL</a>
|
||||
can include <TT>new_gc_alloc.h</tt> before including
|
||||
STL header files.
|
||||
(<TT>gc_alloc.h</tt> corresponds to now obsolete versions of the
|
||||
SGI STL.)
|
||||
This defines SGI-style allocators
|
||||
<UL>
|
||||
<LI> alloc
|
||||
<LI> single_client_alloc
|
||||
<LI> gc_alloc
|
||||
<LI> single_client_gc_alloc
|
||||
</ul>
|
||||
which may be used either directly to allocate memory or to instantiate
|
||||
container templates. The first two allocate uncollectable but traced
|
||||
memory, while the second two allocate collectable memory.
|
||||
The single_client versions are not safe for concurrent access by
|
||||
multiple threads, but are faster.
|
||||
<P>
|
||||
For an example, click <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_alloc_exC.txt">here</a>.
|
||||
<P>
|
||||
Recent versions of the collector also include a more standard-conforming
|
||||
allocator implemention in <TT>gc_allocator.h</tt>. It defines
|
||||
<UL>
|
||||
<LI> traceable_allocator
|
||||
<LI> gc_allocator
|
||||
</ul>
|
||||
Again the former allocates uncollectable but traced memory.
|
||||
This should work with any fully standard-conforming C++ compiler.
|
||||
<DT> <B> Class inheritance based interface </b>
|
||||
<DD>
|
||||
Users may include gc_cpp.h and then cause members of certain classes to
|
||||
be allocated in garbage collectable memory by inheriting from class gc.
|
||||
For details see <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_cpph.txt">gc_cpp.h</a>.
|
||||
<DT> <B> C interface </b>
|
||||
<DD>
|
||||
It is also possible to use the C interface from
|
||||
<A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a> directly.
|
||||
On platforms which use malloc to implement ::new, it should usually be possible
|
||||
to use a version of the collector that has been compiled as a malloc
|
||||
replacement. It is also possible to replace ::new and other allocation
|
||||
functions suitably.
|
||||
<P>
|
||||
Note that user-implemented small-block allocation often works poorly with
|
||||
an underlying garbage-collected large block allocator, since the collector
|
||||
has to view all objects accessible from the user's free list as reachable.
|
||||
This is likely to cause problems if GC_malloc is used with something like
|
||||
the original HP version of STL.
|
||||
This approach works with the SGI versions of the STL only if the
|
||||
<TT>malloc_alloc</tt> allocator is used.
|
||||
</dl>
|
||||
</body>
|
||||
</html>
|
197
boehm-gc/doc/leak.html
Normal file
197
boehm-gc/doc/leak.html
Normal file
@ -0,0 +1,197 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Using the Garbage Collector as Leak Detector</title>
|
||||
</head>
|
||||
<BODY>
|
||||
<H1>Using the Garbage Collector as Leak Detector</h1>
|
||||
The garbage collector may be used as a leak detector.
|
||||
In this case, the primary function of the collector is to report
|
||||
objects that were allocated (typically with <TT>GC_MALLOC</tt>),
|
||||
not deallocated (normally with <TT>GC_FREE</tt>), but are
|
||||
no longer accessible. Since the object is no longer accessible,
|
||||
there in normally no way to deallocate the object at a later time;
|
||||
thus it can safely be assumed that the object has been "leaked".
|
||||
<P>
|
||||
This is substantially different from counting leak detectors,
|
||||
which simply verify that all allocated objects are eventually
|
||||
deallocated. A garbage-collector based leak detector can provide
|
||||
somewhat more precise information when an object was leaked.
|
||||
More importantly, it does not report objects that are never
|
||||
deallocated because they are part of "permanent" data structures.
|
||||
Thus it does not require all objects to be deallocated at process
|
||||
exit time, a potentially useless activity that often triggers
|
||||
large amounts of paging.
|
||||
<P>
|
||||
All non-ancient versions of the garbage collector provide
|
||||
leak detection support. Version 5.3 adds the following
|
||||
features:
|
||||
<OL>
|
||||
<LI> Leak detection mode can be initiated at run-time by
|
||||
setting GC_find_leak instead of building the collector with FIND_LEAK
|
||||
defined. This variable should be set to a nonzero value
|
||||
at program startup.
|
||||
<LI> Leaked objects should be reported and then correctly garbage collected.
|
||||
Prior versions either reported leaks or functioned as a garbage collector.
|
||||
</ol>
|
||||
For the rest of this description we will give instructions that work
|
||||
with any reasonable version of the collector.
|
||||
<P>
|
||||
To use the collector as a leak detector, follow the following steps:
|
||||
<OL>
|
||||
<LI> Build the collector with -DFIND_LEAK. Otherwise use default
|
||||
build options.
|
||||
<LI> Change the program so that all allocation and deallocation goes
|
||||
through the garbage collector.
|
||||
<LI> Arrange to call <TT>GC_gcollect</tt> at appropriate points to check
|
||||
for leaks.
|
||||
(For sufficiently long running programs, this will happen implicitly,
|
||||
but probably not with sufficient frequency.)
|
||||
</ol>
|
||||
The second step can usually be accomplished with the
|
||||
<TT>-DREDIRECT_MALLOC=GC_malloc</tt> option when the collector is built,
|
||||
or by defining <TT>malloc</tt>, <TT>calloc</tt>,
|
||||
<TT>realloc</tt> and <TT>free</tt>
|
||||
to call the corresponding garbage collector functions.
|
||||
But this, by itself, will not yield very informative diagnostics,
|
||||
since the collector does not keep track of information about
|
||||
how objects were allocated. The error reports will include
|
||||
only object addresses.
|
||||
<P>
|
||||
For more precise error reports, as much of the program as possible
|
||||
should use the all uppercase variants of these functions, after
|
||||
defining <TT>GC_DEBUG</tt>, and then including <TT>gc.h</tt>.
|
||||
In this environment <TT>GC_MALLOC</tt> is a macro which causes
|
||||
at least the file name and line number at the allocation point to
|
||||
be saved as part of the object. Leak reports will then also include
|
||||
this information.
|
||||
<P>
|
||||
Many collector features (<I>e.g</i> stubborn objects, finalization,
|
||||
and disappearing links) are less useful in this context, and are not
|
||||
fully supported. Their use will usually generate additional bogus
|
||||
leak reports, since the collector itself drops some associated objects.
|
||||
<P>
|
||||
The same is generally true of thread support. However, as of 6.0alpha4,
|
||||
correct leak reports should be generated with linuxthreads.
|
||||
<P>
|
||||
On a few platforms (currently Solaris/SPARC, Irix, and, with -DSAVE_CALL_CHAIN,
|
||||
Linux/X86), <TT>GC_MALLOC</tt>
|
||||
also causes some more information about its call stack to be saved
|
||||
in the object. Such information is reproduced in the error
|
||||
reports in very non-symbolic form, but it can be very useful with the
|
||||
aid of a debugger.
|
||||
<H2>An Example</h2>
|
||||
The following header file <TT>leak_detector.h</tt> is included in the
|
||||
"include" subdirectory of the distribution:
|
||||
<PRE>
|
||||
#define GC_DEBUG
|
||||
#include "gc.h"
|
||||
#define malloc(n) GC_MALLOC(n)
|
||||
#define calloc(m,n) GC_MALLOC((m)*(n))
|
||||
#define free(p) GC_FREE(p)
|
||||
#define realloc(p,n) GC_REALLOC((p),(n))
|
||||
#define CHECK_LEAKS() GC_gcollect()
|
||||
</pre>
|
||||
<P>
|
||||
Assume the collector has been built with -DFIND_LEAK. (For very
|
||||
new versions of the collector, we could instead add the statement
|
||||
<TT>GC_find_leak = 1</tt> as the first statement in <TT>main</tt>.
|
||||
<P>
|
||||
The program to be tested for leaks can then look like:
|
||||
<PRE>
|
||||
#include "leak_detector.h"
|
||||
|
||||
main() {
|
||||
int *p[10];
|
||||
int i;
|
||||
/* GC_find_leak = 1; for new collector versions not */
|
||||
/* compiled with -DFIND_LEAK. */
|
||||
for (i = 0; i < 10; ++i) {
|
||||
p[i] = malloc(sizeof(int)+i);
|
||||
}
|
||||
for (i = 1; i < 10; ++i) {
|
||||
free(p[i]);
|
||||
}
|
||||
for (i = 0; i < 9; ++i) {
|
||||
p[i] = malloc(sizeof(int)+i);
|
||||
}
|
||||
CHECK_LEAKS();
|
||||
}
|
||||
</pre>
|
||||
<P>
|
||||
On an Intel X86 Linux system this produces on the stderr stream:
|
||||
<PRE>
|
||||
Leaked composite object at 0x806dff0 (leak_test.c:8, sz=4)
|
||||
</pre>
|
||||
(On most unmentioned operating systems, the output is similar to this.
|
||||
If the collector had been built on Linux/X86 with -DSAVE_CALL_CHAIN,
|
||||
the output would be closer to the Solaris example. For this to work,
|
||||
the program should not be compiled with -fomit_frame_pointer.)
|
||||
<P>
|
||||
On Irix it reports
|
||||
<PRE>
|
||||
Leaked composite object at 0x10040fe0 (leak_test.c:8, sz=4)
|
||||
Caller at allocation:
|
||||
##PC##= 0x10004910
|
||||
</pre>
|
||||
and on Solaris the error report is
|
||||
<PRE>
|
||||
Leaked composite object at 0xef621fc8 (leak_test.c:8, sz=4)
|
||||
Call chain at allocation:
|
||||
args: 4 (0x4), 200656 (0x30FD0)
|
||||
##PC##= 0x14ADC
|
||||
args: 1 (0x1), -268436012 (0xEFFFFDD4)
|
||||
##PC##= 0x14A64
|
||||
</pre>
|
||||
In the latter two cases some additional information is given about
|
||||
how malloc was called when the leaked object was allocated. For
|
||||
Solaris, the first line specifies the arguments to <TT>GC_debug_malloc</tt>
|
||||
(the actual allocation routine), The second the program counter inside
|
||||
main, the third the arguments to <TT>main</tt>, and finally the program
|
||||
counter inside the caller to main (i.e. in the C startup code).
|
||||
<P>
|
||||
In the Irix case, only the address inside the caller to main is given.
|
||||
<P>
|
||||
In many cases, a debugger is needed to interpret the additional information.
|
||||
On systems supporting the "adb" debugger, the <TT>callprocs</tt> script
|
||||
can be used to replace program counter values with symbolic names.
|
||||
As of version 6.1, the collector tries to generate symbolic names for
|
||||
call stacks if it knows how to do so on the platform. This is true on
|
||||
Linux/X86, but not on most other platforms.
|
||||
<H2>Simplified leak detection under Linux</h2>
|
||||
Since version 6.1, it should be possible to run the collector in leak
|
||||
detection mode on a program a.out under Linux/X86 as follows:
|
||||
<OL>
|
||||
<LI> Ensure that a.out is a single-threaded executable. This doesn't yet work
|
||||
for multithreaded programs.
|
||||
<LI> If possible, ensure that the addr2line program is installed in
|
||||
/usr/bin. (It comes with RedHat Linux.)
|
||||
<LI> If possible, compile a.out with full debug information.
|
||||
This will improve the quality of the leak reports. With this approach, it is
|
||||
no longer necessary to call GC_ routines explicitly, though that can also
|
||||
improve the quality of the leak reports.
|
||||
<LI> Build the collector and install it in directory <I>foo</i> as follows:
|
||||
<UL>
|
||||
<LI> configure --prefix=<I>foo</i> --enable-full-debug --enable-redirect-malloc
|
||||
--disable-threads
|
||||
<LI> make
|
||||
<LI> make install
|
||||
</ul>
|
||||
<LI> Set environment variables as follows:
|
||||
<UL>
|
||||
<LI> LD_PRELOAD=<I>foo</i>/lib/libgc.so
|
||||
<LI> GC_FIND_LEAK
|
||||
<LI> You may also want to set GC_PRINT_STATS (to confirm that the collector
|
||||
is running) and/or GC_LOOP_ON_ABORT (to facilitate debugging from another
|
||||
window if something goes wrong).
|
||||
</ul
|
||||
<LI> Simply run a.out as you normally would. Note that if you run anything
|
||||
else (<I>e.g.</i> your editor) with those environment variables set,
|
||||
it will also be leak tested. This may or may not be useful and/or
|
||||
embarrassing. It can generate
|
||||
mountains of leak reports if the application wasn't designed to avoid leaks,
|
||||
<I>e.g.</i> because it's always short-lived.
|
||||
</ol>
|
||||
This has not yet been thropughly tested on large applications, but it's known
|
||||
to do the right thing on at least some small ones.
|
||||
</body>
|
||||
</html>
|
210
boehm-gc/doc/scale.html
Normal file
210
boehm-gc/doc/scale.html
Normal file
@ -0,0 +1,210 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Garbage collector scalability</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<H1>Garbage collector scalability</h1>
|
||||
In its default configuration, the Boehm-Demers-Weiser garbage collector
|
||||
is not thread-safe. It can be made thread-safe for a number of environments
|
||||
by building the collector with the appropriate
|
||||
<TT>-D</tt><I>XXX</i><TT>-THREADS</tt> compilation
|
||||
flag. This has primarily two effects:
|
||||
<OL>
|
||||
<LI> It causes the garbage collector to stop all other threads when
|
||||
it needs to see a consistent memory state.
|
||||
<LI> It causes the collector to acquire a lock around essentially all
|
||||
allocation and garbage collection activity.
|
||||
</ol>
|
||||
Since a single lock is used for all allocation-related activity, only one
|
||||
thread can be allocating or collecting at one point. This inherently
|
||||
limits performance of multi-threaded applications on multiprocessors.
|
||||
<P>
|
||||
On most platforms, the allocator/collector lock is implemented as a
|
||||
spin lock with exponential back-off. Longer wait times are implemented
|
||||
by yielding and/or sleeping. If a collection is in progress, the pure
|
||||
spinning stage is skipped. This has the advantage that uncontested and
|
||||
thus most uniprocessor lock acquisitions are very cheap. It has the
|
||||
disadvantage that the application may sleep for small periods of time
|
||||
even when there is work to be done. And threads may be unnecessarily
|
||||
woken up for short periods. Nonetheless, this scheme empirically
|
||||
outperforms native queue-based mutual exclusion implementations in most
|
||||
cases, sometimes drastically so.
|
||||
<H2>Options for enhanced scalability</h2>
|
||||
Version 6.0 of the collector adds two facilities to enhance collector
|
||||
scalability on multiprocessors. As of 6.0alpha1, these are supported
|
||||
only under Linux on X86 and IA64 processors, though ports to other
|
||||
otherwise supported Pthreads platforms should be straightforward.
|
||||
They are intended to be used together.
|
||||
<UL>
|
||||
<LI>
|
||||
Building the collector with <TT>-DPARALLEL_MARK</tt> allows the collector to
|
||||
run the mark phase in parallel in multiple threads, and thus on multiple
|
||||
processors. The mark phase typically consumes the large majority of the
|
||||
collection time. Thus this largely parallelizes the garbage collector
|
||||
itself, though not the allocation process. Currently the marking is
|
||||
performed by the thread that triggered the collection, together with
|
||||
<I>N</i>-1 dedicated
|
||||
threads, where <I>N</i> is the number of processors detected by the collector.
|
||||
The dedicated threads are created once at initialization time.
|
||||
<P>
|
||||
A second effect of this flag is to switch to a more concurrent
|
||||
implementation of <TT>GC_malloc_many</tt>, so that free lists can be
|
||||
built, and memory can be cleared, by more than one thread concurrently.
|
||||
<LI>
|
||||
Building the collector with -DTHREAD_LOCAL_ALLOC adds support for thread
|
||||
local allocation. It does not, by itself, cause thread local allocation
|
||||
to be used. It simply allows the use of the interface in
|
||||
<TT>gc_local_alloc.h</tt>.
|
||||
<P>
|
||||
Memory returned from thread-local allocators is completely interchangeable
|
||||
with that returned by the standard allocators. It may be used by other
|
||||
threads. The only difference is that, if the thread allocates enough
|
||||
memory of a certain kind, it will build a thread-local free list for
|
||||
objects of that kind, and allocate from that. This greatly reduces
|
||||
locking. The thread-local free lists are refilled using
|
||||
<TT>GC_malloc_many</tt>.
|
||||
<P>
|
||||
An important side effect of this flag is to replace the default
|
||||
spin-then-sleep lock to be replace by a spin-then-queue based implementation.
|
||||
This <I>reduces performance</i> for the standard allocation functions,
|
||||
though it usually improves performance when thread-local allocation is
|
||||
used heavily, and thus the number of short-duration lock acquisitions
|
||||
is greatly reduced.
|
||||
</ul>
|
||||
<P>
|
||||
The easiest way to switch an application to thread-local allocation is to
|
||||
<OL>
|
||||
<LI> Define the macro <TT>GC_REDIRECT_TO_LOCAL</tt>,
|
||||
and then include the <TT>gc.h</tt>
|
||||
header in each client source file.
|
||||
<LI> Invoke <TT>GC_thr_init()</tt> before any allocation.
|
||||
<LI> Allocate using <TT>GC_MALLOC</tt>, <TT>GC_MALLOC_ATOMIC</tt>,
|
||||
and/or <TT>GC_GCJ_MALLOC</tt>.
|
||||
</ol>
|
||||
<H2>The Parallel Marking Algorithm</h2>
|
||||
We use an algorithm similar to
|
||||
<A HREF="http://www.yl.is.s.u-tokyo.ac.jp/gc/">that developed by
|
||||
Endo, Taura, and Yonezawa</a> at the University of Tokyo.
|
||||
However, the data structures and implementation are different,
|
||||
and represent a smaller change to the original collector source,
|
||||
probably at the expense of extreme scalability. Some of
|
||||
the refinements they suggest, <I>e.g.</i> splitting large
|
||||
objects, were also incorporated into out approach.
|
||||
<P>
|
||||
The global mark stack is transformed into a global work queue.
|
||||
Unlike the usual case, it never shrinks during a mark phase.
|
||||
The mark threads remove objects from the queue by copying them to a
|
||||
local mark stack and changing the global descriptor to zero, indicating
|
||||
that there is no more work to be done for this entry.
|
||||
This removal
|
||||
is done with no synchronization. Thus it is possible for more than
|
||||
one worker to remove the same entry, resulting in some work duplication.
|
||||
<P>
|
||||
The global work queue grows only if a marker thread decides to
|
||||
return some of its local mark stack to the global one. This
|
||||
is done if the global queue appears to be running low, or if
|
||||
the local stack is in danger of overflowing. It does require
|
||||
synchronization, but should be relatively rare.
|
||||
<P>
|
||||
The sequential marking code is reused to process local mark stacks.
|
||||
Hence the amount of additional code required for parallel marking
|
||||
is minimal.
|
||||
<P>
|
||||
It should be possible to use generational collection in the presence of the
|
||||
parallel collector, by calling <TT>GC_enable_incremental()</tt>.
|
||||
This does not result in fully incremental collection, since parallel mark
|
||||
phases cannot currently be interrupted, and doing so may be too
|
||||
expensive.
|
||||
<P>
|
||||
Gcj-style mark descriptors do not currently mix with the combination
|
||||
of local allocation and incremental collection. They should work correctly
|
||||
with one or the other, but not both.
|
||||
<P>
|
||||
The number of marker threads is set on startup to the number of
|
||||
available processors (or to the value of the <TT>GC_NPROCS</tt>
|
||||
environment variable). If only a single processor is detected,
|
||||
parallel marking is disabled.
|
||||
<P>
|
||||
Note that setting GC_NPROCS to 1 also causes some lock acquisitions inside
|
||||
the collector to immediately yield the processor instead of busy waiting
|
||||
first. In the case of a multiprocessor and a client with multiple
|
||||
simultaneously runnable threads, this may have disastrous performance
|
||||
consequences (e.g. a factor of 10 slowdown).
|
||||
<H2>Performance</h2>
|
||||
We conducted some simple experiments with a version of
|
||||
<A HREF="gc_bench.html">our GC benchmark</a> that was slightly modified to
|
||||
run multiple concurrent client threads in the same address space.
|
||||
Each client thread does the same work as the original benchmark, but they share
|
||||
a heap.
|
||||
This benchmark involves very little work outside of memory allocation.
|
||||
This was run with GC 6.0alpha3 on a dual processor Pentium III/500 machine
|
||||
under Linux 2.2.12.
|
||||
<P>
|
||||
Running with a thread-unsafe collector, the benchmark ran in 9
|
||||
seconds. With the simple thread-safe collector,
|
||||
built with <TT>-DLINUX_THREADS</tt>, the execution time
|
||||
increased to 10.3 seconds, or 23.5 elapsed seconds with two clients.
|
||||
(The times for the <TT>malloc</tt>/i<TT>free</tt> version
|
||||
with glibc <TT>malloc</tt>
|
||||
are 10.51 (standard library, pthreads not linked),
|
||||
20.90 (one thread, pthreads linked),
|
||||
and 24.55 seconds respectively. The benchmark favors a
|
||||
garbage collector, since most objects are small.)
|
||||
<P>
|
||||
The following table gives execution times for the collector built
|
||||
with parallel marking and thread-local allocation support
|
||||
(<TT>-DGC_LINUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC</tt>). We tested
|
||||
the client using either one or two marker threads, and running
|
||||
one or two client threads. Note that the client uses thread local
|
||||
allocation exclusively. With -DTHREAD_LOCAL_ALLOC the collector
|
||||
switches to a locking strategy that is better tuned to less frequent
|
||||
lock acquisition. The standard allocation primitives thus peform
|
||||
slightly worse than without -DTHREAD_LOCAL_ALLOC, and should be
|
||||
avoided in time-critical code.
|
||||
<P>
|
||||
(The results using <TT>pthread_mutex_lock</tt>
|
||||
directly for allocation locking would have been worse still, at
|
||||
least for older versions of linuxthreads.
|
||||
With THREAD_LOCAL_ALLOC, we first repeatedly try to acquire the
|
||||
lock with pthread_mutex_try_lock(), busy_waiting between attempts.
|
||||
After a fixed number of attempts, we use pthread_mutex_lock().)
|
||||
<P>
|
||||
These measurements do not use incremental collection, nor was prefetching
|
||||
enabled in the marker. We used the C version of the benchmark.
|
||||
All measurements are in elapsed seconds on an unloaded machine.
|
||||
<P>
|
||||
<TABLE BORDER ALIGN="CENTER">
|
||||
<TR><TH>Number of threads</th><TH>1 marker thread (secs.)</th>
|
||||
<TH>2 marker threads (secs.)</th></tr>
|
||||
<TR><TD>1 client</td><TD ALIGN="CENTER">10.45</td><TD ALIGN="CENTER">7.85</td>
|
||||
<TR><TD>2 clients</td><TD ALIGN="CENTER">19.95</td><TD ALIGN="CENTER">12.3</td>
|
||||
</table>
|
||||
<PP>
|
||||
The execution time for the single threaded case is slightly worse than with
|
||||
simple locking. However, even the single-threaded benchmark runs faster than
|
||||
even the thread-unsafe version if a second processor is available.
|
||||
The execution time for two clients with thread local allocation time is
|
||||
only 1.4 times the sequential execution time for a single thread in a
|
||||
thread-unsafe environment, even though it involves twice the client work.
|
||||
That represents close to a
|
||||
factor of 2 improvement over the 2 client case with the old collector.
|
||||
The old collector clearly
|
||||
still suffered from some contention overhead, in spite of the fact that the
|
||||
locking scheme had been fairly well tuned.
|
||||
<P>
|
||||
Full linear speedup (i.e. the same execution time for 1 client on one
|
||||
processor as 2 clients on 2 processors)
|
||||
is probably not achievable on this kind of
|
||||
hardware even with such a small number of processors,
|
||||
since the memory system is
|
||||
a major constraint for the garbage collector,
|
||||
the processors usually share a single memory bus, and thus
|
||||
the aggregate memory bandwidth does not increase in
|
||||
proportion to the number of processors.
|
||||
<P>
|
||||
These results are likely to be very sensitive to both hardware and OS
|
||||
issues. Preliminary experiments with an older Pentium Pro machine running
|
||||
an older kernel were far less encouraging.
|
||||
|
||||
</body>
|
||||
</html>
|
232
boehm-gc/include/gc_allocator.h
Normal file
232
boehm-gc/include/gc_allocator.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 1996-1997
|
||||
* Silicon Graphics Computer Systems, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and
|
||||
* that both that copyright notice and this permission notice appear
|
||||
* in supporting documentation. Silicon Graphics makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* Copyright (c) 2002
|
||||
* Hewlett-Packard Company
|
||||
*
|
||||
* Permission to use, copy, modify, distribute and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and
|
||||
* that both that copyright notice and this permission notice appear
|
||||
* in supporting documentation. Hewlett-Packard Company makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This implements standard-conforming allocators that interact with
|
||||
* the garbage collector. Gc_alloctor<T> allocates garbage-collectable
|
||||
* objects of type T. Traceable_allocator<T> allocates objects that
|
||||
* are not temselves garbage collected, but are scanned by the
|
||||
* collector for pointers to collectable objects. Traceable_alloc
|
||||
* should be used for explicitly managed STL containers that may
|
||||
* point to collectable objects.
|
||||
*
|
||||
* This code was derived from an earlier version of the GNU C++ standard
|
||||
* library, which itself was derived from the SGI STL implementation.
|
||||
*/
|
||||
|
||||
#include "gc.h" // For size_t
|
||||
|
||||
/* First some helpers to allow us to dispatch on whether or not a type
|
||||
* is known to be pointerfree.
|
||||
* These are private, except that the client may invoke the
|
||||
* GC_DECLARE_PTRFREE macro.
|
||||
*/
|
||||
|
||||
struct GC_true_type {};
|
||||
struct GC_false_type {};
|
||||
|
||||
template <class GC_tp>
|
||||
struct GC_type_traits {
|
||||
GC_false_type GC_is_ptr_free;
|
||||
};
|
||||
|
||||
# define GC_DECLARE_PTRFREE(T) \
|
||||
template<> struct GC_type_traits<T> { GC_true_type GC_is_ptr_free; }
|
||||
|
||||
GC_DECLARE_PTRFREE(signed char);
|
||||
GC_DECLARE_PTRFREE(unsigned char);
|
||||
GC_DECLARE_PTRFREE(signed short);
|
||||
GC_DECLARE_PTRFREE(unsigned short);
|
||||
GC_DECLARE_PTRFREE(signed int);
|
||||
GC_DECLARE_PTRFREE(unsigned int);
|
||||
GC_DECLARE_PTRFREE(signed long);
|
||||
GC_DECLARE_PTRFREE(unsigned long);
|
||||
GC_DECLARE_PTRFREE(float);
|
||||
GC_DECLARE_PTRFREE(double);
|
||||
/* The client may want to add others. */
|
||||
|
||||
// In the following GC_Tp is GC_true_type iff we are allocating a
|
||||
// pointerfree object.
|
||||
template <class GC_Tp>
|
||||
inline void * GC_selective_alloc(size_t n, GC_Tp) {
|
||||
return GC_MALLOC(n);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void * GC_selective_alloc<GC_true_type>(size_t n, GC_true_type) {
|
||||
return GC_MALLOC_ATOMIC(n);
|
||||
}
|
||||
|
||||
/* Now the public gc_allocator<T> class:
|
||||
*/
|
||||
template <class GC_Tp>
|
||||
class gc_allocator {
|
||||
public:
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef GC_Tp* pointer;
|
||||
typedef const GC_Tp* const_pointer;
|
||||
typedef GC_Tp& reference;
|
||||
typedef const GC_Tp& const_reference;
|
||||
typedef GC_Tp value_type;
|
||||
|
||||
template <class GC_Tp1> struct rebind {
|
||||
typedef gc_allocator<GC_Tp1> other;
|
||||
};
|
||||
|
||||
gc_allocator() {}
|
||||
# ifndef _MSC_VER
|
||||
// I'm not sure why this is needed here in addition to the following.
|
||||
// The standard specifies it for the standard allocator, but VC++ rejects
|
||||
// it. -HB
|
||||
gc_allocator(const gc_allocator&) throw() {}
|
||||
# endif
|
||||
template <class GC_Tp1> gc_allocator(const gc_allocator<GC_Tp1>&) throw() {}
|
||||
~gc_allocator() throw() {}
|
||||
|
||||
pointer address(reference GC_x) const { return &GC_x; }
|
||||
const_pointer address(const_reference GC_x) const { return &GC_x; }
|
||||
|
||||
// GC_n is permitted to be 0. The C++ standard says nothing about what
|
||||
// the return value is when GC_n == 0.
|
||||
GC_Tp* allocate(size_type GC_n, const void* = 0) {
|
||||
GC_type_traits<GC_Tp> traits;
|
||||
return static_cast<GC_Tp *>
|
||||
(GC_selective_alloc(GC_n * sizeof(GC_Tp),
|
||||
traits.GC_is_ptr_free));
|
||||
}
|
||||
|
||||
// __p is not permitted to be a null pointer.
|
||||
void deallocate(pointer __p, size_type GC_n)
|
||||
{ GC_FREE(__p); }
|
||||
|
||||
size_type max_size() const throw()
|
||||
{ return size_t(-1) / sizeof(GC_Tp); }
|
||||
|
||||
void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
|
||||
void destroy(pointer __p) { __p->~GC_Tp(); }
|
||||
};
|
||||
|
||||
template<>
|
||||
class gc_allocator<void> {
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef void* pointer;
|
||||
typedef const void* const_pointer;
|
||||
typedef void value_type;
|
||||
|
||||
template <class GC_Tp1> struct rebind {
|
||||
typedef gc_allocator<GC_Tp1> other;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class GC_T1, class GC_T2>
|
||||
inline bool operator==(const gc_allocator<GC_T1>&, const gc_allocator<GC_T2>&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class GC_T1, class GC_T2>
|
||||
inline bool operator!=(const gc_allocator<GC_T1>&, const gc_allocator<GC_T2>&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* And the public traceable_allocator class.
|
||||
*/
|
||||
|
||||
// Note that we currently don't specialize the pointer-free case, since a
|
||||
// pointer-free traceable container doesn't make that much sense,
|
||||
// though it could become an issue due to abstraction boundaries.
|
||||
template <class GC_Tp>
|
||||
class traceable_allocator {
|
||||
public:
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef GC_Tp* pointer;
|
||||
typedef const GC_Tp* const_pointer;
|
||||
typedef GC_Tp& reference;
|
||||
typedef const GC_Tp& const_reference;
|
||||
typedef GC_Tp value_type;
|
||||
|
||||
template <class GC_Tp1> struct rebind {
|
||||
typedef traceable_allocator<GC_Tp1> other;
|
||||
};
|
||||
|
||||
traceable_allocator() throw() {}
|
||||
# ifndef _MSC_VER
|
||||
traceable_allocator(const traceable_allocator&) throw() {}
|
||||
# endif
|
||||
template <class GC_Tp1> traceable_allocator
|
||||
(const traceable_allocator<GC_Tp1>&) throw() {}
|
||||
~traceable_allocator() throw() {}
|
||||
|
||||
pointer address(reference GC_x) const { return &GC_x; }
|
||||
const_pointer address(const_reference GC_x) const { return &GC_x; }
|
||||
|
||||
// GC_n is permitted to be 0. The C++ standard says nothing about what
|
||||
// the return value is when GC_n == 0.
|
||||
GC_Tp* allocate(size_type GC_n, const void* = 0) {
|
||||
return static_cast<GC_Tp*>(GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)));
|
||||
}
|
||||
|
||||
// __p is not permitted to be a null pointer.
|
||||
void deallocate(pointer __p, size_type GC_n)
|
||||
{ GC_FREE(__p); }
|
||||
|
||||
size_type max_size() const throw()
|
||||
{ return size_t(-1) / sizeof(GC_Tp); }
|
||||
|
||||
void construct(pointer __p, const GC_Tp& __val) { new(__p) GC_Tp(__val); }
|
||||
void destroy(pointer __p) { __p->~GC_Tp(); }
|
||||
};
|
||||
|
||||
template<>
|
||||
class traceable_allocator<void> {
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef void* pointer;
|
||||
typedef const void* const_pointer;
|
||||
typedef void value_type;
|
||||
|
||||
template <class GC_Tp1> struct rebind {
|
||||
typedef traceable_allocator<GC_Tp1> other;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class GC_T1, class GC_T2>
|
||||
inline bool operator==(const traceable_allocator<GC_T1>&, const traceable_allocator<GC_T2>&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class GC_T1, class GC_T2>
|
||||
inline bool operator!=(const traceable_allocator<GC_T1>&, const traceable_allocator<GC_T2>&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
147
boehm-gc/include/gc_config_macros.h
Normal file
147
boehm-gc/include/gc_config_macros.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This should never be included directly. It is included only from gc.h.
|
||||
* We separate it only to make gc.h more suitable as documentation.
|
||||
*
|
||||
* Some tests for old macros. These violate our namespace rules and will
|
||||
* disappear shortly. Use the GC_ names.
|
||||
*/
|
||||
#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS)
|
||||
# define GC_SOLARIS_THREADS
|
||||
#endif
|
||||
#if defined(_SOLARIS_PTHREADS)
|
||||
# define GC_SOLARIS_PTHREADS
|
||||
#endif
|
||||
#if defined(IRIX_THREADS)
|
||||
# define GC_IRIX_THREADS
|
||||
#endif
|
||||
#if defined(DGUX_THREADS)
|
||||
# if !defined(GC_DGUX386_THREADS)
|
||||
# define GC_DGUX386_THREADS
|
||||
# endif
|
||||
#endif
|
||||
#if defined(AIX_THREADS)
|
||||
# define GC_AIX_THREADS
|
||||
#endif
|
||||
#if defined(HPUX_THREADS)
|
||||
# define GC_HPUX_THREADS
|
||||
#endif
|
||||
#if defined(OSF1_THREADS)
|
||||
# define GC_OSF1_THREADS
|
||||
#endif
|
||||
#if defined(LINUX_THREADS)
|
||||
# define GC_LINUX_THREADS
|
||||
#endif
|
||||
#if defined(WIN32_THREADS)
|
||||
# define GC_WIN32_THREADS
|
||||
#endif
|
||||
#if defined(USE_LD_WRAP)
|
||||
# define GC_USE_LD_WRAP
|
||||
#endif
|
||||
|
||||
#if !defined(_REENTRANT) && (defined(GC_SOLARIS_THREADS) \
|
||||
|| defined(GC_SOLARIS_PTHREADS) \
|
||||
|| defined(GC_HPUX_THREADS) \
|
||||
|| defined(GC_AIX_THREADS) \
|
||||
|| defined(GC_LINUX_THREADS))
|
||||
# define _REENTRANT
|
||||
/* Better late than never. This fails if system headers that */
|
||||
/* depend on this were previously included. */
|
||||
#endif
|
||||
|
||||
#if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE)
|
||||
# define _POSIX4A_DRAFT10_SOURCE 1
|
||||
#endif
|
||||
|
||||
# if defined(GC_SOLARIS_PTHREADS) || defined(GC_FREEBSD_THREADS) || \
|
||||
defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) || \
|
||||
defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) || \
|
||||
defined(GC_DGUX386_THREADS) || defined(GC_DARWIN_THREADS) || \
|
||||
defined(GC_AIX_THREADS) || \
|
||||
(defined(GC_WIN32_THREADS) && defined(__CYGWIN32__))
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
|
||||
#if defined(GC_THREADS) && !defined(GC_PTHREADS)
|
||||
# if defined(__linux__)
|
||||
# define GC_LINUX_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if !defined(LINUX) && (defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \
|
||||
|| defined(hppa) || defined(__HPPA))
|
||||
# define GC_HPUX_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if !defined(__linux__) && (defined(__alpha) || defined(__alpha__))
|
||||
# define GC_OSF1_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if defined(__mips) && !defined(__linux__)
|
||||
# define GC_IRIX_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if defined(__sparc) && !defined(__linux__)
|
||||
# define GC_SOLARIS_PTHREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if defined(__APPLE__) && defined(__MACH__) && defined(__ppc__)
|
||||
# define GC_DARWIN_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if !defined(GC_PTHREADS) && defined(__FreeBSD__)
|
||||
# define GC_FREEBSD_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
# if defined(DGUX) && (defined(i386) || defined(__i386__))
|
||||
# define GC_DGUX386_THREADS
|
||||
# define GC_PTHREADS
|
||||
# endif
|
||||
#endif /* GC_THREADS */
|
||||
|
||||
#if defined(GC_THREADS) && !defined(GC_PTHREADS) && defined(MSWIN32)
|
||||
# define GC_WIN32_THREADS
|
||||
#endif
|
||||
|
||||
#if defined(GC_SOLARIS_PTHREADS) && !defined(GC_SOLARIS_THREADS)
|
||||
# define GC_SOLARIS_THREADS
|
||||
#endif
|
||||
|
||||
# define __GC
|
||||
# include <stddef.h>
|
||||
# ifdef _WIN32_WCE
|
||||
/* Yet more kluges for WinCE */
|
||||
# include <stdlib.h> /* size_t is defined here */
|
||||
typedef long ptrdiff_t; /* ptrdiff_t is not defined */
|
||||
# endif
|
||||
|
||||
#if defined(_DLL) && !defined(GC_NOT_DLL) && !defined(GC_DLL)
|
||||
# define GC_DLL
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) && defined(GC_DLL)
|
||||
# ifdef GC_BUILD
|
||||
# define GC_API __declspec(dllexport)
|
||||
# else
|
||||
# define GC_API __declspec(dllimport)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if (defined(__DMC__) || defined(_MSC_VER)) && defined(GC_DLL)
|
||||
# ifdef GC_BUILD
|
||||
# define GC_API extern __declspec(dllexport)
|
||||
# else
|
||||
# define GC_API __declspec(dllimport)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__WATCOMC__) && defined(GC_DLL)
|
||||
# ifdef GC_BUILD
|
||||
# define GC_API extern __declspec(dllexport)
|
||||
# else
|
||||
# define GC_API extern __declspec(dllimport)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef GC_API
|
||||
#define GC_API extern
|
||||
#endif
|
||||
|
68
boehm-gc/include/private/darwin_semaphore.h
Normal file
68
boehm-gc/include/private/darwin_semaphore.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef GC_DARWIN_SEMAPHORE_H
|
||||
#define GC_DARWIN_SEMAPHORE_H
|
||||
|
||||
#if !defined(GC_DARWIN_THREADS)
|
||||
#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
|
||||
#endif
|
||||
|
||||
/*
|
||||
This is a very simple semaphore implementation for darwin. It
|
||||
is implemented in terms of pthreads calls so it isn't async signal
|
||||
safe. This isn't a problem because signals aren't used to
|
||||
suspend threads on darwin.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
int value;
|
||||
} sem_t;
|
||||
|
||||
static int sem_init(sem_t *sem, int pshared, int value) {
|
||||
int ret;
|
||||
if(pshared)
|
||||
GC_abort("sem_init with pshared set");
|
||||
sem->value = value;
|
||||
|
||||
ret = pthread_mutex_init(&sem->mutex,NULL);
|
||||
if(ret < 0) return -1;
|
||||
ret = pthread_cond_init(&sem->cond,NULL);
|
||||
if(ret < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sem_post(sem_t *sem) {
|
||||
if(pthread_mutex_lock(&sem->mutex) < 0)
|
||||
return -1;
|
||||
sem->value++;
|
||||
if(pthread_cond_signal(&sem->cond) < 0) {
|
||||
pthread_mutex_unlock(&sem->mutex);
|
||||
return -1;
|
||||
}
|
||||
if(pthread_mutex_unlock(&sem->mutex) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sem_wait(sem_t *sem) {
|
||||
if(pthread_mutex_lock(&sem->mutex) < 0)
|
||||
return -1;
|
||||
while(sem->value == 0) {
|
||||
pthread_cond_wait(&sem->cond,&sem->mutex);
|
||||
}
|
||||
sem->value--;
|
||||
if(pthread_mutex_unlock(&sem->mutex) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sem_destroy(sem_t *sem) {
|
||||
int ret;
|
||||
ret = pthread_cond_destroy(&sem->cond);
|
||||
if(ret < 0) return -1;
|
||||
ret = pthread_mutex_destroy(&sem->mutex);
|
||||
if(ret < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
15
boehm-gc/include/private/darwin_stop_world.h
Normal file
15
boehm-gc/include/private/darwin_stop_world.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef GC_DARWIN_STOP_WORLD_H
|
||||
#define GC_DARWIN_STOP_WORLD_H
|
||||
|
||||
#if !defined(GC_DARWIN_THREADS)
|
||||
#error darwin_stop_world.h included without GC_DARWIN_THREADS defined
|
||||
#endif
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/thread_act.h>
|
||||
|
||||
struct thread_stop_info {
|
||||
mach_port_t mach_thread;
|
||||
};
|
||||
|
||||
#endif
|
12
boehm-gc/include/private/pthread_stop_world.h
Normal file
12
boehm-gc/include/private/pthread_stop_world.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef GC_PTHREAD_STOP_WORLD_H
|
||||
#define GC_PTHREAD_STOP_WORLD_H
|
||||
|
||||
struct thread_stop_info {
|
||||
int signal;
|
||||
word last_stop_count; /* GC_last_stop_count value when thread */
|
||||
/* last successfully handled a suspend */
|
||||
/* signal. */
|
||||
ptr_t stack_ptr; /* Valid only when stopped. */
|
||||
};
|
||||
|
||||
#endif
|
97
boehm-gc/include/private/pthread_support.h
Normal file
97
boehm-gc/include/private/pthread_support.h
Normal file
@ -0,0 +1,97 @@
|
||||
#ifndef GC_PTHREAD_SUPPORT_H
|
||||
#define GC_PTHREAD_SUPPORT_H
|
||||
|
||||
# include "private/gc_priv.h"
|
||||
|
||||
# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
|
||||
&& !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS)
|
||||
|
||||
#if defined(GC_DARWIN_THREADS)
|
||||
# include "private/darwin_stop_world.h"
|
||||
#else
|
||||
# include "private/pthread_stop_world.h"
|
||||
#endif
|
||||
|
||||
/* We use the allocation lock to protect thread-related data structures. */
|
||||
|
||||
/* The set of all known threads. We intercept thread creation and */
|
||||
/* joins. */
|
||||
/* Protected by allocation/GC lock. */
|
||||
/* Some of this should be declared volatile, but that's inconsistent */
|
||||
/* with some library routine declarations. */
|
||||
typedef struct GC_Thread_Rep {
|
||||
struct GC_Thread_Rep * next; /* More recently allocated threads */
|
||||
/* with a given pthread id come */
|
||||
/* first. (All but the first are */
|
||||
/* guaranteed to be dead, but we may */
|
||||
/* not yet have registered the join.) */
|
||||
pthread_t id;
|
||||
/* Extra bookkeeping information the stopping code uses */
|
||||
struct thread_stop_info stop_info;
|
||||
|
||||
short flags;
|
||||
# define FINISHED 1 /* Thread has exited. */
|
||||
# define DETACHED 2 /* Thread is intended to be detached. */
|
||||
# define MAIN_THREAD 4 /* True for the original thread only. */
|
||||
short thread_blocked; /* Protected by GC lock. */
|
||||
/* Treated as a boolean value. If set, */
|
||||
/* thread will acquire GC lock before */
|
||||
/* doing any pointer manipulations, and */
|
||||
/* has set its sp value. Thus it does */
|
||||
/* not need to be sent a signal to stop */
|
||||
/* it. */
|
||||
ptr_t stack_end; /* Cold end of the stack. */
|
||||
# ifdef IA64
|
||||
ptr_t backing_store_end;
|
||||
ptr_t backing_store_ptr;
|
||||
# endif
|
||||
void * status; /* The value returned from the thread. */
|
||||
/* Used only to avoid premature */
|
||||
/* reclamation of any data it might */
|
||||
/* reference. */
|
||||
# ifdef THREAD_LOCAL_ALLOC
|
||||
# if CPP_WORDSZ == 64 && defined(ALIGN_DOUBLE)
|
||||
# define GRANULARITY 16
|
||||
# define NFREELISTS 49
|
||||
# else
|
||||
# define GRANULARITY 8
|
||||
# define NFREELISTS 65
|
||||
# endif
|
||||
/* The ith free list corresponds to size i*GRANULARITY */
|
||||
# define INDEX_FROM_BYTES(n) ((ADD_SLOP(n) + GRANULARITY - 1)/GRANULARITY)
|
||||
# define BYTES_FROM_INDEX(i) ((i) * GRANULARITY - EXTRA_BYTES)
|
||||
# define SMALL_ENOUGH(bytes) (ADD_SLOP(bytes) <= \
|
||||
(NFREELISTS-1)*GRANULARITY)
|
||||
ptr_t ptrfree_freelists[NFREELISTS];
|
||||
ptr_t normal_freelists[NFREELISTS];
|
||||
# ifdef GC_GCJ_SUPPORT
|
||||
ptr_t gcj_freelists[NFREELISTS];
|
||||
# endif
|
||||
/* Free lists contain either a pointer or a small count */
|
||||
/* reflecting the number of granules allocated at that */
|
||||
/* size. */
|
||||
/* 0 ==> thread-local allocation in use, free list */
|
||||
/* empty. */
|
||||
/* > 0, <= DIRECT_GRANULES ==> Using global allocation, */
|
||||
/* too few objects of this size have been */
|
||||
/* allocated by this thread. */
|
||||
/* >= HBLKSIZE => pointer to nonempty free list. */
|
||||
/* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */
|
||||
/* local alloc, equivalent to 0. */
|
||||
# define DIRECT_GRANULES (HBLKSIZE/GRANULARITY)
|
||||
/* Don't use local free lists for up to this much */
|
||||
/* allocation. */
|
||||
# endif
|
||||
} * GC_thread;
|
||||
|
||||
# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
|
||||
extern volatile GC_thread GC_threads[THREAD_TABLE_SZ];
|
||||
|
||||
extern GC_bool GC_thr_initialized;
|
||||
|
||||
GC_thread GC_lookup_thread(pthread_t id);
|
||||
|
||||
void GC_stop_init();
|
||||
|
||||
#endif /* GC_PTHREADS && !GC_SOLARIS_THREADS.... etc */
|
||||
#endif /* GC_PTHREAD_SUPPORT_H */
|
336
boehm-gc/missing
Executable file
336
boehm-gc/missing
Executable file
@ -0,0 +1,336 @@
|
||||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
# 02111-1307, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case "$1" in
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
tar try tar, gnutar, gtar, then tar without non-portable flags
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]"
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing 0.4 - GNU automake"
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aclocal*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case "$f" in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and you do not seem to have it handy on your
|
||||
system. You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1Help2man' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
|
||||
test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison|yacc)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if [ $# -ne 1 ]; then
|
||||
eval LASTARG="\${$#}"
|
||||
case "$LASTARG" in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ ! -f y.tab.h ]; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if [ ! -f y.tab.c ]; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex|flex)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if [ $# -ne 1 ]; then
|
||||
eval LASTARG="\${$#}"
|
||||
case "$LASTARG" in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ ! -f lex.yy.c ]; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
|
||||
if test -z "$file"; then
|
||||
file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
|
||||
fi
|
||||
if [ -f "$file" ]; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo)
|
||||
if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
|
||||
# We have makeinfo, but it failed.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is missing on your system. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
|
||||
if test -z "$file"; then
|
||||
file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
|
||||
fi
|
||||
touch $file
|
||||
;;
|
||||
|
||||
tar)
|
||||
shift
|
||||
if test -n "$run"; then
|
||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We have already tried tar in the generic part.
|
||||
# Look for gnutar/gtar before invocation to avoid ugly error
|
||||
# messages.
|
||||
if (gnutar --version > /dev/null 2>&1); then
|
||||
gnutar ${1+"$@"} && exit 0
|
||||
fi
|
||||
if (gtar --version > /dev/null 2>&1); then
|
||||
gtar ${1+"$@"} && exit 0
|
||||
fi
|
||||
firstarg="$1"
|
||||
if shift; then
|
||||
case "$firstarg" in
|
||||
*o*)
|
||||
firstarg=`echo "$firstarg" | sed s/o//`
|
||||
tar "$firstarg" ${1+"$@"} && exit 0
|
||||
;;
|
||||
esac
|
||||
case "$firstarg" in
|
||||
*h*)
|
||||
firstarg=`echo "$firstarg" | sed s/h//`
|
||||
tar "$firstarg" ${1+"$@"} && exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: I can't seem to be able to run \`tar' with the given arguments.
|
||||
You may want to install GNU tar or Free paxutils, or check the
|
||||
command line arguments."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and you do not seem to have it handy on your
|
||||
system. You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequirements for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
84
boehm-gc/powerpc_darwin_mach_dep.s
Normal file
84
boehm-gc/powerpc_darwin_mach_dep.s
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
; GC_push_regs function. Under some optimization levels GCC will clobber
|
||||
; some of the non-volatile registers before we get a chance to save them
|
||||
; therefore, this can't be inline asm.
|
||||
|
||||
.text
|
||||
.align 2
|
||||
.globl _GC_push_regs
|
||||
_GC_push_regs:
|
||||
|
||||
; Prolog
|
||||
mflr r0
|
||||
stw r0,8(r1)
|
||||
stwu r1,-80(r1)
|
||||
|
||||
; Push r13-r31
|
||||
mr r3,r13
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r14
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r15
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r16
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r17
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r18
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r19
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r20
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r21
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r22
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r23
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r24
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r25
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r26
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r27
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r28
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r29
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r30
|
||||
bl L_GC_push_one$stub
|
||||
mr r3,r31
|
||||
bl L_GC_push_one$stub
|
||||
|
||||
;
|
||||
lwz r0,88(r1)
|
||||
addi r1,r1,80
|
||||
mtlr r0
|
||||
|
||||
; Return
|
||||
blr
|
||||
|
||||
; PIC stuff, generated by GCC
|
||||
|
||||
.data
|
||||
.picsymbol_stub
|
||||
L_GC_push_one$stub:
|
||||
.indirect_symbol _GC_push_one
|
||||
mflr r0
|
||||
bcl 20,31,L0$_GC_push_one
|
||||
L0$_GC_push_one:
|
||||
mflr r11
|
||||
addis r11,r11,ha16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
|
||||
mtlr r0
|
||||
lwz r12,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)(r11)
|
||||
mtctr r12
|
||||
addi r11,r11,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
|
||||
bctr
|
||||
.data
|
||||
.lazy_symbol_pointer
|
||||
L_GC_push_one$lazy_ptr:
|
||||
.indirect_symbol _GC_push_one
|
||||
.long dyld_stub_binding_helper
|
445
boehm-gc/pthread_stop_world.c
Normal file
445
boehm-gc/pthread_stop_world.c
Normal file
@ -0,0 +1,445 @@
|
||||
#include "private/pthread_support.h"
|
||||
|
||||
#if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
|
||||
&& !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
|
||||
&& !defined(GC_DARWIN_THREADS) && !defined(GC_AIX_THREADS)
|
||||
|
||||
#include <signal.h>
|
||||
#include <semaphore.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if 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
|
||||
|
||||
void GC_print_sig_mask()
|
||||
{
|
||||
sigset_t blocked;
|
||||
int i;
|
||||
|
||||
if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
|
||||
ABORT("pthread_sigmask");
|
||||
GC_printf0("Blocked: ");
|
||||
for (i = 1; i < NSIG; i++) {
|
||||
if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
|
||||
}
|
||||
GC_printf0("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
|
||||
|
||||
#ifdef GC_OSF1_THREADS
|
||||
GC_bool GC_retry_signals = TRUE;
|
||||
#else
|
||||
GC_bool GC_retry_signals = FALSE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We use signals to stop threads during GC.
|
||||
*
|
||||
* Suspended threads wait in signal handler for SIG_THR_RESTART.
|
||||
* That's more portable than semaphores or condition variables.
|
||||
* (We do use sem_post from a signal handler, but that should be portable.)
|
||||
*
|
||||
* The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h.
|
||||
* Note that we can't just stop a thread; we need it to save its stack
|
||||
* pointer(s) and acknowledge.
|
||||
*/
|
||||
|
||||
#ifndef SIG_THR_RESTART
|
||||
# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)
|
||||
# ifdef _SIGRTMIN
|
||||
# define SIG_THR_RESTART _SIGRTMIN + 5
|
||||
# else
|
||||
# define SIG_THR_RESTART SIGRTMIN + 5
|
||||
# endif
|
||||
# else
|
||||
# define SIG_THR_RESTART SIGXCPU
|
||||
# endif
|
||||
#endif
|
||||
|
||||
sem_t GC_suspend_ack_sem;
|
||||
|
||||
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 */
|
||||
/* guaranteed to be the mark_no correspending to our */
|
||||
/* suspension, i.e. the marker can't have incremented it yet. */
|
||||
# endif
|
||||
word my_stop_count = GC_stop_count;
|
||||
|
||||
if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Suspending 0x%lx\n", 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 %lx\n",
|
||||
pthread_self());
|
||||
}
|
||||
return;
|
||||
}
|
||||
# ifdef SPARC
|
||||
me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
|
||||
# else
|
||||
me -> stop_info.stack_ptr = (ptr_t)(&dummy);
|
||||
# endif
|
||||
# ifdef IA64
|
||||
me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack();
|
||||
# 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. */
|
||||
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 */
|
||||
} 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 */
|
||||
/* between the sem_post and sigsuspend. */
|
||||
/* We'd need more handshaking to work around that, since we don't want */
|
||||
/* to accidentally leave a RESTART signal pending, thus causing us to */
|
||||
/* continue prematurely in a future round. */
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Continuing 0x%lx\n", my_thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GC_restart_handler(int sig)
|
||||
{
|
||||
pthread_t my_thread = pthread_self();
|
||||
GC_thread me;
|
||||
|
||||
if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
|
||||
|
||||
/* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */
|
||||
/* 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. */
|
||||
me = GC_lookup_thread(my_thread);
|
||||
me->stop_info.signal = SIG_THR_RESTART;
|
||||
|
||||
/*
|
||||
** Note: even if we didn'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.
|
||||
*/
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("In GC_restart_handler for 0x%lx\n", pthread_self());
|
||||
#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. */
|
||||
void GC_push_all_stacks()
|
||||
{
|
||||
int i;
|
||||
GC_thread p;
|
||||
ptr_t lo, hi;
|
||||
/* On IA64, we also need to scan the register backing store. */
|
||||
IF_IA64(ptr_t bs_lo; ptr_t bs_hi;)
|
||||
pthread_t me = pthread_self();
|
||||
|
||||
if (!GC_thr_initialized) GC_thr_init();
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
|
||||
#endif
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> flags & FINISHED) continue;
|
||||
if (pthread_equal(p -> id, me)) {
|
||||
# ifdef SPARC
|
||||
lo = (ptr_t)GC_save_regs_in_stack();
|
||||
# else
|
||||
lo = GC_approx_sp();
|
||||
# endif
|
||||
IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
|
||||
} else {
|
||||
lo = p -> stop_info.stack_ptr;
|
||||
IF_IA64(bs_hi = p -> backing_store_ptr;)
|
||||
}
|
||||
if ((p -> flags & MAIN_THREAD) == 0) {
|
||||
hi = p -> stack_end;
|
||||
IF_IA64(bs_lo = p -> backing_store_end);
|
||||
} else {
|
||||
/* The original stack. */
|
||||
hi = GC_stackbottom;
|
||||
IF_IA64(bs_lo = BACKING_STORE_BASE;)
|
||||
}
|
||||
#if DEBUG_THREADS
|
||||
GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
|
||||
(unsigned long) p -> id,
|
||||
(unsigned long) lo, (unsigned long) hi);
|
||||
#endif
|
||||
if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
|
||||
# ifdef STACK_GROWS_UP
|
||||
/* We got them backwards! */
|
||||
GC_push_all_stack(hi, lo);
|
||||
# else
|
||||
GC_push_all_stack(lo, hi);
|
||||
# endif
|
||||
# ifdef IA64
|
||||
if (pthread_equal(p -> id, me)) {
|
||||
GC_push_all_eager(bs_lo, bs_hi);
|
||||
} else {
|
||||
GC_push_all_stack(bs_lo, bs_hi);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* We hold the allocation lock. Suspend all threads that might */
|
||||
/* still be running. Return the number of suspend signals that */
|
||||
/* were sent. */
|
||||
int GC_suspend_all()
|
||||
{
|
||||
int n_live_threads = 0;
|
||||
int i;
|
||||
GC_thread p;
|
||||
int result;
|
||||
pthread_t my_thread = pthread_self();
|
||||
|
||||
GC_stopping_thread = my_thread; /* debugging only. */
|
||||
GC_stopping_pid = getpid(); /* debugging only. */
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id != my_thread) {
|
||||
if (p -> flags & FINISHED) continue;
|
||||
if (p -> stop_info.last_stop_count == GC_stop_count) continue;
|
||||
if (p -> thread_blocked) /* Will wait */ continue;
|
||||
n_live_threads++;
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Sending suspend signal to 0x%lx\n", p -> id);
|
||||
#endif
|
||||
|
||||
result = pthread_kill(p -> id, SIG_SUSPEND);
|
||||
switch(result) {
|
||||
case ESRCH:
|
||||
/* Not really there anymore. Possible? */
|
||||
n_live_threads--;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
ABORT("pthread_kill failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return n_live_threads;
|
||||
}
|
||||
|
||||
/* Caller holds allocation lock. */
|
||||
void GC_stop_world()
|
||||
{
|
||||
int i;
|
||||
int n_live_threads;
|
||||
int code;
|
||||
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
|
||||
#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
|
||||
GC_acquire_mark_lock();
|
||||
GC_ASSERT(GC_fl_builder_count == 0);
|
||||
/* We should have previously waited for it to become zero. */
|
||||
# endif /* PARALLEL_MARK */
|
||||
++GC_stop_count;
|
||||
n_live_threads = GC_suspend_all();
|
||||
|
||||
if (GC_retry_signals) {
|
||||
unsigned long wait_usecs = 0; /* Total wait since retry. */
|
||||
# define WAIT_UNIT 3000
|
||||
# define RETRY_INTERVAL 100000
|
||||
for (;;) {
|
||||
int ack_count;
|
||||
|
||||
sem_getvalue(&GC_suspend_ack_sem, &ack_count);
|
||||
if (ack_count == n_live_threads) break;
|
||||
if (wait_usecs > RETRY_INTERVAL) {
|
||||
int newly_sent = GC_suspend_all();
|
||||
|
||||
# ifdef CONDPRINT
|
||||
if (GC_print_stats) {
|
||||
GC_printf1("Resent %ld signals after timeout\n",
|
||||
newly_sent);
|
||||
}
|
||||
# endif
|
||||
sem_getvalue(&GC_suspend_ack_sem, &ack_count);
|
||||
if (newly_sent < n_live_threads - ack_count) {
|
||||
WARN("Lost some threads during GC_stop_world?!\n",0);
|
||||
n_live_threads = ack_count + newly_sent;
|
||||
}
|
||||
wait_usecs = 0;
|
||||
}
|
||||
usleep(WAIT_UNIT);
|
||||
wait_usecs += WAIT_UNIT;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < n_live_threads; i++) {
|
||||
if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
|
||||
GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
|
||||
ABORT("sem_wait for handler failed");
|
||||
}
|
||||
}
|
||||
# ifdef PARALLEL_MARK
|
||||
GC_release_mark_lock();
|
||||
# endif
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("World stopped from 0x%lx\n", pthread_self());
|
||||
#endif
|
||||
GC_stopping_thread = 0; /* debugging only */
|
||||
}
|
||||
|
||||
/* Caller holds allocation lock, and has held it continuously since */
|
||||
/* the world stopped. */
|
||||
void GC_start_world()
|
||||
{
|
||||
pthread_t my_thread = pthread_self();
|
||||
register int i;
|
||||
register GC_thread p;
|
||||
register int n_live_threads = 0;
|
||||
register int result;
|
||||
|
||||
# if DEBUG_THREADS
|
||||
GC_printf0("World starting\n");
|
||||
# endif
|
||||
|
||||
for (i = 0; i < THREAD_TABLE_SZ; i++) {
|
||||
for (p = GC_threads[i]; p != 0; p = p -> next) {
|
||||
if (p -> id != my_thread) {
|
||||
if (p -> flags & FINISHED) continue;
|
||||
if (p -> thread_blocked) continue;
|
||||
n_live_threads++;
|
||||
#if DEBUG_THREADS
|
||||
GC_printf1("Sending restart signal to 0x%lx\n", p -> id);
|
||||
#endif
|
||||
|
||||
result = pthread_kill(p -> id, SIG_THR_RESTART);
|
||||
switch(result) {
|
||||
case ESRCH:
|
||||
/* Not really there anymore. Possible? */
|
||||
n_live_threads--;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
ABORT("pthread_kill failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG_THREADS
|
||||
GC_printf0("World started\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void GC_stop_init() {
|
||||
struct sigaction act;
|
||||
|
||||
if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
|
||||
ABORT("sem_init failed");
|
||||
|
||||
act.sa_flags = SA_RESTART;
|
||||
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. */
|
||||
act.sa_handler = GC_suspend_handler;
|
||||
if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
|
||||
ABORT("Cannot set SIG_SUSPEND handler");
|
||||
}
|
||||
|
||||
act.sa_handler = GC_restart_handler;
|
||||
if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
|
||||
ABORT("Cannot set SIG_THR_RESTART handler");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
# ifdef CONDPRINT
|
||||
if (GC_print_stats && GC_retry_signals) {
|
||||
GC_printf0("Will retry suspend signal if necessary.\n");
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
1570
boehm-gc/pthread_support.c
Normal file
1570
boehm-gc/pthread_support.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user