Initial revision

From-SVN: r69872
This commit is contained in:
Jeff Sturm 2003-07-28 03:46:07 +00:00
parent b4acb5ef4b
commit 6991c6c926
22 changed files with 5739 additions and 0 deletions

693
boehm-gc/aix_irix_threads.c Normal file
View 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
View 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

View 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
View 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
View 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
View 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
View 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.

View 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
View 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.

View 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
View 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
View 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>

View 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;
}

View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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

File diff suppressed because it is too large Load Diff