aboutsummaryrefslogtreecommitdiff
path: root/boehm-gc/include
diff options
context:
space:
mode:
authorBryce McKinlay <bryce@gcc.gnu.org>2001-05-21 07:39:23 +0100
committerBryce McKinlay <bryce@gcc.gnu.org>2001-05-21 07:39:23 +0100
commitf54d42879f9f2e50f38c703064db6e3d6e91a568 (patch)
tree74fdbde7939a341e9ef653b68f90004a5beb56a6 /boehm-gc/include
parent9d2f2c45d8936179588489a6cb3df61203ed1028 (diff)
downloadgcc-f54d42879f9f2e50f38c703064db6e3d6e91a568.zip
gcc-f54d42879f9f2e50f38c703064db6e3d6e91a568.tar.gz
gcc-f54d42879f9f2e50f38c703064db6e3d6e91a568.tar.bz2
Initial revision
From-SVN: r42370
Diffstat (limited to 'boehm-gc/include')
-rw-r--r--boehm-gc/include/gc_amiga_redirects.h30
-rw-r--r--boehm-gc/include/gc_backptr.h65
-rw-r--r--boehm-gc/include/gc_local_alloc.h85
-rw-r--r--boehm-gc/include/gc_mark.h145
-rw-r--r--boehm-gc/include/gc_pthread_redirects.h69
-rw-r--r--boehm-gc/include/private/dbg_mlc.h132
-rw-r--r--boehm-gc/include/private/gc_locks.h480
-rw-r--r--boehm-gc/include/private/gc_pmark.h396
-rw-r--r--boehm-gc/include/private/solaris_threads.h34
-rw-r--r--boehm-gc/include/private/specific.h83
10 files changed, 1519 insertions, 0 deletions
diff --git a/boehm-gc/include/gc_amiga_redirects.h b/boehm-gc/include/gc_amiga_redirects.h
new file mode 100644
index 0000000..9e975c8
--- /dev/null
+++ b/boehm-gc/include/gc_amiga_redirects.h
@@ -0,0 +1,30 @@
+#ifndef GC_AMIGA_REDIRECTS_H
+
+# define GC_AMIGA_REDIRECTS_H
+
+# if ( defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB) )
+ extern void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes);
+# define GC_realloc(a,b) GC_amiga_realloc(a,b)
+ extern void GC_amiga_set_toany(void (*func)(void));
+ extern int GC_amiga_free_space_divisor_inc;
+ extern void *(*GC_amiga_allocwrapper_do) \
+ (size_t size,void *(*AllocFunction)(size_t size2));
+# define GC_malloc(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc)
+# define GC_malloc_atomic(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic)
+# define GC_malloc_uncollectable(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable)
+# define GC_malloc_stubborn(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_stubborn)
+# define GC_malloc_atomic_uncollectable(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable)
+# define GC_malloc_ignore_off_page(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page)
+# define GC_malloc_atomic_ignore_off_page(a) \
+ (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page)
+# endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */
+
+#endif /* GC_AMIGA_REDIRECTS_H */
+
+
diff --git a/boehm-gc/include/gc_backptr.h b/boehm-gc/include/gc_backptr.h
new file mode 100644
index 0000000..5899496
--- /dev/null
+++ b/boehm-gc/include/gc_backptr.h
@@ -0,0 +1,65 @@
+/*
+ * This is a simple API to implement pointer back tracing, i.e.
+ * to answer questions such as "who is pointing to this" or
+ * "why is this object being retained by the collector"
+ *
+ * This API assumes that we have an ANSI C compiler.
+ *
+ * Most of these calls yield useful information on only after
+ * a garbage collection. Usually the client will first force
+ * a full collection and then gather information, preferably
+ * before much intervening allocation.
+ *
+ * The implementation of the interface is only about 99.9999%
+ * correct. It is intended to be good enough for profiling,
+ * but is not intended to be used with production code.
+ *
+ * Results are likely to be much more useful if all allocation is
+ * accomplished through the debugging allocators.
+ *
+ * The implementation idea is due to A. Demers.
+ */
+
+#ifndef GC_BACKPTR_H
+#define GC_BACKPTR_H
+/* Store information about the object referencing dest in *base_p */
+/* and *offset_p. */
+/* If multiple objects or roots point to dest, the one reported */
+/* will be the last on used by the garbage collector to trace the */
+/* object. */
+/* source is root ==> *base_p = address, *offset_p = 0 */
+/* source is heap object ==> *base_p != 0, *offset_p = offset */
+/* Returns 1 on success, 0 if source couldn't be determined. */
+/* Dest can be any address within a heap object. */
+typedef enum { GC_UNREFERENCED, /* No reference info available. */
+ GC_NO_SPACE, /* Dest not allocated with debug alloc */
+ GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p */
+ GC_REFD_FROM_REG, /* Referenced from a register, i.e. */
+ /* a root without an address. */
+ GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */
+ GC_FINALIZER_REFD /* Finalizable and hence accessible. */
+} GC_ref_kind;
+
+GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p);
+
+/* Generate a random heap address. */
+/* The resulting address is in the heap, but */
+/* not necessarily inside a valid object. */
+void * GC_generate_random_heap_address(void);
+
+/* Generate a random address inside a valid marked heap object. */
+void * GC_generate_random_valid_address(void);
+
+/* Force a garbage collection and generate a backtrace from a */
+/* random heap address. */
+/* This uses the GC logging mechanism (GC_printf) to produce */
+/* output. It can often be called from a debugger. The */
+/* source in dbg_mlc.c also serves as a sample client. */
+void GC_generate_random_backtrace(void);
+
+/* Print a backtrace from a specific address. Used by the */
+/* above. The client should call GC_gcollect() immediately */
+/* before invocation. */
+void GC_print_backtrace(void *);
+
+#endif /* GC_BACKPTR_H */
diff --git a/boehm-gc/include/gc_local_alloc.h b/boehm-gc/include/gc_local_alloc.h
new file mode 100644
index 0000000..1e58730
--- /dev/null
+++ b/boehm-gc/include/gc_local_alloc.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2000 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.
+ */
+
+/*
+ * Interface for thread local allocation. Memory obtained
+ * this way can be used by all threads, as though it were obtained
+ * from an allocator like GC_malloc. The difference is that GC_local_malloc
+ * counts the number of allocations of a given size from the current thread,
+ * and uses GC_malloc_many to perform the allocations once a threashold
+ * is exceeded. Thus far less synchronization may be needed.
+ * Allocation of known large objects should not use this interface.
+ * This interface is designed primarily for fast allocation of small
+ * objects on multiprocessors, e.g. for a JVM running on an MP server.
+ *
+ * If this file is included with GC_GCJ_SUPPORT defined, GCJ-style
+ * bitmap allocation primitives will also be included.
+ *
+ * If this file is included with GC_REDIRECT_TO_LOCAL defined, then
+ * GC_MALLOC, GC_MALLOC_ATOMIC, and possibly GC_GCJ_MALLOC will
+ * be redefined to use the thread local allocatoor.
+ *
+ * The interface is available only if the collector is built with
+ * -DTHREAD_LOCAL_ALLOC, which is currently supported only on Linux.
+ *
+ * The debugging allocators use standard, not thread-local allocation.
+ */
+
+#ifndef GC_LOCAL_ALLOC_H
+#define GC_LOCAL_ALLOC_H
+
+#ifndef _GC_H
+# include "gc.h"
+#endif
+
+#if defined(GC_GCJ_SUPPORT) && !defined(GC_GCJ_H)
+# include "gc_gcj.h"
+#endif
+
+/* We assume ANSI C for this interface. */
+
+GC_PTR GC_local_malloc(size_t bytes);
+
+GC_PTR GC_local_malloc_atomic(size_t bytes);
+
+#if defined(GC_GCJ_SUPPORT)
+ GC_PTR GC_local_gcj_malloc(size_t bytes,
+ void * ptr_to_struct_containing_descr);
+#endif
+
+# ifdef GC_DEBUG
+# define GC_LOCAL_MALLOC(s) GC_debug_malloc(s,GC_EXTRAS)
+# define GC_LOCAL_MALLOC_ATOMIC(s) GC_debug_malloc_atomic(s,GC_EXTRAS)
+# ifdef GC_GCJ_SUPPORT
+# define GC_LOCAL_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS)
+# endif
+# else
+# define GC_LOCAL_MALLOC(s) GC_local_malloc(s)
+# define GC_LOCAL_MALLOC_ATOMIC(s) GC_local_malloc_atomic(s)
+# ifdef GC_GCJ_SUPPORT
+# define GC_LOCAL_GCJ_MALLOC(s,d) GC_local_gcj_malloc(s,d)
+# endif
+# endif
+
+# ifdef GC_REDIRECT_TO_LOCAL
+# undef GC_MALLOC
+# define GC_MALLOC(s) GC_LOCAL_MALLOC(s)
+# undef GC_MALLOC_ATOMIC
+# define GC_MALLOC_ATOMIC(s) GC_LOCAL_MALLOC_ATOMIC(s)
+# ifdef GC_GCJ_SUPPORT
+# undef GC_GCJ_MALLOC
+# define GC_GCJ_MALLOC(s,d) GC_LOCAL_GCJ_MALLOC(s,d)
+# endif
+# endif
+
+#endif /* GC_LOCAL_ALLOC_H */
diff --git a/boehm-gc/include/gc_mark.h b/boehm-gc/include/gc_mark.h
new file mode 100644
index 0000000..0856d16
--- /dev/null
+++ b/boehm-gc/include/gc_mark.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 2001 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.
+ *
+ */
+
+/*
+ * This contains interfaces to the GC marker that are likely to be useful to
+ * clients that provide detailed heap layout information to the collector.
+ * This interface should not be used by normal C or C++ clients.
+ * It will be useful to runtimes for other languages.
+ *
+ * Note that this file is not "namespace-clean", i.e. it introduces names
+ * not prefixed with GC_, which may collide with the client's names. It
+ * should be included only in those few places that directly provide
+ * information to the collector.
+ */
+#ifndef GC_MARK_H
+# define GC_MARK_H
+
+# ifndef GC_H
+# include "gc.h"
+# endif
+
+/* A client supplied mark procedure. Returns new mark stack pointer. */
+/* Primary effect should be to push new entries on the mark stack. */
+/* Mark stack pointer values are passed and returned explicitly. */
+/* Global variables decribing mark stack are not necessarily valid. */
+/* (This usually saves a few cycles by keeping things in registers.) */
+/* Assumed to scan about GC_PROC_BYTES on average. If it needs to do */
+/* much more work than that, it should do it in smaller pieces by */
+/* pushing itself back on the mark stack. */
+/* Note that it should always do some work (defined as marking some */
+/* objects) before pushing more than one entry on the mark stack. */
+/* This is required to ensure termination in the event of mark stack */
+/* overflows. */
+/* This procedure is always called with at least one empty entry on the */
+/* mark stack. */
+/* Currently we require that mark procedures look for pointers in a */
+/* subset of the places the conservative marker would. It must be safe */
+/* to invoke the normal mark procedure instead. */
+/* WARNING: Such a mark procedure may be invoked on an unused object */
+/* residing on a free list. Such objects are cleared, except for a */
+/* free list link field in the first word. Thus mark procedures may */
+/* not count on the presence of a type descriptor, and must handle this */
+/* case correctly somehow. */
+# define GC_PROC_BYTES 100
+struct GC_ms_entry;
+typedef struct GC_ms_entry * (*GC_mark_proc) GC_PROTO((
+ GC_word * addr, struct GC_ms_entry * mark_stack_ptr,
+ struct GC_ms_entry * mark_stack_limit, GC_word env));
+
+# define GC_LOG_MAX_MARK_PROCS 6
+# define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS)
+
+/* In a few cases it's necessary to assign statically known indices to */
+/* certain mark procs. Thus we reserve a few for well known clients. */
+/* (This is necessary if mark descriptors are compiler generated.) */
+#define GC_RESERVED_MARK_PROCS 8
+# define GC_GCJ_RESERVED_MARK_PROC_INDEX 0
+
+/* Object descriptors on mark stack or in objects. Low order two */
+/* bits are tags distinguishing among the following 4 possibilities */
+/* for the high order 30 bits. */
+#define GC_DS_TAG_BITS 2
+#define GC_DS_TAGS ((1 << GC_DS_TAG_BITS) - 1)
+#define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */
+ /* must be a multiple of 4. */
+#define GC_DS_BITMAP 1 /* 30 (62) bits are a bitmap describing pointer */
+ /* fields. The msb is 1 iff the first word */
+ /* is a pointer. */
+ /* (This unconventional ordering sometimes */
+ /* makes the marker slightly faster.) */
+ /* Zeroes indicate definite nonpointers. Ones */
+ /* indicate possible pointers. */
+ /* Only usable if pointers are word aligned. */
+#define GC_DS_PROC 2
+ /* The objects referenced by this object can be */
+ /* pushed on the mark stack by invoking */
+ /* PROC(descr). ENV(descr) is passed as the */
+ /* last argument. */
+# define GC_MAKE_PROC(proc_index, env) \
+ (((((env) << GC_LOG_MAX_MARK_PROCS) \
+ | (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC)
+#define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */
+ /* byte displacement from the beginning of the */
+ /* object given by descr & ~DS_TAGS */
+ /* If the descriptor is negative, the real */
+ /* descriptor is at (*<object_start>) - */
+ /* (descr & ~DS_TAGS) - GC_INDIR_PER_OBJ_BIAS */
+ /* The latter alternative can be used if each */
+ /* object contains a type descriptor in the */
+ /* first word. */
+ /* Note that in multithreaded environments */
+ /* per object descriptors maust be located in */
+ /* either the first two or last two words of */
+ /* the object, since only those are guaranteed */
+ /* to be cleared while the allocation lock is */
+ /* held. */
+#define GC_INDIR_PER_OBJ_BIAS 0x10
+
+extern GC_PTR GC_least_plausible_heap_addr;
+extern GC_PTR GC_greatest_plausible_heap_addr;
+ /* Bounds on the heap. Guaranteed valid */
+ /* Likely to include future heap expansion. */
+
+/* Handle nested references in a custom mark procedure. */
+/* Check if obj is a valid object. If so, ensure that it is marked. */
+/* If it was not previously marked, push its contents onto the mark */
+/* stack for future scanning. The object will then be scanned using */
+/* its mark descriptor. */
+/* Returns the new mark stack pointer. */
+/* Handles mark stack overflows correctly. */
+/* Since this marks first, it makes progress even if there are mark */
+/* stack overflows. */
+/* Src is the address of the pointer to obj, which is used only */
+/* for back pointer-based heap debugging. */
+/* It is strongly recommended that most objects be handled without mark */
+/* procedures, e.g. with bitmap descriptors, and that mark procedures */
+/* be reserved for exceptional cases. That will ensure that */
+/* performance of this call is not extremely performance critical. */
+/* (Otherwise we would need to inline GC_mark_and_push completely, */
+/* which would tie the client code to a fixed colllector version.) */
+struct GC_ms_entry *GC_mark_and_push
+ GC_PROTO((GC_PTR obj,
+ struct GC_ms_entry * mark_stack_ptr,
+ struct GC_ms_entry * mark_stack_limit, GC_PTR *src));
+
+#define GC_MARK_AND_PUSH(obj, msp, lim, src) \
+ (((GC_word)obj >= (GC_word)GC_least_plausible_heap_addr && \
+ (GC_word)obj <= (GC_word)GC_greatest_plausible_heap_addr)? \
+ GC_mark_and_push(obj, msp, lim, src) : \
+ msp)
+
+#endif /* GC_MARK_H */
+
diff --git a/boehm-gc/include/gc_pthread_redirects.h b/boehm-gc/include/gc_pthread_redirects.h
new file mode 100644
index 0000000..ac254a8
--- /dev/null
+++ b/boehm-gc/include/gc_pthread_redirects.h
@@ -0,0 +1,69 @@
+/* Our pthread support normally needs to intercept a number of thread */
+/* calls. We arrange to do that here, if appropriate. */
+
+#ifndef GC_PTHREAD_REDIRECTS_H
+
+#define GC_PTHREAD_REDIRECTS_H
+
+#if defined(GC_SOLARIS_THREADS)
+/* We need to intercept calls to many of the threads primitives, so */
+/* that we can locate thread stacks and stop the world. */
+/* Note also that the collector cannot see thread specific data. */
+/* Thread specific data should generally consist of pointers to */
+/* uncollectable objects (allocated with GC_malloc_uncollectable, */
+/* not the system malloc), which are deallocated using the destructor */
+/* facility in thr_keycreate. Alternatively, keep a redundant pointer */
+/* to thread specific data on the thread stack. */
+# include <thread.h>
+ int GC_thr_create(void *stack_base, size_t stack_size,
+ void *(*start_routine)(void *), void *arg, long flags,
+ thread_t *new_thread);
+ int GC_thr_join(thread_t wait_for, thread_t *departed, void **status);
+ int GC_thr_suspend(thread_t target_thread);
+ int GC_thr_continue(thread_t target_thread);
+ void * GC_dlopen(const char *path, int mode);
+# define thr_create GC_thr_create
+# define thr_join GC_thr_join
+# define thr_suspend GC_thr_suspend
+# define thr_continue GC_thr_continue
+#endif /* GC_SOLARIS_THREADS */
+
+#if defined(GC_SOLARIS_PTHREADS)
+# include <pthread.h>
+# include <signal.h>
+ extern int GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void * (*thread_execp)(void *), void *arg);
+ extern int GC_pthread_join(pthread_t wait_for, void **status);
+# define pthread_join GC_pthread_join
+# define pthread_create GC_pthread_create
+#endif
+
+#if defined(GC_SOLARIS_PTHREADS) || defined(GC_SOLARIS_THREADS)
+# define dlopen GC_dlopen
+#endif /* SOLARIS_THREADS || SOLARIS_PTHREADS */
+
+
+#if !defined(GC_USE_LD_WRAP) && \
+ (defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \
+ || defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS))
+/* We treat these similarly. */
+# include <pthread.h>
+# include <signal.h>
+
+ int GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
+ int GC_pthread_join(pthread_t thread, void **retval);
+ int GC_pthread_detach(pthread_t thread);
+
+# define pthread_create GC_pthread_create
+# define pthread_sigmask GC_pthread_sigmask
+# define pthread_join GC_pthread_join
+# define pthread_detach GC_pthread_detach
+# define dlopen GC_dlopen
+
+#endif /* GC_xxxxx_THREADS */
+
+#endif /* GC_PTHREAD_REDIRECTS_H */
diff --git a/boehm-gc/include/private/dbg_mlc.h b/boehm-gc/include/private/dbg_mlc.h
new file mode 100644
index 0000000..1ee814d
--- /dev/null
+++ b/boehm-gc/include/private/dbg_mlc.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1997 by Silicon Graphics. All rights reserved.
+ * Copyright (c) 1999 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.
+ */
+
+/*
+ * This is mostly an internal header file. Typical clients should
+ * not use it. Clients that define their own object kinds with
+ * debugging allocators will probably want to include this, however.
+ * No attempt is made to keep the namespace clean. This should not be
+ * included from header filrd that are frequently included by clients.
+ */
+
+#ifndef _DBG_MLC_H
+
+#define _DBG_MLC_H
+
+# define I_HIDE_POINTERS
+# include "gc_priv.h"
+# ifdef KEEP_BACK_PTRS
+# include "gc_backptr.h"
+# endif
+
+# define START_FLAG ((word)0xfedcedcb)
+# define END_FLAG ((word)0xbcdecdef)
+ /* Stored both one past the end of user object, and one before */
+ /* the end of the object as seen by the allocator. */
+
+
+/* Object header */
+typedef struct {
+# ifdef KEEP_BACK_PTRS
+ GC_hidden_pointer oh_back_ptr;
+ /* We make sure that we only store even valued */
+ /* pointers here, so that the hidden version has */
+ /* the least significant bit set. We never */
+ /* overwrite a value with the least significant */
+ /* bit clear, thus ensuring that we never overwrite */
+ /* a free list link field. */
+ /* The following are special back pointer values. */
+ /* Note that the "hidden" (i.e. bitwise */
+ /* complemented version) of these is actually */
+ /* stored. */
+# define NOT_MARKED (ptr_t)(0)
+# define MARKED_FOR_FINALIZATION (ptr_t)(2)
+ /* Object was marked because it is finalizable. */
+# define MARKED_FROM_REGISTER (ptr_t)(4)
+ /* Object was marked from a rgister. Hence the */
+ /* source of the reference doesn't have an address. */
+# if ALIGNMENT == 1
+ /* Fudge back pointer to be even. */
+# define HIDE_BACK_PTR(p) HIDE_POINTER(~1 & (GC_word)(p))
+# else
+# define HIDE_BACK_PTR(p) HIDE_POINTER(p)
+# endif
+# ifdef ALIGN_DOUBLE
+ word oh_dummy;
+# endif
+# endif
+ char * oh_string; /* object descriptor string */
+ word oh_int; /* object descriptor integers */
+# ifdef NEED_CALLINFO
+ struct callinfo oh_ci[NFRAMES];
+# endif
+# ifndef SHORT_DBG_HDRS
+ word oh_sz; /* Original malloc arg. */
+ word oh_sf; /* start flag */
+# endif /* SHORT_DBG_HDRS */
+} oh;
+/* The size of the above structure is assumed not to dealign things, */
+/* and to be a multiple of the word length. */
+
+#define DEBUG_BYTES (sizeof (oh) + sizeof (word))
+#define USR_PTR_FROM_BASE(p) ((ptr_t)(p) + sizeof(oh))
+
+/* There is no reason to ever add a byte at the end explicitly, since we */
+/* already add a guard word. */
+#undef ROUNDED_UP_WORDS
+#define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
+
+#ifdef SAVE_CALL_CHAIN
+# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci)
+# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
+#else
+# ifdef GC_ADD_CALLER
+# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra)
+# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
+# else
+# define ADD_CALL_CHAIN(base, ra)
+# define PRINT_CALL_CHAIN(base)
+# endif
+#endif
+
+# ifdef GC_ADD_CALLER
+# define OPT_RA ra,
+# else
+# define OPT_RA
+# endif
+
+
+/* Check whether object with base pointer p has debugging info */
+/* p is assumed to point to a legitimate object in our part */
+/* of the heap. */
+#ifdef SHORT_DBG_HDRS
+# define GC_has_other_debug_info(p) TRUE
+#else
+ GC_bool GC_has_other_debug_info(/* p */);
+#endif
+
+#ifdef KEEP_BACK_PTRS
+# define GC_HAS_DEBUG_INFO(p) \
+ ((((oh *)p)->oh_back_ptr & 1) && GC_has_other_debug_info(p))
+#else
+# define GC_HAS_DEBUG_INFO(p) GC_has_other_debug_info(p)
+#endif
+
+/* Store debugging info into p. Return displaced pointer. */
+/* Assumes we don't hold allocation lock. */
+ptr_t GC_store_debug_info(/* p, sz, string, integer */);
+
+#endif /* _DBG_MLC_H */
diff --git a/boehm-gc/include/private/gc_locks.h b/boehm-gc/include/private/gc_locks.h
new file mode 100644
index 0000000..b9ff0cf
--- /dev/null
+++ b/boehm-gc/include/private/gc_locks.h
@@ -0,0 +1,480 @@
+/*
+ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
+ * Copyright (c) 1999 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.
+ */
+
+#ifndef GC_LOCKS_H
+#define GC_LOCKS_H
+
+/*
+ * Mutual exclusion between allocator/collector routines.
+ * Needed if there is more than one allocator thread.
+ * FASTLOCK() is assumed to try to acquire the lock in a cheap and
+ * dirty way that is acceptable for a few instructions, e.g. by
+ * inhibiting preemption. This is assumed to have succeeded only
+ * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE.
+ * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED().
+ * If signals cannot be tolerated with the FASTLOCK held, then
+ * FASTLOCK should disable signals. The code executed under
+ * FASTLOCK is otherwise immune to interruption, provided it is
+ * not restarted.
+ * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK
+ * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK.
+ * (There is currently no equivalent for FASTLOCK.)
+ *
+ * In the PARALLEL_MARK case, we also need to define a number of
+ * other inline finctions here:
+ * GC_bool GC_compare_and_exchange( volatile GC_word *addr,
+ * GC_word old, GC_word new )
+ * GC_word GC_atomic_add( volatile GC_word *addr, GC_word how_much )
+ * void GC_memory_barrier( )
+ *
+ */
+# ifdef THREADS
+# ifdef PCR_OBSOLETE /* Faster, but broken with multiple lwp's */
+# include "th/PCR_Th.h"
+# include "th/PCR_ThCrSec.h"
+ extern struct PCR_Th_MLRep GC_allocate_ml;
+# define DCL_LOCK_STATE PCR_sigset_t GC_old_sig_mask
+# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
+# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
+# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
+# define FASTLOCK() PCR_ThCrSec_EnterSys()
+ /* Here we cheat (a lot): */
+# define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0)
+ /* TRUE if nobody currently holds the lock */
+# define FASTUNLOCK() PCR_ThCrSec_ExitSys()
+# endif
+# ifdef PCR
+# include <base/PCR_Base.h>
+# include <th/PCR_Th.h>
+ extern PCR_Th_ML GC_allocate_ml;
+# define DCL_LOCK_STATE \
+ PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask
+# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
+# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
+# define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml))
+# define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay)
+# define FASTUNLOCK() {\
+ if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); }
+# endif
+# ifdef SRC_M3
+ extern GC_word RT0u__inCritical;
+# define LOCK() RT0u__inCritical++
+# define UNLOCK() RT0u__inCritical--
+# endif
+# ifdef SOLARIS_THREADS
+# include <thread.h>
+# include <signal.h>
+ extern mutex_t GC_allocate_ml;
+# define LOCK() mutex_lock(&GC_allocate_ml);
+# define UNLOCK() mutex_unlock(&GC_allocate_ml);
+# endif
+
+/* Try to define GC_TEST_AND_SET and a matching GC_CLEAR for spin lock */
+/* acquisition and release. We need this for correct operation of the */
+/* incremental GC. */
+# ifdef __GNUC__
+# if defined(I386)
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ int oldval;
+ /* Note: the "xchg" instruction does not need a "lock" prefix */
+ __asm__ __volatile__("xchgl %0, %1"
+ : "=r"(oldval), "=m"(*(addr))
+ : "0"(1), "m"(*(addr)) : "memory");
+ return oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+# endif
+# if defined(IA64)
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ long oldval, n = 1;
+ __asm__ __volatile__("xchg4 %0=%1,%2"
+ : "=r"(oldval), "=m"(*addr)
+ : "r"(n), "1"(*addr) : "memory");
+ return oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+ /* Should this handle post-increment addressing?? */
+ inline static void GC_clear(volatile unsigned int *addr) {
+ __asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr) : : "memory");
+ }
+# define GC_CLEAR_DEFINED
+# endif
+# ifdef SPARC
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ int oldval;
+
+ __asm__ __volatile__("ldstub %1,%0"
+ : "=r"(oldval), "=m"(*addr)
+ : "m"(*addr) : "memory");
+ return oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+# endif
+# ifdef M68K
+ /* Contributed by Tony Mantler. I'm not sure how well it was */
+ /* tested. */
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ char oldval; /* this must be no longer than 8 bits */
+
+ /* The return value is semi-phony. */
+ /* 'tas' sets bit 7 while the return */
+ /* value pretends bit 0 was set */
+ __asm__ __volatile__(
+ "tas %1@; sne %0; negb %0"
+ : "=d" (oldval)
+ : "a" (addr) : "memory");
+ return oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+# endif
+# if defined(POWERPC)
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ int oldval;
+ int temp = 1; // locked value
+
+ __asm__ __volatile__(
+ "1:\tlwarx %0,0,%3\n" // load and reserve
+ "\tcmpwi %0, 0\n" // if load is
+ "\tbne 2f\n" // non-zero, return already set
+ "\tstwcx. %2,0,%1\n" // else store conditional
+ "\tbne- 1b\n" // retry if lost reservation
+ "2:\t\n" // oldval is zero if we set
+ : "=&r"(oldval), "=p"(addr)
+ : "r"(temp), "1"(addr)
+ : "memory");
+ return (int)oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+ inline static void GC_clear(volatile unsigned int *addr) {
+ __asm__ __volatile__("eieio" ::: "memory");
+ *(addr) = 0;
+ }
+# define GC_CLEAR_DEFINED
+# endif
+# if defined(ALPHA)
+ inline static int GC_test_and_set(volatile unsigned int * addr)
+ {
+ unsigned long oldvalue;
+ unsigned long temp;
+
+ __asm__ __volatile__(
+ "1: ldl_l %0,%1\n"
+ " and %0,%3,%2\n"
+ " bne %2,2f\n"
+ " xor %0,%3,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,3f\n"
+ " mb\n"
+ "2:\n"
+ ".section .text2,\"ax\"\n"
+ "3: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue)
+ :"Ir" (1), "m" (*addr)
+ :"memory");
+
+ return oldvalue;
+ }
+# define GC_TEST_AND_SET_DEFINED
+ /* Should probably also define GC_clear, since it needs */
+ /* a memory barrier ?? */
+# endif /* ALPHA */
+# ifdef ARM32
+ inline static int GC_test_and_set(volatile unsigned int *addr) {
+ int oldval;
+ /* SWP on ARM is very similar to XCHG on x86. Doesn't lock the
+ * bus because there are no SMP ARM machines. If/when there are,
+ * this code will likely need to be updated. */
+ /* See linuxthreads/sysdeps/arm/pt-machine.h in glibc-2.1 */
+ __asm__ __volatile__("swp %0, %1, [%2]"
+ : "=r"(oldval)
+ : "r"(1), "r"(addr)
+ : "memory");
+ return oldval;
+ }
+# define GC_TEST_AND_SET_DEFINED
+# endif /* ARM32 */
+# endif /* __GNUC__ */
+# if (defined(ALPHA) && !defined(__GNUC__))
+# define GC_test_and_set(addr) __cxx_test_and_set_atomic(addr, 1)
+# define GC_TEST_AND_SET_DEFINED
+# endif
+# if defined(MSWIN32)
+# define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1)
+# define GC_TEST_AND_SET_DEFINED
+# endif
+# ifdef MIPS
+# if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
+ || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700
+# define GC_test_and_set(addr, v) test_and_set(addr,v)
+# else
+# define GC_test_and_set(addr, v) __test_and_set(addr,v)
+# define GC_clear(addr) __lock_release(addr);
+# define GC_CLEAR_DEFINED
+# endif
+# define GC_TEST_AND_SET_DEFINED
+# endif /* MIPS */
+# if 0 /* defined(HP_PA) */
+ /* The official recommendation seems to be to not use ldcw from */
+ /* user mode. Since multithreaded incremental collection doesn't */
+ /* work anyway on HP_PA, this shouldn't be a major loss. */
+
+ /* "set" means 0 and "clear" means 1 here. */
+# define GC_test_and_set(addr) !GC_test_and_clear(addr);
+# define GC_TEST_AND_SET_DEFINED
+# define GC_clear(addr) GC_noop1(addr); *(volatile unsigned int *)addr = 1;
+ /* The above needs a memory barrier! */
+# define GC_CLEAR_DEFINED
+# endif
+# if defined(GC_TEST_AND_SET_DEFINED) && !defined(GC_CLEAR_DEFINED)
+# ifdef __GNUC__
+ inline static void GC_clear(volatile unsigned int *addr) {
+ /* Try to discourage gcc from moving anything past this. */
+ __asm__ __volatile__(" " : : : "memory");
+ *(addr) = 0;
+ }
+# else
+ /* The function call in the following should prevent the */
+ /* compiler from moving assignments to below the UNLOCK. */
+# define GC_clear(addr) GC_noop1((word)(addr)); \
+ *((volatile unsigned int *)(addr)) = 0;
+# endif
+# define GC_CLEAR_DEFINED
+# endif /* !GC_CLEAR_DEFINED */
+
+# if !defined(GC_TEST_AND_SET_DEFINED)
+# define USE_PTHREAD_LOCKS
+# endif
+
+# if defined(LINUX_THREADS) || defined(OSF1_THREADS) \
+ || defined(HPUX_THREADS)
+# define NO_THREAD (pthread_t)(-1)
+# include <pthread.h>
+# if defined(PARALLEL_MARK)
+ /* We need compare-and-swap to update mark bits, where it's */
+ /* performance critical. If USE_MARK_BYTES is defined, it is */
+ /* no longer needed for this purpose. However we use it in */
+ /* either case to implement atomic fetch-and-add, though that's */
+ /* less performance critical, and could perhaps be done with */
+ /* a lock. */
+# if defined(GENERIC_COMPARE_AND_SWAP)
+ /* Probably not useful, except for debugging. */
+ /* We do use GENERIC_COMPARE_AND_SWAP on PA_RISC, but we */
+ /* minimize its use. */
+ extern pthread_mutex_t GC_compare_and_swap_lock;
+
+ /* Note that if GC_word updates are not atomic, a concurrent */
+ /* reader should acquire GC_compare_and_swap_lock. On */
+ /* currently supported platforms, such updates are atomic. */
+ extern GC_bool GC_compare_and_exchange(volatile GC_word *addr,
+ GC_word old, GC_word new_val);
+# endif /* GENERIC_COMPARE_AND_SWAP */
+# if defined(I386)
+# if !defined(GENERIC_COMPARE_AND_SWAP)
+ /* Returns TRUE if the comparison succeeded. */
+ inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
+ GC_word old,
+ GC_word new_val)
+ {
+ char result;
+ __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1"
+ : "=m"(*(addr)), "=r"(result)
+ : "r" (new_val), "0"(*(addr)), "a"(old) : "memory");
+ return (GC_bool) result;
+ }
+# endif /* !GENERIC_COMPARE_AND_SWAP */
+ inline static void GC_memory_write_barrier()
+ {
+ /* We believe the processor ensures at least processor */
+ /* consistent ordering. Thus a compiler barrier */
+ /* should suffice. */
+ __asm__ __volatile__("" : : : "memory");
+ }
+# endif /* I386 */
+# if defined(IA64)
+# if !defined(GENERIC_COMPARE_AND_SWAP)
+ inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
+ GC_word old, GC_word new_val)
+ {
+ unsigned long oldval;
+ __asm__ __volatile__("mov ar.ccv=%4 ;; cmpxchg8.rel %0=%1,%2,ar.ccv"
+ : "=r"(oldval), "=m"(*addr)
+ : "r"(new_val), "1"(*addr), "r"(old) : "memory");
+ return (oldval == old);
+ }
+# endif /* !GENERIC_COMPARE_AND_SWAP */
+# if 0
+ /* Shouldn't be needed; we use volatile stores instead. */
+ inline static void GC_memory_write_barrier()
+ {
+ __asm__ __volatile__("mf" : : : "memory");
+ }
+# endif /* 0 */
+# endif /* IA64 */
+# if !defined(GENERIC_COMPARE_AND_SWAP)
+ /* Returns the original value of *addr. */
+ inline static GC_word GC_atomic_add(volatile GC_word *addr,
+ GC_word how_much)
+ {
+ GC_word old;
+ do {
+ old = *addr;
+ } while (!GC_compare_and_exchange(addr, old, old+how_much));
+ return old;
+ }
+# else /* GENERIC_COMPARE_AND_SWAP */
+ /* So long as a GC_word can be atomically updated, it should */
+ /* be OK to read *addr without a lock. */
+ extern GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much);
+# endif /* GENERIC_COMPARE_AND_SWAP */
+
+# endif /* PARALLEL_MARK */
+
+# if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
+ /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to */
+ /* be held for long periods, if it is held at all. Thus spinning */
+ /* and sleeping for fixed periods are likely to result in */
+ /* significant wasted time. We thus rely mostly on queued locks. */
+# define USE_SPIN_LOCK
+ extern volatile unsigned int GC_allocate_lock;
+ extern void GC_lock(void);
+ /* Allocation lock holder. Only set if acquired by client through */
+ /* GC_call_with_alloc_lock. */
+# ifdef GC_ASSERTIONS
+# define LOCK() \
+ { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); \
+ SET_LOCK_HOLDER(); }
+# define UNLOCK() \
+ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
+ GC_clear(&GC_allocate_lock); }
+# else
+# define LOCK() \
+ { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); }
+# define UNLOCK() \
+ GC_clear(&GC_allocate_lock)
+# endif /* !GC_ASSERTIONS */
+# if 0
+ /* Another alternative for OSF1 might be: */
+# include <sys/mman.h>
+ extern msemaphore GC_allocate_semaphore;
+# define LOCK() { if (msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) \
+ != 0) GC_lock(); else GC_allocate_lock = 1; }
+ /* The following is INCORRECT, since the memory model is too weak. */
+ /* Is this true? Presumably msem_unlock has the right semantics? */
+ /* - HB */
+# define UNLOCK() { GC_allocate_lock = 0; \
+ msem_unlock(&GC_allocate_semaphore, 0); }
+# endif /* 0 */
+# else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
+# ifndef USE_PTHREAD_LOCKS
+# define USE_PTHREAD_LOCKS
+# endif
+# endif /* THREAD_LOCAL_ALLOC */
+# ifdef USE_PTHREAD_LOCKS
+# include <pthread.h>
+ extern pthread_mutex_t GC_allocate_ml;
+# ifdef GC_ASSERTIONS
+# define LOCK() \
+ { GC_lock(); \
+ SET_LOCK_HOLDER(); }
+# define UNLOCK() \
+ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
+ pthread_mutex_unlock(&GC_allocate_ml); }
+# else /* !GC_ASSERTIONS */
+# define LOCK() \
+ { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); }
+# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+# endif /* !GC_ASSERTIONS */
+# endif /* USE_PTHREAD_LOCKS */
+# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
+ extern VOLATILE GC_bool GC_collecting;
+# define ENTER_GC() GC_collecting = 1;
+# define EXIT_GC() GC_collecting = 0;
+ extern void GC_lock(void);
+ extern pthread_t GC_lock_holder;
+# ifdef GC_ASSERTIONS
+ extern pthread_t GC_mark_lock_holder;
+# endif
+# endif /* LINUX_THREADS || OSF1_THREADS || HPUX_THREADS */
+# if defined(IRIX_THREADS)
+# include <pthread.h>
+ /* This probably should never be included, but I can't test */
+ /* on Irix anymore. */
+# include <mutex.h>
+
+ extern unsigned long GC_allocate_lock;
+ /* This is not a mutex because mutexes that obey the (optional) */
+ /* POSIX scheduling rules are subject to convoys in high contention */
+ /* applications. This is basically a spin lock. */
+ extern pthread_t GC_lock_holder;
+ extern void GC_lock(void);
+ /* Allocation lock holder. Only set if acquired by client through */
+ /* GC_call_with_alloc_lock. */
+# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
+# define NO_THREAD (pthread_t)(-1)
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
+# define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); }
+# define UNLOCK() GC_clear(&GC_allocate_lock);
+ extern VOLATILE GC_bool GC_collecting;
+# define ENTER_GC() \
+ { \
+ GC_collecting = 1; \
+ }
+# define EXIT_GC() GC_collecting = 0;
+# endif /* IRIX_THREADS */
+# ifdef WIN32_THREADS
+# include <windows.h>
+ GC_API CRITICAL_SECTION GC_allocate_ml;
+# define LOCK() EnterCriticalSection(&GC_allocate_ml);
+# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
+# endif
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
+# endif
+# else /* !THREADS */
+# define LOCK()
+# define UNLOCK()
+# endif /* !THREADS */
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
+# endif
+# ifndef ENTER_GC
+# define ENTER_GC()
+# define EXIT_GC()
+# endif
+
+# ifndef DCL_LOCK_STATE
+# define DCL_LOCK_STATE
+# endif
+# ifndef FASTLOCK
+# define FASTLOCK() LOCK()
+# define FASTLOCK_SUCCEEDED() TRUE
+# define FASTUNLOCK() UNLOCK()
+# endif
+
+#endif /* GC_LOCKS_H */
diff --git a/boehm-gc/include/private/gc_pmark.h b/boehm-gc/include/private/gc_pmark.h
new file mode 100644
index 0000000..1592b6f
--- /dev/null
+++ b/boehm-gc/include/private/gc_pmark.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 2001 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.
+ *
+ */
+
+/* Private declarations of GC marker data structures and macros */
+
+/*
+ * Declarations of mark stack. Needed by marker and client supplied mark
+ * routines. Transitively include gc_priv.h.
+ * (Note that gc_priv.h should not be included before this, since this
+ * includes dbg_mlc.h, which wants to include gc_priv.h AFTER defining
+ * I_HIDE_POINTERS.)
+ */
+#ifndef GC_PMARK_H
+# define GC_PMARK_H
+
+# ifdef KEEP_BACK_PTRS
+# include "dbg_mlc.h"
+# endif
+# ifndef GC_MARK_H
+# include "../gc_mark.h"
+# endif
+# ifndef GC_PRIVATE_H
+# include "gc_priv.h"
+# endif
+
+/* The real declarations of the following is in gc_priv.h, so that */
+/* we can avoid scanning the following table. */
+/*
+extern mark_proc GC_mark_procs[MAX_MARK_PROCS];
+*/
+
+/*
+ * Mark descriptor stuff that should remain private for now, mostly
+ * because it's hard to export WORDSZ without including gcconfig.h.
+ */
+# define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS)
+# define PROC(descr) \
+ (GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)])
+# define ENV(descr) \
+ ((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS))
+# define MAX_ENV \
+ (((word)1 << (WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS)) - 1)
+
+
+extern word GC_n_mark_procs;
+
+/* Number of mark stack entries to discard on overflow. */
+#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8)
+
+typedef struct GC_ms_entry {
+ GC_word * mse_start; /* First word of object */
+ GC_word mse_descr; /* Descriptor; low order two bits are tags, */
+ /* identifying the upper 30 bits as one of the */
+ /* following: */
+} mse;
+
+extern word GC_mark_stack_size;
+
+extern mse * GC_mark_stack_limit;
+
+#ifdef PARALLEL_MARK
+ extern mse * VOLATILE GC_mark_stack_top;
+#else
+ extern mse * GC_mark_stack_top;
+#endif
+
+extern mse * GC_mark_stack;
+
+#ifdef PARALLEL_MARK
+ /*
+ * Allow multiple threads to participate in the marking process.
+ * This works roughly as follows:
+ * The main mark stack never shrinks, but it can grow.
+ *
+ * The initiating threads holds the GC lock, and sets GC_help_wanted.
+ *
+ * Other threads:
+ * 1) update helper_count (while holding mark_lock.)
+ * 2) allocate a local mark stack
+ * repeatedly:
+ * 3) Steal a global mark stack entry by atomically replacing
+ * its descriptor with 0.
+ * 4) Copy it to the local stack.
+ * 5) Mark on the local stack until it is empty, or
+ * it may be profitable to copy it back.
+ * 6) If necessary, copy local stack to global one,
+ * holding mark lock.
+ * 7) Stop when the global mark stack is empty.
+ * 8) decrement helper_count (holding mark_lock).
+ *
+ * This is an experiment to see if we can do something along the lines
+ * of the University of Tokyo SGC in a less intrusive, though probably
+ * also less performant, way.
+ */
+ void GC_do_parallel_mark();
+ /* inititate parallel marking. */
+
+ extern GC_bool GC_help_wanted; /* Protected by mark lock */
+ extern unsigned GC_helper_count; /* Number of running helpers. */
+ /* Protected by mark lock */
+ extern unsigned GC_active_count; /* Number of active helpers. */
+ /* Protected by mark lock */
+ /* May increase and decrease */
+ /* within each mark cycle. But */
+ /* once it returns to 0, it */
+ /* stays zero for the cycle. */
+ /* GC_mark_stack_top is also protected by mark lock. */
+ extern mse * VOLATILE GC_first_nonempty;
+ /* Lowest entry on mark stack */
+ /* that may be nonempty. */
+ /* Updated only by initiating */
+ /* thread. */
+ /*
+ * GC_notify_all_marker() is used when GC_help_wanted is first set,
+ * when the last helper becomes inactive,
+ * when something is added to the global mark stack, and just after
+ * GC_mark_no is incremented.
+ * This could be split into multiple CVs (and probably should be to
+ * scale to really large numbers of processors.)
+ */
+#endif /* PARALLEL_MARK */
+
+ptr_t GC_find_start();
+
+mse * GC_signal_mark_stack_overflow();
+
+# ifdef GATHERSTATS
+# define ADD_TO_ATOMIC(sz) GC_atomic_in_use += (sz)
+# define ADD_TO_COMPOSITE(sz) GC_composite_in_use += (sz)
+# else
+# define ADD_TO_ATOMIC(sz)
+# define ADD_TO_COMPOSITE(sz)
+# endif
+
+/* Push the object obj with corresponding heap block header hhdr onto */
+/* the mark stack. */
+# define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \
+{ \
+ register word _descr = (hhdr) -> hb_descr; \
+ \
+ if (_descr == 0) { \
+ ADD_TO_ATOMIC((hhdr) -> hb_sz); \
+ } else { \
+ ADD_TO_COMPOSITE((hhdr) -> hb_sz); \
+ mark_stack_top++; \
+ if (mark_stack_top >= mark_stack_limit) { \
+ mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \
+ } \
+ mark_stack_top -> mse_start = (obj); \
+ mark_stack_top -> mse_descr = _descr; \
+ } \
+}
+
+#ifdef PRINT_BLACK_LIST
+# define GC_FIND_START(current, hhdr, source) \
+ GC_find_start(current, hhdr, source)
+#else
+# define GC_FIND_START(current, hhdr, source) \
+ GC_find_start(current, hhdr)
+#endif
+
+/* Push the contents of current onto the mark stack if it is a valid */
+/* ptr to a currently unmarked object. Mark it. */
+/* If we assumed a standard-conforming compiler, we could probably */
+/* generate the exit_label transparently. */
+# define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
+ source, exit_label) \
+{ \
+ hdr * my_hhdr; \
+ ptr_t my_current = current; \
+ \
+ GET_HDR(my_current, my_hhdr); \
+ if (IS_FORWARDING_ADDR_OR_NIL(my_hhdr)) { \
+ my_current = GC_FIND_START(my_current, my_hhdr, (word)source); \
+ if (my_current == 0) goto exit_label; \
+ my_hhdr = GC_find_header(my_current); \
+ } \
+ PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
+ source, exit_label, my_hhdr); \
+exit_label: ; \
+}
+
+/* As above, but use header cache for header lookup. */
+# define HC_PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
+ source, exit_label) \
+{ \
+ hdr * my_hhdr; \
+ ptr_t my_current = current; \
+ \
+ HC_GET_HDR(my_current, my_hhdr, source); \
+ PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \
+ source, exit_label, my_hhdr); \
+exit_label: ; \
+}
+
+/* As above, but deal with two pointers in interleaved fashion. */
+# define HC_PUSH_CONTENTS2(current1, current2, mark_stack_top, \
+ mark_stack_limit, \
+ source1, source2, exit_label1, exit_label2) \
+{ \
+ hdr * hhdr1; \
+ ptr_t my_current1 = current1; \
+ hdr * hhdr2; \
+ ptr_t my_current2 = current2; \
+ \
+ HC_GET_HDR2(my_current1, hhdr1, source1, my_current2, hhdr2, source2); \
+ PUSH_CONTENTS_HDR(my_current1, mark_stack_top, mark_stack_limit, \
+ source1, exit_label1, hhdr1); \
+exit_label1: ; \
+ if (0 != hhdr2) { \
+ PUSH_CONTENTS_HDR(my_current2, mark_stack_top, mark_stack_limit, \
+ source2, exit_label2, hhdr2); \
+ } \
+exit_label2: ; \
+}
+
+/* Set mark bit, exit if it was already set. */
+
+# ifdef USE_MARK_BYTES
+ /* Unlike the mark bit case, there is a race here, and we may set */
+ /* the bit twice in the concurrent case. This can result in the */
+ /* object being pushed twice. But that's only a performance issue. */
+# define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \
+ { \
+ register VOLATILE char * mark_byte_addr = \
+ hhdr -> hb_marks + ((displ) >> 1); \
+ register char mark_byte = *mark_byte_addr; \
+ \
+ if (mark_byte) goto exit_label; \
+ *mark_byte_addr = 1; \
+ }
+# else
+# define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \
+ { \
+ register word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(displ); \
+ register word mark_word = *mark_word_addr; \
+ \
+ OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(displ), \
+ exit_label); \
+ }
+# endif /* USE_MARK_BYTES */
+
+/* If the mark bit corresponding to current is not set, set it, and */
+/* push the contents of the object on the mark stack. Since we */
+/* already have the header, we only look at the low order bits of */
+/* current. (The value of current doesn't matter if hhdr = */
+/* GC_invalid_header.) */
+# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
+ source, exit_label, hhdr) \
+{ \
+ int displ; /* Displacement in block; first bytes, then words */ \
+ int map_entry; \
+ \
+ displ = HBLKDISPL(current); \
+ map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \
+ displ = BYTES_TO_WORDS(displ); \
+ if (map_entry > CPP_MAX_OFFSET) { \
+ if (map_entry == OFFSET_TOO_BIG) { \
+ map_entry = displ % (hhdr -> hb_sz); \
+ displ -= map_entry; \
+ if (displ + (hhdr -> hb_sz) > BYTES_TO_WORDS(HBLKSIZE)) { \
+ GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); \
+ goto exit_label; \
+ } \
+ } else { \
+ GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); goto exit_label; \
+ } \
+ } else { \
+ displ -= map_entry; \
+ } \
+ GC_ASSERT(displ >= 0 && displ < MARK_BITS_PER_HBLK); \
+ SET_MARK_BIT_EXIT_IF_SET(hhdr, displ, exit_label); \
+ GC_STORE_BACK_PTR((ptr_t)source, (ptr_t)HBLKPTR(current) \
+ + WORDS_TO_BYTES(displ)); \
+ PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \
+ mark_stack_top, mark_stack_limit) \
+}
+
+#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
+# define PUSH_ONE_CHECKED_STACK(p, source) \
+ GC_mark_and_push_stack(p, (ptr_t)(source))
+#else
+# define PUSH_ONE_CHECKED_STACK(p, source) \
+ GC_mark_and_push_stack(p)
+#endif
+
+/*
+ * Push a single value onto mark stack. Mark from the object pointed to by p.
+ * P is considered valid even if it is an interior pointer.
+ * Previously marked objects are not pushed. Hence we make progress even
+ * if the mark stack overflows.
+ */
+# define GC_PUSH_ONE_STACK(p, source) \
+ if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
+ && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
+ PUSH_ONE_CHECKED_STACK(p, source); \
+ }
+
+/*
+ * As above, but interior pointer recognition as for
+ * normal for heap pointers.
+ */
+# define GC_PUSH_ONE_HEAP(p,source) \
+ if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
+ && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
+ GC_mark_stack_top = GC_mark_and_push( \
+ (GC_PTR)(p), GC_mark_stack_top, \
+ GC_mark_stack_limit, (GC_PTR *)(source)); \
+ }
+
+/* Mark starting at mark stack entry top (incl.) down to */
+/* mark stack entry bottom (incl.). Stop after performing */
+/* about one page worth of work. Return the new mark stack */
+/* top entry. */
+mse * GC_mark_from GC_PROTO((mse * top, mse * bottom, mse *limit));
+
+#define MARK_FROM_MARK_STACK() \
+ GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \
+ GC_mark_stack, \
+ GC_mark_stack + GC_mark_stack_size);
+
+/*
+ * Mark from one finalizable object using the specified
+ * mark proc. May not mark the object pointed to by
+ * real_ptr. That is the job of the caller, if appropriate
+ */
+# define GC_MARK_FO(real_ptr, mark_proc) \
+{ \
+ (*(mark_proc))(real_ptr); \
+ while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK(); \
+ if (GC_mark_state != MS_NONE) { \
+ GC_set_mark_bit(real_ptr); \
+ while (!GC_mark_some((ptr_t)0)); \
+ } \
+}
+
+extern GC_bool GC_mark_stack_too_small;
+ /* We need a larger mark stack. May be */
+ /* set by client supplied mark routines.*/
+
+typedef int mark_state_t; /* Current state of marking, as follows:*/
+ /* Used to remember where we are during */
+ /* concurrent marking. */
+
+ /* We say something is dirty if it was */
+ /* written since the last time we */
+ /* retrieved dirty bits. We say it's */
+ /* grungy if it was marked dirty in the */
+ /* last set of bits we retrieved. */
+
+ /* Invariant I: all roots and marked */
+ /* objects p are either dirty, or point */
+ /* to objects q that are either marked */
+ /* or a pointer to q appears in a range */
+ /* on the mark stack. */
+
+# define MS_NONE 0 /* No marking in progress. I holds. */
+ /* Mark stack is empty. */
+
+# define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */
+ /* being pushed. I holds, except */
+ /* that grungy roots may point to */
+ /* unmarked objects, as may marked */
+ /* grungy objects above scan_ptr. */
+
+# define MS_PUSH_UNCOLLECTABLE 2
+ /* I holds, except that marked */
+ /* uncollectable objects above scan_ptr */
+ /* may point to unmarked objects. */
+ /* Roots may point to unmarked objects */
+
+# define MS_ROOTS_PUSHED 3 /* I holds, mark stack may be nonempty */
+
+# define MS_PARTIALLY_INVALID 4 /* I may not hold, e.g. because of M.S. */
+ /* overflow. However marked heap */
+ /* objects below scan_ptr point to */
+ /* marked or stacked objects. */
+
+# define MS_INVALID 5 /* I may not hold. */
+
+extern mark_state_t GC_mark_state;
+
+#endif /* GC_PMARK_H */
+
diff --git a/boehm-gc/include/private/solaris_threads.h b/boehm-gc/include/private/solaris_threads.h
new file mode 100644
index 0000000..b2cdb36
--- /dev/null
+++ b/boehm-gc/include/private/solaris_threads.h
@@ -0,0 +1,34 @@
+#ifdef SOLARIS_THREADS
+
+/* 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. In particular, the */
+/* definition of cond_t doesn't mention volatile! */
+ typedef struct GC_Thread_Rep {
+ struct GC_Thread_Rep * next;
+ thread_t id;
+ word flags;
+# define FINISHED 1 /* Thread has exited. */
+# define DETACHED 2 /* Thread is intended to be detached. */
+# define CLIENT_OWNS_STACK 4
+ /* Stack was supplied by client. */
+# define SUSPENDED 8 /* Currently suspended. */
+ ptr_t stack;
+ size_t stack_size;
+ cond_t join_cv;
+ void * status;
+ } * GC_thread;
+ extern GC_thread GC_new_thread(thread_t id);
+
+ extern GC_bool GC_thr_initialized;
+ extern volatile GC_thread GC_threads[];
+ extern size_t GC_min_stack_sz;
+ extern size_t GC_page_sz;
+ extern void GC_thr_init(void);
+
+# endif /* SOLARIS_THREADS */
+
diff --git a/boehm-gc/include/private/specific.h b/boehm-gc/include/private/specific.h
new file mode 100644
index 0000000..60c152c
--- /dev/null
+++ b/boehm-gc/include/private/specific.h
@@ -0,0 +1,83 @@
+/*
+ * This is a reimplementation of a subset of the pthread_getspecific/setspecific
+ * interface. This appears to outperform the standard linuxthreads one
+ * by a significant margin.
+ * The major restriction is that each thread may only make a single
+ * pthread_setspecific call on a single key. (The current data structure
+ * doesn't really require that. The restriction should be easily removable.)
+ * We don't currently support the destruction functions, though that
+ * could be done.
+ * We also currently assume that only one pthread_setspecific call
+ * can be executed at a time, though that assumption would be easy to remove
+ * by adding a lock.
+ */
+
+#include <errno.h>
+
+/* Called during key creation or setspecific. */
+/* For the GC we already hold lock. */
+/* Currently allocated objects leak on thread exit. */
+/* That's hard to fix, but OK if we allocate garbage */
+/* collected memory. */
+#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
+#define PREFIXED(name) GC_##name
+
+#define TS_CACHE_SIZE 1024
+#define CACHE_HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_CACHE_SIZE - 1))
+#define TS_HASH_SIZE 1024
+#define HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_HASH_SIZE - 1))
+
+typedef struct thread_specific_entry {
+ unsigned long qtid; /* quick thread id, only for cache */
+ void * value;
+ pthread_t thread;
+ struct thread_specific_entry *next;
+} tse;
+
+
+/* We represent each thread-specific datum as two tables. The first is */
+/* a cache, index by a "quick thread identifier". The "quick" thread */
+/* identifier is an easy to compute value, which is guaranteed to */
+/* determine the thread, though a thread may correspond to more than */
+/* one value. We typically use the address of a page in the stack. */
+/* The second is a hash table, indexed by pthread_self(). It is used */
+/* only as a backup. */
+
+/* Return the "quick thread id". Default version. Assumes page size, */
+/* or at least thread stack separation, is at least 4K. */
+static __inline__ long quick_thread_id() {
+ int dummy;
+ return (long)(&dummy) >> 12;
+}
+
+#define INVALID_QTID ((unsigned long)(-1))
+
+typedef struct thread_specific_data {
+ tse * volatile cache[TS_CACHE_SIZE];
+ /* A faster index to the hash table */
+ tse * hash[TS_HASH_SIZE];
+ pthread_mutex_t lock;
+} tsd;
+
+typedef tsd * PREFIXED(key_t);
+
+extern int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *));
+
+extern int PREFIXED(setspecific) (tsd * key, void * value);
+
+extern void PREFIXED(remove_specific) (tsd * key);
+
+/* An internal version of getspecific that assumes a cache miss. */
+void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid,
+ tse * volatile * cache_entry);
+
+static __inline__ void * PREFIXED(getspecific) (tsd * key) {
+ long qtid = quick_thread_id();
+ unsigned hash_val = CACHE_HASH(qtid);
+ tse * volatile * entry_ptr = key -> cache + hash_val;
+ tse * entry = *entry_ptr; /* Must be loaded only once. */
+ if (entry -> qtid == qtid) return entry -> value;
+ return PREFIXED(slow_getspecific) (key, qtid, entry_ptr);
+}
+
+