diff options
Diffstat (limited to '')
-rw-r--r-- | gc/include/cord.h | 2 | ||||
-rw-r--r-- | gc/include/gc.h | 310 | ||||
-rw-r--r-- | gc/include/gc_alloc.h | 3 | ||||
-rw-r--r-- | gc/include/gc_amiga_redirects.h | 30 | ||||
-rw-r--r-- | gc/include/gc_backptr.h (renamed from gc/include/backptr.h) | 11 | ||||
-rw-r--r-- | gc/include/gc_cpp.h | 114 | ||||
-rw-r--r-- | gc/include/gc_gcj.h | 104 | ||||
-rw-r--r-- | gc/include/gc_inl.h | 12 | ||||
-rw-r--r-- | gc/include/gc_local_alloc.h | 85 | ||||
-rw-r--r-- | gc/include/gc_mark.h | 145 | ||||
-rw-r--r-- | gc/include/gc_pthread_redirects.h | 69 | ||||
-rw-r--r-- | gc/include/gc_typed.h | 2 | ||||
-rw-r--r-- | gc/include/new_gc_alloc.h | 21 | ||||
-rw-r--r-- | gc/include/private/dbg_mlc.h | 153 | ||||
-rw-r--r-- | gc/include/private/gc_hdrs.h | 98 | ||||
-rw-r--r-- | gc/include/private/gc_locks.h | 481 | ||||
-rw-r--r-- | gc/include/private/gc_pmark.h | 388 | ||||
-rw-r--r-- | gc/include/private/gc_priv.h | 1433 | ||||
-rw-r--r-- | gc/include/private/gcconfig.h | 776 | ||||
-rw-r--r-- | gc/include/private/solaris_threads.h (renamed from gc/solaris_threads.h) | 0 | ||||
-rw-r--r-- | gc/include/private/specific.h | 83 |
21 files changed, 3472 insertions, 848 deletions
diff --git a/gc/include/cord.h b/gc/include/cord.h index 584112f..926089e 100644 --- a/gc/include/cord.h +++ b/gc/include/cord.h @@ -41,7 +41,7 @@ * This interface is fairly big, largely for performance reasons. * The most basic constants and functions: * - * CORD - the type fo a cord; + * CORD - the type of a cord; * CORD_EMPTY - empty cord; * CORD_len(cord) - length of a cord; * CORD_cat(cord1,cord2) - concatenation of two cords; diff --git a/gc/include/gc.h b/gc/include/gc.h index 3061409..aaf29ee 100644 --- a/gc/include/gc.h +++ b/gc/include/gc.h @@ -1,7 +1,8 @@ /* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. - * Copyright 1996 by Silicon Graphics. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 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. @@ -28,16 +29,70 @@ #ifndef _GC_H # define _GC_H + +#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +#endif + +/* + * Some tests for old macros. These violate our namespace rules and will + * disappear shortly. + */ +#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(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_LINUX_THREADS)) +# define _REENTRANT + /* Better late than never. This fails if system headers that */ + /* depend on this were previously included. */ +#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(__CYGWIN32__) && defined(GC_USE_DLL) -#include "libgc_globals.h" +#if defined(__MINGW32__) && defined(WIN32_THREADS) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif #endif -#if defined(_MSC_VER) && defined(_DLL) +#if (defined(__DMC__) || defined(_MSC_VER)) \ + && (defined(_DLL) && !defined(GC_NOT_DLL) \ + || defined(GC_DLL)) # ifdef GC_BUILD -# define GC_API __declspec(dllexport) +# define GC_API extern __declspec(dllexport) # else # define GC_API __declspec(dllimport) # endif @@ -58,9 +113,11 @@ # if defined(__STDC__) || defined(__cplusplus) # define GC_PROTO(args) args typedef void * GC_PTR; +# define GC_CONST const # else # define GC_PROTO(args) () typedef char * GC_PTR; +# define GC_CONST # endif # ifdef __cplusplus @@ -83,6 +140,16 @@ typedef long GC_signed_word; GC_API GC_word GC_gc_no;/* Counter incremented per collection. */ /* Includes empty GCs at startup. */ + +GC_API int GC_parallel; /* GC is parallelized for performance on */ + /* multiprocessors. Currently set only */ + /* implicitly if collector is built with */ + /* -DPARALLEL_MARK and if either: */ + /* Env variable GC_NPROC is set to > 1, or */ + /* GC_NPROC is not set and this is an MP. */ + /* If GC_parallel is set, incremental */ + /* collection is aonly partially functional, */ + /* and may not be desirable. */ /* Public R/W variables */ @@ -102,6 +169,18 @@ GC_API int GC_find_leak; /* deallocated with GC_free. Initial value */ /* is determined by FIND_LEAK macro. */ +GC_API int GC_all_interior_pointers; + /* Arrange for pointers to object interiors to */ + /* be recognized as valid. May not be changed */ + /* after GC initialization. */ + /* Initial value is determined by */ + /* -DALL_INTERIOR_POINTERS. */ + /* Unless DONT_ADD_BYTE_AT_END is defined, this */ + /* also affects whether sizes are increased by */ + /* at least a byte to allow "off the end" */ + /* pointer recognition. */ + /* MUST BE 0 or 1. */ + GC_API int GC_quiet; /* Disable statistics output. Only matters if */ /* collector has been compiled with statistics */ /* enabled. This involves a performance cost, */ @@ -109,7 +188,7 @@ GC_API int GC_quiet; /* Disable statistics output. Only matters if */ GC_API int GC_finalize_on_demand; /* If nonzero, finalizers will only be run in */ - /* response to an eplit GC_invoke_finalizers */ + /* response to an explicit GC_invoke_finalizers */ /* call. The default is determined by whether */ /* the FINALIZE_ON_DEMAND macro is defined */ /* when the collector is built. */ @@ -121,6 +200,15 @@ GC_API int GC_java_finalization; /* ordered finalization. Default value is */ /* determined by JAVA_FINALIZATION macro. */ +GC_API void (* GC_finalizer_notifier)(); + /* Invoked by the collector when there are */ + /* objects to be finalized. Invoked at most */ + /* once per GC cycle. Never invoked unless */ + /* GC_finalize_on_demand is set. */ + /* Typically this will notify a finalization */ + /* thread, which will call GC_invoke_finalizers */ + /* in response. */ + GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */ /* because it's not safe. */ @@ -128,13 +216,40 @@ GC_API int GC_dont_expand; /* Dont expand heap unless explicitly requested */ /* or forced to. */ +GC_API int GC_use_entire_heap; + /* Causes the nonincremental collector to use the */ + /* entire heap before collecting. This was the only */ + /* option for GC versions < 5.0. This sometimes */ + /* results in more large block fragmentation, since */ + /* very larg blocks will tend to get broken up */ + /* during each GC cycle. It is likely to result in a */ + /* larger working set, but lower collection */ + /* frequencies, and hence fewer instructions executed */ + /* in the collector. */ + GC_API int GC_full_freq; /* Number of partial collections between */ /* full collections. Matters only if */ /* GC_incremental is set. */ + /* Full collections are also triggered if */ + /* the collector detects a substantial */ + /* increase in the number of in-use heap */ + /* blocks. Values in the tens are now */ + /* perfectly reasonable, unlike for */ + /* earlier GC versions. */ GC_API GC_word GC_non_gc_bytes; /* Bytes not considered candidates for collection. */ /* Used only to control scheduling of collections. */ + /* Updated by GC_malloc_uncollectable and GC_free. */ + /* Wizards only. */ + +GC_API int GC_no_dls; + /* Don't register dynamic library data segments. */ + /* Wizards only. Should be used only if the */ + /* application explicitly registers all roots. */ + /* In Microsoft Windows environments, this will */ + /* usually also prevent registration of the */ + /* main data segment as part of the root set. */ GC_API GC_word GC_free_space_divisor; /* We try to make sure that we allocate at */ @@ -165,8 +280,16 @@ GC_API char *GC_stackbottom; /* Cool end of user stack. */ /* automatically. */ /* For multithreaded code, this is the */ /* cold end of the stack for the */ - /* primordial thread. */ + /* primordial thread. */ +GC_API int GC_dont_precollect; /* Don't collect as part of */ + /* initialization. Should be set only */ + /* if the client wants a chance to */ + /* manually initialize the root set */ + /* before the first collection. */ + /* Interferes with blacklisting. */ + /* Wizards only. */ + /* Public procedures */ /* * general purpose allocation routines, with roughly malloc calling conv. @@ -176,8 +299,13 @@ GC_API char *GC_stackbottom; /* Cool end of user stack. */ * will occur after GC_end_stubborn_change has been called on the * result of GC_malloc_stubborn. GC_malloc_uncollectable allocates an object * that is scanned for pointers to collectable objects, but is not itself - * collectable. GC_malloc_uncollectable and GC_free called on the resulting + * collectable. The object is scanned even if it does not appear to + * be reachable. GC_malloc_uncollectable and GC_free called on the resulting * object implicitly update GC_non_gc_bytes appropriately. + * + * Note that the GC_malloc_stubborn support is stubbed out by default + * starting in 6.0. GC_malloc_stubborn is an alias for GC_malloc unless + * the collector is built with STUBBORN_ALLOC defined. */ GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes)); GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes)); @@ -216,6 +344,10 @@ GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR)); /* Return a pointer to the base (lowest address) of an object given */ /* a pointer to a location within the object. */ +/* I.e. map an interior pointer to the corresponding bas pointer. */ +/* Note that with debugging allocation, this returns a pointer to the */ +/* actual base of the object, i.e. the debug information, not to */ +/* the base of the user object. */ /* Return 0 if displaced_pointer doesn't point to within a valid */ /* object. */ GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer)); @@ -297,15 +429,24 @@ GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func)); /* Includes some pages that were allocated but never written. */ GC_API size_t GC_get_heap_size GC_PROTO((void)); +/* Return a lower bound on the number of free bytes in the heap. */ +GC_API size_t GC_get_free_bytes GC_PROTO((void)); + /* Return the number of bytes allocated since the last collection. */ GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); +/* Return the total number of bytes allocated in this process. */ +/* Never decreases. */ +GC_API size_t GC_get_total_bytes GC_PROTO((void)); + /* Enable incremental/generational collection. */ /* Not advisable unless dirty bits are */ /* available or most heap objects are */ /* pointerfree(atomic) or immutable. */ /* Don't use in leak finding mode. */ /* Ignored if GC_dont_gc is true. */ +/* Only the generational piece of this is */ +/* functional if GC_parallel is TRUE. */ GC_API void GC_enable_incremental GC_PROTO((void)); /* Perform some garbage collection work, if appropriate. */ @@ -341,10 +482,10 @@ GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); #ifdef GC_ADD_CALLER # define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ -# define GC_EXTRA_PARAMS GC_word ra, char * descr_string, int descr_int +# define GC_EXTRA_PARAMS GC_word ra, GC_CONST char * s, int i #else # define GC_EXTRAS __FILE__, __LINE__ -# define GC_EXTRA_PARAMS char * descr_string, int descr_int +# define GC_EXTRA_PARAMS GC_CONST char * s, int i #endif /* Debugging (annotated) allocation. GC_gcollect will check */ @@ -375,6 +516,8 @@ GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); GC_debug_register_finalizer(p, f, d, of, od) # define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) # define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS); # define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p) # define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) @@ -391,6 +534,8 @@ GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); GC_register_finalizer(p, f, d, of, od) # define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) # define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz) # define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) # define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) @@ -462,6 +607,8 @@ GC_API void GC_debug_register_finalizer /* but it's unavoidable for C++, since the compiler may */ /* silently introduce these. It's also benign in that specific */ /* case. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ GC_API void GC_register_finalizer_ignore_self GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, GC_finalization_proc *ofn, GC_PTR *ocd)); @@ -469,6 +616,18 @@ GC_API void GC_debug_register_finalizer_ignore_self GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, GC_finalization_proc *ofn, GC_PTR *ocd)); +/* Another version of the above. It ignores all cycles. */ +/* It should probably only be used by Java implementations. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ +GC_API void GC_register_finalizer_no_order + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer_no_order + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + + /* The following routine may be used to break cycles between */ /* finalizable objects, thus causing cyclic finalizable */ /* objects to be finalized in the correct order. Standard */ @@ -525,6 +684,9 @@ GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); +/* Returns !=0 if GC_invoke_finalizers has something to do. */ +GC_API int GC_should_invoke_finalizers GC_PROTO((void)); + GC_API int GC_invoke_finalizers GC_PROTO((void)); /* Run finalizers for all objects that are ready to */ /* be finalized. Return the number of finalizers */ @@ -540,7 +702,7 @@ GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); /* Returns old warning procedure. */ /* The following is intended to be used by a higher level */ -/* (e.g. cedar-like) finalization facility. It is expected */ +/* (e.g. Java-like) finalization facility. It is expected */ /* that finalization code will arrange for hidden pointers to */ /* disappear. Otherwise objects can be accessed after they */ /* have been collected. */ @@ -560,6 +722,9 @@ typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); GC_API GC_PTR GC_call_with_alloc_lock GC_PROTO((GC_fn_type fn, GC_PTR client_data)); +/* The following routines are primarily intended for use with a */ +/* preprocessor which inserts calls to check C pointer arithmetic. */ + /* Check that p and q point to the same object. */ /* Fail conspicuously if they don't. */ /* Returns the first argument. */ @@ -587,7 +752,7 @@ GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p)); /* Check that if p is a pointer to a heap page, then it points to */ /* a valid displacement within a heap object. */ /* Fail conspicuously if this property does not hold. */ -/* Uninteresting with ALL_INTERIOR_POINTERS. */ +/* Uninteresting with GC_all_interior_pointers. */ /* Always returns its argument. */ GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p)); @@ -603,9 +768,9 @@ GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p)); # ifdef __GNUC__ # define GC_PTR_ADD(x, n) \ GC_PTR_ADD3(x, n, typeof(x)) -# define GC_PRE_INCR(x, n) \ +# define GC_PRE_INCR(x, n) \ GC_PRE_INCR3(x, n, typeof(x)) -# define GC_POST_INCR(x, n) \ +# define GC_POST_INCR(x, n) \ GC_POST_INCR3(x, typeof(x)) # else /* We can't do this right without typeof, which ANSI */ @@ -645,74 +810,28 @@ GC_API void (*GC_is_valid_displacement_print_proc) GC_API void (*GC_is_visible_print_proc) GC_PROTO((GC_PTR p)); -#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) -# define SOLARIS_THREADS -#endif -#ifdef 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, which are deallocated using the destructor */ -/* facility in thr_keycreate. */ -# include <thread.h> -# include <signal.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); - -# ifdef _SOLARIS_PTHREADS -# include <pthread.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); - -# undef thread_t - -# define pthread_join GC_pthread_join -# define pthread_create GC_pthread_create -#endif +/* For pthread support, we generally need to intercept a number of */ +/* thread library calls. We do that here by macro defining them. */ -# define thr_create GC_thr_create -# define thr_join GC_thr_join -# define thr_suspend GC_thr_suspend -# define thr_continue GC_thr_continue -# define dlopen GC_dlopen - -# endif /* SOLARIS_THREADS */ - - -#if defined(IRIX_THREADS) || defined(LINUX_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); - -# define pthread_create GC_pthread_create -# define pthread_sigmask GC_pthread_sigmask -# define pthread_join GC_pthread_join - -#endif /* IRIX_THREADS || LINUX_THREADS */ +#if !defined(GC_USE_LD_WRAP) && \ + (defined(GC_LINUX_THREADS) || defined(GC_HPUX_THREADS) || \ + defined(GC_IRIX_THREADS) || defined(GC_SOLARIS_PTHREADS) || \ + defined(GC_SOLARIS_THREADS) || defined(GC_OSF1_THREADS)) +# include "gc_pthread_redirects.h" +#endif -# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ - defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ - defined(IRIX_JDK_THREADS) +# if defined(PCR) || defined(GC_SOLARIS_THREADS) || \ + defined(GC_SOLARIS_PTHREADS) || defined(GC_WIN32_THREADS) || \ + defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) || \ + defined(GC_HPUX_THREADS) /* Any flavor of threads except SRC_M3. */ /* This returns a list of objects, linked through their first */ /* word. Its use can greatly reduce lock contention problems, since */ /* the allocation lock can be acquired and released many fewer times. */ /* lb must be large enough to hold the pointer field. */ +/* It is used internally by gc_local_alloc.h, which provides a simpler */ +/* programming interface on Linux. */ GC_PTR GC_malloc_many(size_t lb); #define GC_NEXT(p) (*(GC_PTR *)(p)) /* Retrieve the next element */ /* in returned list. */ @@ -720,6 +839,35 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ #endif /* THREADS && !SRC_M3 */ +#if defined(WIN32_THREADS) && defined(_WIN32_WCE) +# include <windows.h> + + /* + * win32_threads.c implements the real WinMain, which will start a new thread + * to call GC_WinMain after initializing the garbage collector. + */ + int WINAPI GC_WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, + int nCmdShow ); + + /* + * All threads must be created using GC_CreateThread, so that they will be + * recorded in the thread table. + */ + HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); + +# ifndef GC_BUILD +# define WinMain GC_WinMain +# define CreateThread GC_CreateThread +# endif + +#endif + /* * If you are planning on putting * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() @@ -740,13 +888,23 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif #endif -#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ - || defined(_WIN32) +#if !defined(_WIN32_WCE) \ + && ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)) /* win32S may not free all resources on process exit. */ /* This explicitly deallocates the heap. */ GC_API void GC_win32_free_heap (); #endif +#if ( defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB) ) + /* Allocation really goes through GC_amiga_allocwrapper_do */ +# include "gc_amiga_redirects.h" +#endif + +#if defined(GC_REDIRECT_TO_LOCAL) && !defined(GC_LOCAL_ALLOC_H) +# include "gc_local_alloc.h" +#endif + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/gc/include/gc_alloc.h b/gc/include/gc_alloc.h index 1f1d54a..c50a758 100644 --- a/gc/include/gc_alloc.h +++ b/gc/include/gc_alloc.h @@ -25,6 +25,9 @@ // This uses template classes with static members, and hence does not work // with g++ 2.7.2 and earlier. // +// This code assumes that the collector itself has been compiled with a +// compiler that defines __STDC__ . +// #include "gc.h" diff --git a/gc/include/gc_amiga_redirects.h b/gc/include/gc_amiga_redirects.h new file mode 100644 index 0000000..9e975c8 --- /dev/null +++ b/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/gc/include/backptr.h b/gc/include/gc_backptr.h index d34224e..5899496 100644 --- a/gc/include/backptr.h +++ b/gc/include/gc_backptr.h @@ -20,6 +20,8 @@ * 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 */ @@ -29,9 +31,11 @@ /* 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 refence info available. */ +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; @@ -53,4 +57,9 @@ void * GC_generate_random_valid_address(void); /* 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/gc/include/gc_cpp.h b/gc/include/gc_cpp.h index ad7df5d..ceb73f5 100644 --- a/gc/include/gc_cpp.h +++ b/gc/include/gc_cpp.h @@ -16,12 +16,11 @@ the code was modified is included with the above copyright notice. C++ Interface to the Boehm Collector John R. Ellis and Jesse Hull - Last modified on Mon Jul 24 15:43:42 PDT 1995 by ellis This interface provides access to the Boehm collector. It provides basic facilities similar to those described in "Safe, Efficient Garbage Collection for C++", by John R. Elis and David L. Detlefs -(ftp.parc.xerox.com:/pub/ellis/gc). +(ftp://ftp.parc.xerox.com/pub/ellis/gc). All heap-allocated objects are either "collectable" or "uncollectable". Programs must explicitly delete uncollectable @@ -38,7 +37,7 @@ Objects derived from class "gc" are collectable. For example: A* a = new A; // a is collectable. Collectable instances of non-class types can be allocated using the GC -placement: +(or UseGC) placement: typedef int A[ 10 ]; A* a = new (GC) A; @@ -84,7 +83,7 @@ Cautions: 1. Be sure the collector has been augmented with "make c++". 2. If your compiler supports the new "operator new[]" syntax, then -add -DOPERATOR_NEW_ARRAY to the Makefile. +add -DGC_OPERATOR_NEW_ARRAY to the Makefile. If your compiler doesn't support "operator new[]", beware that an array of type T, where T is derived from "gc", may or may not be @@ -124,6 +123,12 @@ invoked using the ANSI-conforming syntax t->~T(). If you're using cfront 3.0, you'll have to comment out the class gc_cleanup, which uses explicit invocation. +5. GC name conflicts: + +Many other systems seem to use the identifier "GC" as an abbreviation +for "Graphics Context". Since version 5.0, GC placement has been replaced +by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. + ****************************************************************************/ #include "gc.h" @@ -132,24 +137,35 @@ uses explicit invocation. #define _cdecl #endif -#if ! defined( OPERATOR_NEW_ARRAY ) \ - && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6) \ - || __WATCOMC__ >= 1050) -# define OPERATOR_NEW_ARRAY +#if ! defined( GC_NO_OPERATOR_NEW_ARRAY ) \ + && !defined(_ENABLE_ARRAYNEW) /* Digimars */ \ + && (defined(__BORLANDC__) && (__BORLANDC__ < 0x450) \ + || (defined(__GNUC__) && \ + (__GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 6)) \ + || (defined(__WATCOMC__) && __WATCOMC__ < 1050)) +# define GC_NO_OPERATOR_NEW_ARRAY #endif -enum GCPlacement {GC, NoGC, PointerFreeGC}; +#if !defined(GC_NO_OPERATOR_NEW_ARRAY) && !defined(GC_OPERATOR_NEW_ARRAY) +# define GC_OPERATOR_NEW_ARRAY +#endif + +enum GCPlacement {UseGC, +#ifndef GC_NAME_CONFLICT + GC=UseGC, +#endif + NoGC, PointerFreeGC}; class gc {public: inline void* operator new( size_t size ); inline void* operator new( size_t size, GCPlacement gcp ); inline void operator delete( void* obj ); -#ifdef OPERATOR_NEW_ARRAY +#ifdef GC_OPERATOR_NEW_ARRAY inline void* operator new[]( size_t size ); inline void* operator new[]( size_t size, GCPlacement gcp ); inline void operator delete[]( void* obj ); -#endif /* OPERATOR_NEW_ARRAY */ +#endif /* GC_OPERATOR_NEW_ARRAY */ }; /* Instances of classes derived from "gc" will be allocated in the @@ -170,6 +186,12 @@ private: extern "C" {typedef void (*GCCleanUpFunc)( void* obj, void* clientData );} +#ifdef _MSC_VER + // Disable warning that "no matching operator delete found; memory will + // not be freed if initialization throws an exception" +# pragma warning(disable:4291) +#endif + inline void* operator new( size_t size, GCPlacement gcp, @@ -189,7 +211,50 @@ inline void* operator new( classes derived from "gc_cleanup" or containing members derived from "gc_cleanup". */ -#ifdef OPERATOR_NEW_ARRAY +#ifdef GC_OPERATOR_NEW_ARRAY + +#ifdef _MSC_VER + /** This ensures that the system default operator new[] doesn't get + * undefined, which is what seems to happen on VC++ 6 for some reason + * if we define a multi-argument operator new[]. + * There seems to be really redirect new in this environment without + * including this everywhere. + */ + inline void *operator new[]( size_t size ) + { + return GC_MALLOC_UNCOLLECTABLE( size ); + } + + inline void operator delete[](void* obj) + { + GC_FREE(obj); + }; + + inline void* operator new( size_t size) + { + return GC_MALLOC_UNCOLLECTABLE( size); + }; + + inline void operator delete(void* obj) + { + GC_FREE(obj); + }; + + +// This new operator is used by VC++ in case of Debug builds ! + inline void* operator new( size_t size, + int ,//nBlockUse, + const char * szFileName, + int nLine + ) { +# ifndef GC_DEBUG + return GC_malloc_uncollectable( size ); +# else + return GC_debug_malloc_uncollectable(size, szFileName, nLine); +# endif + } + +#endif /* _MSC_VER */ inline void* operator new[]( size_t size, @@ -199,7 +264,7 @@ inline void* operator new[]( /* The operator new for arrays, identical to the above. */ -#endif /* OPERATOR_NEW_ARRAY */ +#endif /* GC_OPERATOR_NEW_ARRAY */ /**************************************************************************** @@ -211,7 +276,7 @@ inline void* gc::operator new( size_t size ) { return GC_MALLOC( size );} inline void* gc::operator new( size_t size, GCPlacement gcp ) { - if (gcp == GC) + if (gcp == UseGC) return GC_MALLOC( size ); else if (gcp == PointerFreeGC) return GC_MALLOC_ATOMIC( size ); @@ -222,7 +287,7 @@ inline void gc::operator delete( void* obj ) { GC_FREE( obj );} -#ifdef OPERATOR_NEW_ARRAY +#ifdef GC_OPERATOR_NEW_ARRAY inline void* gc::operator new[]( size_t size ) { return gc::operator new( size );} @@ -233,7 +298,7 @@ inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { inline void gc::operator delete[]( void* obj ) { gc::operator delete( obj );} -#endif /* OPERATOR_NEW_ARRAY */ +#endif /* GC_OPERATOR_NEW_ARRAY */ inline gc_cleanup::~gc_cleanup() { @@ -246,12 +311,13 @@ inline gc_cleanup::gc_cleanup() { GC_finalization_proc oldProc; void* oldData; void* base = GC_base( (void *) this ); - if (0 == base) return; - GC_REGISTER_FINALIZER_IGNORE_SELF( - base, cleanup, (void*) ((char*) this - (char*) base), + if (0 != base) { + // Don't call the debug version, since this is a real base address. + GC_register_finalizer_ignore_self( + base, (GC_finalization_proc)cleanup, (void*) ((char*) this - (char*) base), &oldProc, &oldData ); - if (0 != oldProc) { - GC_REGISTER_FINALIZER_IGNORE_SELF( base, oldProc, oldData, 0, 0 );}} + if (0 != oldProc) { + GC_register_finalizer_ignore_self( base, oldProc, oldData, 0, 0 );}}} inline void* operator new( size_t size, @@ -261,7 +327,7 @@ inline void* operator new( { void* obj; - if (gcp == GC) { + if (gcp == UseGC) { obj = GC_MALLOC( size ); if (cleanup != 0) GC_REGISTER_FINALIZER_IGNORE_SELF( @@ -273,7 +339,7 @@ inline void* operator new( return obj;} -#ifdef OPERATOR_NEW_ARRAY +#ifdef GC_OPERATOR_NEW_ARRAY inline void* operator new[]( size_t size, @@ -283,7 +349,7 @@ inline void* operator new[]( { return ::operator new( size, gcp, cleanup, clientData );} -#endif /* OPERATOR_NEW_ARRAY */ +#endif /* GC_OPERATOR_NEW_ARRAY */ #endif /* GC_CPP_H */ diff --git a/gc/include/gc_gcj.h b/gc/include/gc_gcj.h new file mode 100644 index 0000000..9123a28 --- /dev/null +++ b/gc/include/gc_gcj.h @@ -0,0 +1,104 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 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 file assumes the collector has been compiled with GC_GCJ_SUPPORT */ +/* and that an ANSI C compiler is available. */ + +/* + * We allocate objects whose first word contains a pointer to a struct + * describing the object type. This struct contains a garbage collector mark + * descriptor at offset MARK_DESCR_OFFSET. Alternatively, the objects + * may be marked by the mark procedure passed to GC_init_gcj_malloc. + */ + +#ifndef GC_GCJ_H + +#define GC_GCJ_H + +#ifndef MARK_DESCR_OFFSET +# define MARK_DESCR_OFFSET sizeof(word) +#endif + /* Gcj keeps GC descriptor as second word of vtable. This */ + /* probably needs to be adjusted for other clients. */ + /* We currently assume that this offset is such that: */ + /* - all objects of this kind are large enough to have */ + /* a value at that offset, and */ + /* - it is not zero. */ + /* These assumptions allow objects on the free list to be */ + /* marked normally. */ + +#ifndef _GC_H +# include "gc.h" +#endif + +/* The following allocators signal an out of memory condition with */ +/* return GC_oom_fn(bytes); */ + +extern void * (*GC_oom_action)(void); + +/* The following function must be called before the gcj allocators */ +/* can be invoked. */ +/* mp_index and mp are the index and mark_proc (see gc_mark.h) */ +/* respectively for the allocated objects. Mark_proc will be */ +/* used to build the descriptor for objects allocated through the */ +/* debugging interface. The mark_proc will be invoked on all such */ +/* objects with an "environment" value of 1. The client may chose */ +/* to use the same mark_proc for some of its generated mark descriptors.*/ +/* In that case, it should use a different "environment" value to */ +/* detect the presence or absence of the debug header. */ +/* Mp is really of type mark_proc, as defined in gc_mark.h. We don't */ +/* want to include that here for namespace pollution reasons. */ +extern void GC_init_gcj_malloc(int mp_index, void * /* really mark_proc */mp); + +/* Allocate an object, clear it, and store the pointer to the */ +/* type structure (vtable in gcj). */ +/* This adds a byte at the end of the object if GC_malloc would.*/ +extern void * GC_gcj_malloc(size_t lb, void * ptr_to_struct_containing_descr); +/* The debug versions allocate such that the specified mark_proc */ +/* is always invoked. */ +extern void * GC_debug_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr, + GC_EXTRA_PARAMS); + +/* Similar to the above, but the size is in words, and we don't */ +/* adjust it. The size is assumed to be such that it can be */ +/* allocated as a small object. */ +/* Unless it is known that the collector is not configured */ +/* with USE_MARK_BYTES and unless it is known that the object */ +/* has weak alignment requirements, lw must be even. */ +extern void * GC_gcj_fast_malloc(size_t lw, + void * ptr_to_struct_containing_descr); +extern void * GC_debug_gcj_fast_malloc(size_t lw, + void * ptr_to_struct_containing_descr, + GC_EXTRA_PARAMS); + +/* Similar to GC_gcj_malloc, but assumes that a pointer to near the */ +/* beginning of the resulting object is always maintained. */ +extern void * GC_gcj_malloc_ignore_off_page(size_t lb, + void * ptr_to_struct_containing_descr); + +# ifdef GC_DEBUG +# define GC_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +# define GC_GCJ_FAST_MALLOC(s,d) GC_debug_gcj_fast_malloc(s,d,GC_EXTRAS) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +# else +# define GC_GCJ_MALLOC(s,d) GC_gcj_malloc(s,d) +# define GC_GCJ_FAST_MALLOC(s,d) GC_gcj_fast_malloc(s,d) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) \ + GC_gcj_malloc_ignore_off_page(s,d) +# endif + +#endif /* GC_GCJ_H */ diff --git a/gc/include/gc_inl.h b/gc/include/gc_inl.h index 700843b..c535cfd 100644 --- a/gc/include/gc_inl.h +++ b/gc/include/gc_inl.h @@ -17,18 +17,22 @@ # include "private/gc_priv.h" # endif -/* USE OF THIS FILE IS NOT RECOMMENDED unless the collector has been */ -/* compiled without -DALL_INTERIOR_POINTERS or with */ +/* USE OF THIS FILE IS NOT RECOMMENDED unless GC_all_interior_pointers */ +/* is always set, or the collector has been built with */ /* -DDONT_ADD_BYTE_AT_END, or the specified size includes a pointerfree */ /* word at the end. In the standard collector configuration, */ /* the final word of each object may not be scanned. */ -/* This is most useful for compilers that generate C. */ +/* This iinterface is most useful for compilers that generate C. */ /* Manual use is hereby discouraged. */ /* Allocate n words (NOT BYTES). X is made to point to the result. */ /* It is assumed that n < MAXOBJSZ, and */ /* that n > 0. On machines requiring double word alignment of some */ -/* data, we also assume that n is 1 or even. This bypasses the */ +/* data, we also assume that n is 1 or even. */ +/* If the collector is built with -DUSE_MARK_BYTES or -DPARALLEL_MARK, */ +/* the n = 1 case is also disallowed. */ +/* Effectively this means that portable code should make sure n is even.*/ +/* This bypasses the */ /* MERGE_SIZES mechanism. In order to minimize the number of distinct */ /* free lists that are maintained, the caller should ensure that a */ /* small number of distinct values of n are used. (The MERGE_SIZES */ diff --git a/gc/include/gc_local_alloc.h b/gc/include/gc_local_alloc.h new file mode 100644 index 0000000..1e58730 --- /dev/null +++ b/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/gc/include/gc_mark.h b/gc/include/gc_mark.h new file mode 100644 index 0000000..0856d16 --- /dev/null +++ b/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/gc/include/gc_pthread_redirects.h b/gc/include/gc_pthread_redirects.h new file mode 100644 index 0000000..ac254a8 --- /dev/null +++ b/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/gc/include/gc_typed.h b/gc/include/gc_typed.h index e4a6b94..2e0598f 100644 --- a/gc/include/gc_typed.h +++ b/gc/include/gc_typed.h @@ -61,6 +61,7 @@ GC_API GC_PTR GC_malloc_explicitly_typed GC_PROTO((size_t size_in_bytes, GC_descr d)); /* Allocate an object whose layout is described by d. */ /* The resulting object MAY NOT BE PASSED TO REALLOC. */ + /* The returned object is cleared. */ GC_API GC_PTR GC_malloc_explicitly_typed_ignore_off_page GC_PROTO((size_t size_in_bytes, GC_descr d)); @@ -75,6 +76,7 @@ GC_API GC_PTR GC_calloc_explicitly_typed /* alignment required for pointers. E.g. on a 32-bit */ /* machine with 16-bit aligned pointers, size_in_bytes */ /* must be a multiple of 2. */ + /* Returned object is cleared. */ #ifdef GC_DEBUG # define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes) diff --git a/gc/include/new_gc_alloc.h b/gc/include/new_gc_alloc.h index 5771388..1187f0b 100644 --- a/gc/include/new_gc_alloc.h +++ b/gc/include/new_gc_alloc.h @@ -20,9 +20,9 @@ // It also doesn't yet understand the new header file names or // namespaces. // -// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE -// and -DALL_INTERIOR_POINTERS. We also recommend -// -DREDIRECT_MALLOC=GC_uncollectable_malloc. +// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE. +// The user should also consider -DREDIRECT_MALLOC=GC_uncollectable_malloc, +// to ensure that object allocated through malloc are traced. // // Some of this could be faster in the explicit deallocation case. // In particular, we spend too much time clearing objects on the @@ -43,11 +43,14 @@ // problems. The argument for changing it is that the usual default // allocator is usually a very bad choice for a garbage collected environment.) // +// This code assumes that the collector itself has been compiled with a +// compiler that defines __STDC__ . +// #ifndef GC_ALLOC_H #include "gc.h" -#include <alloc.h> +#include <stack> // A more portable way to get stl_alloc.h . #define GC_ALLOC_H @@ -318,12 +321,10 @@ class traceable_alloc_template { typedef traceable_alloc_template < 0 > traceable_alloc; -#ifdef _SGI_SOURCE - // We want to specialize simple_alloc so that it does the right thing // for all pointerfree types. At the moment there is no portable way to // even approximate that. The following approximation should work for -// SGI compilers, and perhaps some others. +// SGI compilers, and recent versions of g++. # define __GC_SPECIALIZE(T,alloc) \ class simple_alloc<T, alloc> { \ @@ -339,6 +340,8 @@ public: \ { alloc::ptr_free_deallocate(p, sizeof (T)); } \ }; +__STL_BEGIN_NAMESPACE + __GC_SPECIALIZE(char, gc_alloc) __GC_SPECIALIZE(int, gc_alloc) __GC_SPECIALIZE(unsigned, gc_alloc) @@ -363,6 +366,8 @@ __GC_SPECIALIZE(unsigned, single_client_traceable_alloc) __GC_SPECIALIZE(float, single_client_traceable_alloc) __GC_SPECIALIZE(double, single_client_traceable_alloc) +__STL_END_NAMESPACE + #ifdef __STL_USE_STD_ALLOCATORS __STL_BEGIN_NAMESPACE @@ -451,6 +456,4 @@ __STL_END_NAMESPACE #endif /* __STL_USE_STD_ALLOCATORS */ -#endif /* _SGI_SOURCE */ - #endif /* GC_ALLOC_H */ diff --git a/gc/include/private/dbg_mlc.h b/gc/include/private/dbg_mlc.h new file mode 100644 index 0000000..6f5b3c8 --- /dev/null +++ b/gc/include/private/dbg_mlc.h @@ -0,0 +1,153 @@ +/* + * 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 files 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 + +#ifndef HIDE_POINTER + /* Gc.h was previously included, and hence the I_HIDE_POINTERS */ + /* definition had no effect. Repeat the gc.h definitions here to */ + /* get them anyway. */ + typedef GC_word GC_hidden_pointer; +# define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +# define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) +#endif /* HIDE_POINTER */ + +# 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. */ + +# if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) + /* Pointer "source"s that aren't real locations. */ + /* Used in oh_back_ptr fields and as "source" */ + /* argument to some marking functions. */ +# 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. */ +# endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ + +/* 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. */ + /* Note that blocks dropped by black-listing will */ + /* also have the lsb clear once debugging has */ + /* started. */ + /* The following are special back pointer values. */ + /* Note that the "hidden" (i.e. bitwise */ + /* complemented version) of these is actually */ + /* stored. */ +# 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 + GC_CONST 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. */ + +#ifdef SHORT_DBG_HDRS +# define DEBUG_BYTES (sizeof (oh)) +#else + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ +# define DEBUG_BYTES (sizeof (oh) + sizeof (word) - EXTRA_BYTES) +#endif +#define USR_PTR_FROM_BASE(p) ((ptr_t)(p) + sizeof(oh)) + +/* Round bytes to words without adding extra byte at end. */ +#define SIMPLE_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/gc/include/private/gc_hdrs.h b/gc/include/private/gc_hdrs.h index 60dc2ad..dd61545 100644 --- a/gc/include/private/gc_hdrs.h +++ b/gc/include/private/gc_hdrs.h @@ -24,6 +24,15 @@ typedef struct hblkhdr hdr; * The 2 level tree data structure that is used to find block headers. * If there are more than 32 bits in a pointer, the top level is a hash * table. + * + * This defines HDR, GET_HDR, and SET_HDR, the main macros used to + * retrieve and set object headers. + * + * Since 5.0 alpha 5, we can also take advantage of a header lookup + * cache. This is a locally declared direct mapped cache, used inside + * the marker. The HC_GET_HDR macro uses and maintains this + * cache. Assuming we get reasonable hit rates, this shaves a few + * memory references from each pointer validation. */ # if CPP_WORDSZ > 32 @@ -45,6 +54,95 @@ typedef struct hblkhdr hdr; # define TOP_SZ (1 << LOG_TOP_SZ) # define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) +#ifndef SMALL_CONFIG +# define USE_HDR_CACHE +#endif + +/* #define COUNT_HDR_CACHE_HITS */ + +extern hdr * GC_invalid_header; /* header for an imaginary block */ + /* containing no objects. */ + + +/* Check whether p and corresponding hhdr point to long or invalid */ +/* object. If so, advance hhdr to */ +/* beginning of block, or set hhdr to GC_invalid_header. */ +#define ADVANCE(p, hhdr, source) \ + { \ + hdr * new_hdr = GC_invalid_header; \ + p = GC_FIND_START(p, hhdr, &new_hdr, (word)source); \ + hhdr = new_hdr; \ + } + +#ifdef USE_HDR_CACHE + +# ifdef COUNT_HDR_CACHE_HITS + extern word GC_hdr_cache_hits; + extern word GC_hdr_cache_misses; +# define HC_HIT() ++GC_hdr_cache_hits +# define HC_MISS() ++GC_hdr_cache_misses +# else +# define HC_HIT() +# define HC_MISS() +# endif + + typedef struct hce { + word block_addr; /* right shifted by LOG_HBLKSIZE */ + hdr * hce_hdr; + } hdr_cache_entry; + +# define HDR_CACHE_SIZE 8 /* power of 2 */ + +# define DECLARE_HDR_CACHE \ + hdr_cache_entry hdr_cache[HDR_CACHE_SIZE] + +# define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache)); + +# define HCE(h) hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1)) + +# define HCE_VALID_FOR(hce,h) ((hce) -> block_addr == \ + ((word)(h) >> LOG_HBLKSIZE)) + +# define HCE_HDR(h) ((hce) -> hce_hdr) + + +/* Analogous to GET_HDR, except that in the case of large objects, it */ +/* Returns the header for the object beginning, and updates p. */ +/* Returns &GC_bad_header instead of 0. All of this saves a branch */ +/* in the fast path. */ +# define HC_GET_HDR(p, hhdr, source) \ + { \ + hdr_cache_entry * hce = HCE(p); \ + if (HCE_VALID_FOR(hce, p)) { \ + HC_HIT(); \ + hhdr = hce -> hce_hdr; \ + } else { \ + HC_MISS(); \ + GET_HDR(p, hhdr); \ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ + ADVANCE(p, hhdr, source); \ + } else { \ + hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; \ + hce -> hce_hdr = hhdr; \ + } \ + } \ + } + +#else /* !USE_HDR_CACHE */ + +# define DECLARE_HDR_CACHE + +# define INIT_HDR_CACHE + +# define HC_GET_HDR(p, hhdr, source) \ + { \ + GET_HDR(p, hhdr); \ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ + ADVANCE(p, hhdr, source); \ + } \ + } +#endif + typedef struct bi { hdr * index[BOTTOM_SZ]; /* diff --git a/gc/include/private/gc_locks.h b/gc/include/private/gc_locks.h new file mode 100644 index 0000000..eed9f10 --- /dev/null +++ b/gc/include/private/gc_locks.h @@ -0,0 +1,481 @@ +/* + * 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 + void GC_noop1 GC_PROTO((word)); +# 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((word)(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/gc/include/private/gc_pmark.h b/gc/include/private/gc_pmark.h new file mode 100644 index 0000000..cf70706 --- /dev/null +++ b/gc/include/private/gc_pmark.h @@ -0,0 +1,388 @@ +/* + * 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 + +# if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) +# 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 */ + +/* Return a pointer to within 1st page of object. */ +/* Set *new_hdr_p to corr. hdr. */ +#ifdef __STDC__ +# ifdef PRINT_BLACK_LIST + ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p, word source); +# else + ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p); +# endif +#else + ptr_t GC_find_start(); +#endif + +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, new_hdr_p, source) \ + GC_find_start(current, hhdr, new_hdr_p, source) +#else +# define GC_FIND_START(current, hhdr, new_hdr_p, source) \ + GC_find_start(current, hhdr, new_hdr_p) +#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)) { \ + hdr * new_hdr = GC_invalid_header; \ + my_current = GC_FIND_START(my_current, my_hhdr, \ + &new_hdr, (word)source); \ + my_hhdr = new_hdr; \ + } \ + 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: ; \ +} + +/* 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. For a small */ +/* object we assume that current is the (possibly interior) pointer */ +/* to the object. For large objects we assume that current points */ +/* to somewhere inside the first page of the object. If */ +/* GC_all_interior_pointers is set, it may have been previously */ +/* adjusted to make that true. */ +# 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/gc/include/private/gc_priv.h b/gc/include/private/gc_priv.h index 5ce52a7..5135e3e 100644 --- a/gc/include/private/gc_priv.h +++ b/gc/include/private/gc_priv.h @@ -1,6 +1,9 @@ /* * 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-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. @@ -11,7 +14,6 @@ * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ -/* Boehm, February 16, 1996 2:30 pm PST */ # ifndef GC_PRIVATE_H @@ -38,16 +40,16 @@ # include "gc.h" # endif -typedef GC_word word; -typedef GC_signed_word signed_word; +# ifndef GC_MARK_H +# include "../gc_mark.h" +# endif -# ifndef CONFIG_H +# ifndef GCCONFIG_H # include "gcconfig.h" # endif -# ifndef HEADERS_H -# include "gc_hdrs.h" -# endif +typedef GC_word word; +typedef GC_signed_word signed_word; typedef int GC_bool; # define TRUE 1 @@ -58,27 +60,54 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* Preferably identical to caddr_t, if it */ /* exists. */ +# ifndef HEADERS_H +# include "gc_hdrs.h" +# endif + #if defined(__STDC__) # include <stdlib.h> # if !(defined( sony_news ) ) # include <stddef.h> # endif # define VOLATILE volatile -# define CONST const #else # ifdef MSWIN32 # include <stdlib.h> # endif # define VOLATILE -# define CONST #endif -#if 0 /* was once defined for AMIGA */ +#if 0 /* defined(__GNUC__) doesn't work yet */ +# define EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define EXPECT(expr, outcome) (expr) +#endif /* __GNUC__ */ + +# ifndef GC_LOCKS_H +# include "gc_locks.h" +# endif + +# ifdef STACK_GROWS_DOWN +# define COOLER_THAN > +# define HOTTER_THAN < +# define MAKE_COOLER(x,y) if ((word)(x)+(y) > (word)(x)) {(x) += (y);} \ + else {(x) = (word)ONES;} +# define MAKE_HOTTER(x,y) (x) -= (y) +# else +# define COOLER_THAN < +# define HOTTER_THAN > +# define MAKE_COOLER(x,y) if ((word)(x)-(y) < (word)(x)) {(x) -= (y);} else {(x) = 0;} +# define MAKE_HOTTER(x,y) (x) += (y) +# endif + +#if defined(AMIGA) && defined(__SASC) # define GC_FAR __far #else # define GC_FAR #endif + /*********************************/ /* */ /* Definitions for conservative */ @@ -92,11 +121,9 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* */ /*********************************/ -#define STUBBORN_ALLOC /* Define stubborn allocation primitives */ -#if defined(SRC_M3) || defined(SMALL_CONFIG) -# undef STUBBORN_ALLOC -#endif - +/* #define STUBBORN_ALLOC */ + /* Enable stubborm allocation, and thus a limited */ + /* form of incremental collection w/o dirty bits. */ /* #define ALL_INTERIOR_POINTERS */ /* Forces all pointers into the interior of an */ @@ -123,6 +150,8 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* touched. */ /* If you can easily avoid using this option, do. */ /* If not, try to keep individual objects small. */ + /* This is now really controlled at startup, */ + /* through GC_all_interior_pointers. */ #define PRINTSTATS /* Print garbage collection statistics */ /* For less verbose output, undefine in reclaim.c */ @@ -153,12 +182,12 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # define GATHERSTATS #endif -#ifdef FINALIZE_ON_DEMAND -# define GC_INVOKE_FINALIZERS() -#else -# define GC_INVOKE_FINALIZERS() (void)GC_invoke_finalizers() +#if defined(PRINTSTATS) || !defined(SMALL_CONFIG) +# define CONDPRINT /* Print some things if GC_print_stats is set */ #endif +#define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers() + #define MERGE_SIZES /* Round up some object sizes, so that fewer distinct */ /* free lists are actually maintained. This applies */ /* only to the top level routines in misc.c, not to */ @@ -170,13 +199,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* May save significant amounts of space for obj_map */ /* entries. */ -#ifndef OLD_BLOCK_ALLOC - /* Macros controlling large block allocation strategy. */ -# define EXACT_FIRST /* Make a complete pass through the large object */ - /* free list before splitting a block */ -# define PRESERVE_LAST /* Do not divide last allocated heap segment */ - /* unless we would otherwise need to expand the */ - /* heap. */ +#if defined(USE_MARK_BYTES) && !defined(ALIGN_DOUBLE) +# define ALIGN_DOUBLE + /* We use one byte for every 2 words, which doesn't allow for */ + /* odd numbered words to have mark bits. */ #endif /* ALIGN_DOUBLE requires MERGE_SIZES at present. */ @@ -184,15 +210,17 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # define MERGE_SIZES # endif -#if defined(ALL_INTERIOR_POINTERS) && !defined(DONT_ADD_BYTE_AT_END) -# define ADD_BYTE_AT_END +#if !defined(DONT_ADD_BYTE_AT_END) +# define EXTRA_BYTES GC_all_interior_pointers +#else +# define EXTRA_BYTES 0 #endif # ifndef LARGE_CONFIG -# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ - /* Must be multiple of largest page size. */ -# define MAXHINCR 512 /* Maximum heap increment, in blocks */ +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ + /* Must be multiple of largest page size. */ +# define MAXHINCR 2048 /* Maximum heap increment, in blocks */ # else # define MINHINCR 64 # define MAXHINCR 4096 @@ -225,17 +253,22 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ * Number of frames and arguments to save in objects allocated by * debugging allocator. */ -# define NFRAMES 6 /* Number of frames to save. Even for */ +# ifndef SAVE_CALL_COUNT +# define NFRAMES 6 /* Number of frames to save. Even for */ /* alignment reasons. */ +# else +# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +# endif # define NARGS 2 /* Mumber of arguments to save for each call. */ # define NEED_CALLINFO /* Fill in the pc and argument information for up to NFRAMES of my */ /* callers. Ignore my frame and my callers frame. */ -void GC_save_callers (/* struct callinfo info[NFRAMES] */); - -void GC_print_callers (/* struct callinfo info[NFRAMES] */); +struct callinfo; +void GC_save_callers GC_PROTO((struct callinfo info[NFRAMES])); + +void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES])); #else @@ -249,7 +282,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); #ifdef NEED_CALLINFO struct callinfo { - word ci_pc; + word ci_pc; /* Caller, not callee, pc */ # if NARGS > 0 word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ # endif @@ -278,6 +311,13 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define MS_TIME_DIFF(a,b) ((double) (a.tv_sec - b.tv_sec) * 1000.0 \ + (double) (a.tv_usec - b.tv_usec) / 1000.0) #else /* !BSD_TIME */ +# if defined(MSWIN32) || defined(MSWINCE) +# include <windows.h> +# include <winbase.h> +# define CLOCK_TYPE DWORD +# define GET_TIME(x) x = GetTickCount() +# define MS_TIME_DIFF(a,b) ((long)((a)-(b))) +# else /* !MSWIN32, !MSWINCE, !BSD_TIME */ # include <time.h> # if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4) clock_t clock(); /* Not in time.h, where it belongs */ @@ -303,6 +343,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define GET_TIME(x) x = clock() # define MS_TIME_DIFF(a,b) ((unsigned long) \ (1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC)) +# endif /* !MSWIN32 */ #endif /* !BSD_TIME */ /* We use bzero and bcopy internally. They may not be available. */ @@ -322,6 +363,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # include <string.h> # define BCOPY_EXISTS # endif +# if defined(MACOSX) +# define BCOPY_EXISTS +# endif # ifndef BCOPY_EXISTS # include <string.h> @@ -339,6 +383,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); /* GET_MEM is currently not assumed to retrieve 0 filled space, */ /* though we should perhaps take advantage of the case in which */ /* does. */ +struct hblk; /* See below. */ # ifdef PCR char * real_malloc(); # define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ @@ -350,7 +395,8 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); + GC_page_size) \ + GC_page_size-1) # else -# if defined(AMIGA) || defined(NEXT) || defined(MACOSX) || defined(DOS4GW) +# if defined(NEXT) || defined(MACOSX) || defined(DOS4GW) || \ + (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) # define GET_MEM(bytes) HBLKPTR((size_t) \ calloc(1, (size_t)bytes + GC_page_size) \ + GC_page_size-1) @@ -371,198 +417,26 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); NewPtrClear(bytes + GC_page_size) + GC_page_size-1) # endif # else - extern ptr_t GC_unix_get_mem(); -# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# ifdef MSWINCE + extern ptr_t GC_wince_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +# else +# if defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) + extern void *GC_amiga_get_mem(size_t size); + define GET_MEM(bytes) HBLKPTR((size_t) \ + GC_amiga_get_mem((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else + extern ptr_t GC_unix_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +# endif # endif # endif # endif # endif # endif -/* - * 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.) - */ -# 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 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 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 -# ifdef LINUX_THREADS -# include <pthread.h> -# ifdef __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))); - return oldval; - } -# else - -- > Need implementation of GC_test_and_set() -# endif -# define GC_clear(addr) (*(addr) = 0) - - extern volatile unsigned int 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())) -# ifdef UNDEFINED -# define LOCK() pthread_mutex_lock(&GC_allocate_ml) -# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) -# else -# define LOCK() \ - { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); } -# define UNLOCK() \ - GC_clear(&GC_allocate_lock) -# endif - extern GC_bool GC_collecting; -# define ENTER_GC() \ - { \ - GC_collecting = 1; \ - } -# define EXIT_GC() GC_collecting = 0; -# endif /* LINUX_THREADS */ -# if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) -# include <pthread.h> -# include <mutex.h> - -# 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) -# endif - 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())) -# ifdef UNDEFINED -# define LOCK() pthread_mutex_lock(&GC_allocate_ml) -# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) -# else -# define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); } -# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \ - && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 700 -# define UNLOCK() __lock_release(&GC_allocate_lock) -# else - /* The function call in the following should prevent the */ - /* compiler from moving assignments to below the UNLOCK. */ - /* This is probably not necessary for ucode or gcc 2.8. */ - /* It may be necessary for Ragnarok and future gcc */ - /* versions. */ -# define UNLOCK() { GC_noop1(&GC_allocate_lock); \ - *(volatile unsigned long *)(&GC_allocate_lock) = 0; } -# endif -# endif - extern GC_bool GC_collecting; -# define ENTER_GC() \ - { \ - GC_collecting = 1; \ - } -# define EXIT_GC() GC_collecting = 0; -# endif /* IRIX_THREADS || IRIX_JDK_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 -# define LOCK() -# define UNLOCK() -# 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 -# 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 - /* Delay any interrupts or signals that may abort this thread. Data */ /* structures are in a consistent state outside this pair of calls. */ /* ANSI C allows both to be empty (though the standard isn't very */ @@ -576,9 +450,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); PCR_Th_SetSigMask(&GC_old_sig_mask, NIL) # else # if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ - || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ - || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ - || defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS) + || defined(MSWIN32) || defined(MSWINCE) || defined(MACOS) \ + || defined(DJGPP) || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ + || defined(LINUX_THREADS) /* Also useful for debugging. */ /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ # define DISABLE_SIGNALS() @@ -607,7 +481,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # else # if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ - || defined(IRIX_JDK_THREADS) + || defined(HPUX_THREADS) void GC_stop_world(); void GC_start_world(); # define STOP_WORLD() GC_stop_world() @@ -641,6 +515,13 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define WARN(msg,arg) (*GC_current_warn_proc)(msg, (GC_word)(arg)) extern GC_warn_proc GC_current_warn_proc; +/* Get environment entry */ +#if !defined(NO_GETENV) +# define GETENV(name) getenv(name) +#else +# define GETENV(name) 0 +#endif + /*********************************/ /* */ /* Word-size-dependent defines */ @@ -670,7 +551,7 @@ extern GC_warn_proc GC_current_warn_proc; #define WORDSZ ((word)CPP_WORDSZ) #define SIGNB ((word)1 << (WORDSZ-1)) #define BYTES_PER_WORD ((word)(sizeof (word))) -#define ONES ((word)(-1)) +#define ONES ((word)(signed_word)(-1)) #define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ /*********************/ @@ -723,6 +604,8 @@ extern GC_warn_proc GC_current_warn_proc; /* max size objects supported by freelist (larger objects may be */ /* allocated, but less efficiently) */ +#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2) +#define MAXOBJBYTES ((word)CPP_MAXOBJBYTES) #define CPP_MAXOBJSZ BYTES_TO_WORDS(CPP_HBLKSIZE/2) #define MAXOBJSZ ((word)CPP_MAXOBJSZ) @@ -743,25 +626,28 @@ extern GC_warn_proc GC_current_warn_proc; # define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1)) /* Round up byte allocation requests to integral number of words, etc. */ -# ifdef ADD_BYTE_AT_END -# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1)) -# ifdef ALIGN_DOUBLE -# define ALIGNED_WORDS(n) (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2)) & ~1) -# else -# define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) -# endif -# define SMALL_OBJ(bytes) ((bytes) < WORDS_TO_BYTES(MAXOBJSZ)) -# define ADD_SLOP(bytes) ((bytes)+1) -# else -# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1)) -# ifdef ALIGN_DOUBLE +# define ROUNDED_UP_WORDS(n) \ + BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1 + EXTRA_BYTES)) +# ifdef ALIGN_DOUBLE # define ALIGNED_WORDS(n) \ - (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1) & ~1) -# else + (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1 + EXTRA_BYTES) & ~1) +# else # define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) < (MAXOBJBYTES - EXTRA_BYTES)) +# define ADD_SLOP(bytes) ((bytes) + EXTRA_BYTES) +# ifndef MIN_WORDS + /* MIN_WORDS is the size of the smallest allocated object. */ + /* 1 and 2 are the only valid values. */ + /* 2 must be used if: */ + /* - GC_gcj_malloc can be used for objects of requested */ + /* size smaller than 2 words, or */ + /* - USE_MARK_BYTES is defined. */ +# if defined(USE_MARK_BYTES) || defined(GC_GCJ_SUPPORT) +# define MIN_WORDS 2 /* Smallest allocated object. */ +# else +# define MIN_WORDS 1 # endif -# define SMALL_OBJ(bytes) ((bytes) <= WORDS_TO_BYTES(MAXOBJSZ)) -# define ADD_SLOP(bytes) (bytes) # endif @@ -772,11 +658,19 @@ extern GC_warn_proc GC_current_warn_proc; */ # ifdef LARGE_CONFIG -# define LOG_PHT_ENTRIES 17 +# define LOG_PHT_ENTRIES 19 /* Collisions likely at 512K blocks, */ + /* which is >= 2GB. Each table takes */ + /* 64KB. */ # else -# define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */ - /* to more than 16K hblks = 64MB. */ - /* Each hash table occupies 2K bytes. */ +# ifdef SMALL_CONFIG +# define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */ + /* to more than 16K hblks = 64MB. */ + /* Each hash table occupies 2K bytes. */ +# else /* default "medium" configuration */ +# define LOG_PHT_ENTRIES 16 /* Collisions are likely if heap grows */ + /* to more than 16K hblks >= 256MB. */ + /* Each hash table occupies 8K bytes. */ +# endif # endif # define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) # define PHT_SIZE (PHT_ENTRIES >> LOGWL) @@ -790,6 +684,10 @@ typedef word page_hash_table[PHT_SIZE]; (bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) # define clear_pht_entry_from_index(bl, index) \ (bl)[divWORDSZ(index)] &= ~((word)1 << modWORDSZ(index)) +/* And a dumb but thread-safe version of set_pht_entry_from_index. */ +/* This sets (many) extra bits. */ +# define set_pht_entry_from_index_safe(bl, index) \ + (bl)[divWORDSZ(index)] = ONES @@ -802,20 +700,28 @@ typedef word page_hash_table[PHT_SIZE]; /* heap block header */ #define HBLKMASK (HBLKSIZE-1) -#define BITS_PER_HBLK (HBLKSIZE * 8) +#define BITS_PER_HBLK (CPP_HBLKSIZE * 8) #define MARK_BITS_PER_HBLK (BITS_PER_HBLK/CPP_WORDSZ) /* upper bound */ - /* We allocate 1 bit/word. Only the first word */ + /* We allocate 1 bit/word, unless USE_MARK_BYTES */ + /* is defined. Only the first word */ /* in each object is actually marked. */ -# ifdef ALIGN_DOUBLE -# define MARK_BITS_SZ (((MARK_BITS_PER_HBLK + 2*CPP_WORDSZ - 1) \ - / (2*CPP_WORDSZ))*2) +# ifdef USE_MARK_BYTES +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/2) + /* Unlike the other case, this is in units of bytes. */ + /* We actually allocate only every second mark bit, since we */ + /* force all objects to be doubleword aligned. */ + /* However, each mark bit is allocated as a byte. */ # else -# define MARK_BITS_SZ ((MARK_BITS_PER_HBLK + CPP_WORDSZ - 1)/CPP_WORDSZ) +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ) # endif - /* Upper bound on number of mark words per heap block */ + +/* We maintain layout maps for heap blocks containing objects of a given */ +/* size. Each entry in this map describes a byte offset and has the */ +/* following type. */ +typedef unsigned char map_entry_type; struct hblkhdr { word hb_sz; /* If in use, size in words, of objects in the block. */ @@ -826,7 +732,8 @@ struct hblkhdr { struct hblk * hb_prev; /* Backwards link for free list. */ word hb_descr; /* object descriptor for marking. See */ /* mark.h. */ - char* hb_map; /* A pointer to a pointer validity map of the block. */ + map_entry_type * hb_map; + /* A pointer to a pointer validity map of the block. */ /* See GC_obj_map. */ /* Valid for all blocks with headers. */ /* Free blocks point to GC_invalid_map. */ @@ -848,51 +755,49 @@ struct hblkhdr { /* Value of GC_gc_no when block was */ /* last allocated or swept. May wrap. */ /* For a free block, this is maintained */ - /* unly for USE_MUNMAP, and indicates */ + /* only for USE_MUNMAP, and indicates */ /* when the header was allocated, or */ /* when the size of the block last */ /* changed. */ - word hb_marks[MARK_BITS_SZ]; +# ifdef USE_MARK_BYTES + union { + char _hb_marks[MARK_BITS_SZ]; + /* The i'th byte is 1 if the object */ + /* starting at word 2i is marked, 0 o.w. */ + word dummy; /* Force word alignment of mark bytes. */ + } _mark_byte_union; +# define hb_marks _mark_byte_union._hb_marks +# else + word hb_marks[MARK_BITS_SZ]; /* Bit i in the array refers to the */ /* object starting at the ith word (header */ /* INCLUDED) in the heap block. */ /* The lsb of word 0 is numbered 0. */ + /* Unused bits are invalid, and are */ + /* occasionally set, e.g for uncollectable */ + /* objects. */ +# endif /* !USE_MARK_BYTES */ }; /* heap block body */ -# define DISCARD_WORDS 0 - /* Number of words to be dropped at the beginning of each block */ - /* Must be a multiple of WORDSZ. May reasonably be nonzero */ - /* on machines that don't guarantee longword alignment of */ - /* pointers, so that the number of false hits is minimized. */ - /* 0 and WORDSZ are probably the only reasonable values. */ - -# define BODY_SZ ((HBLKSIZE-WORDS_TO_BYTES(DISCARD_WORDS))/sizeof(word)) +# define BODY_SZ (HBLKSIZE/sizeof(word)) struct hblk { -# if (DISCARD_WORDS != 0) +# if 0 /* DISCARDWORDS no longer supported */ word garbage[DISCARD_WORDS]; # endif word hb_body[BODY_SZ]; }; -# define HDR_WORDS ((word)DISCARD_WORDS) -# define HDR_BYTES ((word)WORDS_TO_BYTES(DISCARD_WORDS)) - # define OBJ_SZ_TO_BLOCKS(sz) \ - divHBLKSZ(HDR_BYTES + WORDS_TO_BYTES(sz) + HBLKSIZE-1) + divHBLKSZ(WORDS_TO_BYTES(sz) + HBLKSIZE-1) /* Size of block (in units of HBLKSIZE) needed to hold objects of */ /* given sz (in words). */ /* Object free list link */ # define obj_link(p) (*(ptr_t *)(p)) -/* The type of mark procedures. This really belongs in gc_mark.h. */ -/* But we put it here, so that we can avoid scanning the mark proc */ -/* table. */ -typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, - mark_stack_limit, env */); # define LOG_MAX_MARK_PROCS 6 # define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) @@ -906,12 +811,12 @@ typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, # ifdef PCR # define MAX_ROOT_SETS 1024 # else -# ifdef MSWIN32 -# define MAX_ROOT_SETS 512 +# if defined(MSWIN32) || defined(MSWINCE) +# define MAX_ROOT_SETS 1024 /* Under NT, we add only written pages, which can result */ /* in many small root sets. */ # else -# define MAX_ROOT_SETS 64 +# define MAX_ROOT_SETS 256 # endif # endif # endif @@ -934,14 +839,14 @@ struct exclusion { struct roots { ptr_t r_start; ptr_t r_end; -# ifndef MSWIN32 +# if !defined(MSWIN32) && !defined(MSWINCE) struct roots * r_next; # endif GC_bool r_tmp; /* Delete before registering new dynamic libraries */ }; -#ifndef MSWIN32 +#if !defined(MSWIN32) && !defined(MSWINCE) /* Size of hash table index to roots. */ # define LOG_RT_SIZE 6 # define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ @@ -969,16 +874,28 @@ struct roots { struct _GC_arrays { word _heapsize; word _max_heapsize; + word _requested_heapsize; /* Heap size due to explicit expansion */ ptr_t _last_heap_addr; ptr_t _prev_heap_addr; word _large_free_bytes; /* Total bytes contained in blocks on large object free */ /* list. */ + word _large_allocd_bytes; + /* Total number of bytes in allocated large objects blocks. */ + /* For the purposes of this counter and the next one only, a */ + /* large object is one that occupies a block of at least */ + /* 2*HBLKSIZE. */ + word _max_large_allocd_bytes; + /* Maximum number of bytes that were ever allocated in */ + /* large object blocks. This is used to help decide when it */ + /* is safe to split up a large block. */ word _words_allocd_before_gc; /* Number of words allocated before this */ /* collection cycle. */ - word _words_allocd; +# ifndef SEPARATE_GLOBALS + word _words_allocd; /* Number of words allocated during this collection cycle */ +# endif word _words_wasted; /* Number of words wasted due to internal fragmentation */ /* in large objects, or due to dropping blacklisted */ @@ -993,14 +910,21 @@ struct _GC_arrays { word _mem_freed; /* Number of explicitly deallocated words of memory */ /* since last collection. */ - mark_proc _mark_procs[MAX_MARK_PROCS]; + ptr_t _scratch_end_ptr; + ptr_t _scratch_last_end_ptr; + /* Used by headers.c, and can easily appear to point to */ + /* heap. */ + GC_mark_proc _mark_procs[MAX_MARK_PROCS]; /* Table of user-defined mark procedures. There is */ /* a small number of these, which can be referenced */ /* by DS_PROC mark descriptors. See gc_mark.h. */ - ptr_t _objfreelist[MAXOBJSZ+1]; + +# ifndef SEPARATE_GLOBALS + ptr_t _objfreelist[MAXOBJSZ+1]; /* free list for objects */ - ptr_t _aobjfreelist[MAXOBJSZ+1]; + ptr_t _aobjfreelist[MAXOBJSZ+1]; /* free list for atomic objs */ +# endif ptr_t _uobjfreelist[MAXOBJSZ+1]; /* uncollectable but traced objs */ @@ -1033,41 +957,28 @@ struct _GC_arrays { ptr_t _sobjfreelist[MAXOBJSZ+1]; # endif /* free list for immutable objects */ - ptr_t _obj_map[MAXOBJSZ+1]; + map_entry_type * _obj_map[MAXOBJSZ+1]; /* If not NIL, then a pointer to a map of valid */ /* object addresses. _obj_map[sz][i] is j if the */ /* address block_start+i is a valid pointer */ - /* to an object at */ - /* block_start+i&~3 - WORDS_TO_BYTES(j). */ - /* (If ALL_INTERIOR_POINTERS is defined, then */ - /* instead ((short *)(hb_map[sz])[i] is j if */ - /* block_start+WORDS_TO_BYTES(i) is in the */ - /* interior of an object starting at */ - /* block_start+WORDS_TO_BYTES(i-j)). */ - /* It is OBJ_INVALID if */ - /* block_start+WORDS_TO_BYTES(i) is not */ - /* valid as a pointer to an object. */ - /* We assume all values of j <= OBJ_INVALID. */ - /* The zeroth entry corresponds to large objects.*/ -# ifdef ALL_INTERIOR_POINTERS -# define map_entry_type short -# define OBJ_INVALID 0x7fff -# define MAP_ENTRY(map, bytes) \ - (((map_entry_type *)(map))[BYTES_TO_WORDS(bytes)]) -# define MAP_ENTRIES BYTES_TO_WORDS(HBLKSIZE) -# define MAP_SIZE (MAP_ENTRIES * sizeof(map_entry_type)) -# define OFFSET_VALID(displ) TRUE -# define CPP_MAX_OFFSET (HBLKSIZE - HDR_BYTES - 1) -# define MAX_OFFSET ((word)CPP_MAX_OFFSET) -# else -# define map_entry_type char -# define OBJ_INVALID 0x7f -# define MAP_ENTRY(map, bytes) \ - (map)[bytes] -# define MAP_ENTRIES HBLKSIZE -# define MAP_SIZE MAP_ENTRIES -# define CPP_MAX_OFFSET (WORDS_TO_BYTES(OBJ_INVALID) - 1) -# define MAX_OFFSET ((word)CPP_MAX_OFFSET) + /* to an object at block_start + */ + /* WORDS_TO_BYTES(BYTES_TO_WORDS(i) - j) */ + /* I.e. j is a word displacement from the */ + /* object beginning. */ + /* The entry is OBJ_INVALID if the corresponding */ + /* address is not a valid pointer. It is */ + /* OFFSET_TOO_BIG if the value j would be too */ + /* large to fit in the entry. (Note that the */ + /* size of these entries matters, both for */ + /* space consumption and for cache utilization. */ +# define OFFSET_TOO_BIG 0xfe +# define OBJ_INVALID 0xff +# define MAP_ENTRY(map, bytes) (map)[bytes] +# define MAP_ENTRIES HBLKSIZE +# define MAP_SIZE MAP_ENTRIES +# define CPP_MAX_OFFSET (OFFSET_TOO_BIG - 1) +# define MAX_OFFSET ((word)CPP_MAX_OFFSET) + /* The following are used only if GC_all_interior_ptrs != 0 */ # define VALID_OFFSET_SZ \ (CPP_MAX_OFFSET > WORDS_TO_BYTES(CPP_MAXOBJSZ)? \ CPP_MAX_OFFSET+1 \ @@ -1075,11 +986,11 @@ struct _GC_arrays { char _valid_offsets[VALID_OFFSET_SZ]; /* GC_valid_offsets[i] == TRUE ==> i */ /* is registered as a displacement. */ -# define OFFSET_VALID(displ) GC_valid_offsets[displ] +# define OFFSET_VALID(displ) \ + (GC_all_interior_pointers || GC_valid_offsets[displ]) char _modws_valid_offsets[sizeof(word)]; /* GC_valid_offsets[i] ==> */ /* GC_modws_valid_offsets[i%sizeof(word)] */ -# endif # ifdef STUBBORN_ALLOC page_hash_table _changed_pages; /* Stubborn object pages that were changes since last call to */ @@ -1106,17 +1017,25 @@ struct _GC_arrays { # define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ # endif # else -# define MAX_HEAP_SECTS 256 +# ifdef SMALL_CONFIG +# define MAX_HEAP_SECTS 128 /* Roughly 1GB */ +# else +# define MAX_HEAP_SECTS 384 /* Roughly 3GB */ +# endif # endif struct HeapSect { ptr_t hs_start; word hs_bytes; } _heap_sects[MAX_HEAP_SECTS]; -# ifdef MSWIN32 +# if defined(MSWIN32) || defined(MSWINCE) ptr_t _heap_bases[MAX_HEAP_SECTS]; /* Start address of memory regions obtained from kernel. */ # endif +# ifdef MSWINCE + word _heap_lengths[MAX_HEAP_SECTS]; + /* Commited lengths of memory regions obtained from kernel. */ +# endif struct roots _static_roots[MAX_ROOT_SETS]; -# ifndef MSWIN32 +# if !defined(MSWIN32) && !defined(MSWINCE) struct roots * _root_index[RT_SIZE]; # endif struct exclusion _excl_table[MAX_EXCLUSIONS]; @@ -1135,8 +1054,11 @@ struct _GC_arrays { GC_API GC_FAR struct _GC_arrays GC_arrays; -# define GC_objfreelist GC_arrays._objfreelist -# define GC_aobjfreelist GC_arrays._aobjfreelist +# ifndef SEPARATE_GLOBALS +# define GC_objfreelist GC_arrays._objfreelist +# define GC_aobjfreelist GC_arrays._aobjfreelist +# define GC_words_allocd GC_arrays._words_allocd +# endif # define GC_uobjfreelist GC_arrays._uobjfreelist # ifdef ATOMIC_UNCOLLECTABLE # define GC_auobjfreelist GC_arrays._auobjfreelist @@ -1151,24 +1073,31 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # define GC_obj_map GC_arrays._obj_map # define GC_last_heap_addr GC_arrays._last_heap_addr # define GC_prev_heap_addr GC_arrays._prev_heap_addr -# define GC_words_allocd GC_arrays._words_allocd # define GC_words_wasted GC_arrays._words_wasted # define GC_large_free_bytes GC_arrays._large_free_bytes +# define GC_large_allocd_bytes GC_arrays._large_allocd_bytes +# define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes # define GC_words_finalized GC_arrays._words_finalized # define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc # define GC_mem_freed GC_arrays._mem_freed +# define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +# define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr # define GC_mark_procs GC_arrays._mark_procs # define GC_heapsize GC_arrays._heapsize # define GC_max_heapsize GC_arrays._max_heapsize +# define GC_requested_heapsize GC_arrays._requested_heapsize # define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc # define GC_heap_sects GC_arrays._heap_sects # define GC_last_stack GC_arrays._last_stack # ifdef USE_MUNMAP # define GC_unmapped_bytes GC_arrays._unmapped_bytes # endif -# ifdef MSWIN32 +# if defined(MSWIN32) || defined(MSWINCE) # define GC_heap_bases GC_arrays._heap_bases # endif +# ifdef MSWINCE +# define GC_heap_lengths GC_arrays._heap_lengths +# endif # define GC_static_roots GC_arrays._static_roots # define GC_root_index GC_arrays._root_index # define GC_excl_table GC_arrays._excl_table @@ -1194,6 +1123,8 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # define beginGC_arrays ((ptr_t)(&GC_arrays)) # define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) +#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes) + /* Object kinds: */ # define MAXOBJKINDS 16 @@ -1213,10 +1144,27 @@ extern struct obj_kind { GC_bool ok_init; /* Clear objects before putting them on the free list. */ } GC_obj_kinds[MAXOBJKINDS]; -# define endGC_obj_kinds (((ptr_t)(&GC_obj_kinds)) + (sizeof GC_obj_kinds)) +# define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds)) +# define endGC_obj_kinds (beginGC_obj_kinds + (sizeof GC_obj_kinds)) -# define end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ - endGC_obj_kinds : endGC_arrays) +/* Variables that used to be in GC_arrays, but need to be accessed by */ +/* inline allocation code. If they were in GC_arrays, the inlined */ +/* allocation code would include GC_arrays offsets (as it did), which */ +/* introduce maintenance problems. */ + +#ifdef SEPARATE_GLOBALS + word GC_words_allocd; + /* Number of words allocated during this collection cycle */ + ptr_t GC_objfreelist[MAXOBJSZ+1]; + /* free list for NORMAL objects */ +# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist)) +# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist)) + + ptr_t GC_aobjfreelist[MAXOBJSZ+1]; + /* free list for atomic (PTRFREE) objs */ +# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist)) +# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist)) +#endif /* Predefined kinds: */ # define PTRFREE 0 @@ -1240,8 +1188,10 @@ extern word GC_n_heap_sects; /* Number of separately added heap */ extern word GC_page_size; -# ifdef MSWIN32 -extern word GC_n_heap_bases; /* See GC_heap_bases. */ +# if defined(MSWIN32) || defined(MSWINCE) + struct _SYSTEM_INFO; + extern struct _SYSTEM_INFO GC_sysinfo; + extern word GC_n_heap_bases; /* See GC_heap_bases. */ # endif extern word GC_total_stack_black_listed; @@ -1254,7 +1204,7 @@ extern word GC_black_list_spacing; /* "stack-blacklisted", i.e. that are */ /* problematic in the interior of an object. */ -extern char * GC_invalid_map; +extern map_entry_type * GC_invalid_map; /* Pointer to the nowhere valid hblk map */ /* Blocks pointing to this map are free. */ @@ -1273,7 +1223,7 @@ extern GC_bool GC_objects_are_marked; /* There are marked objects in */ extern GC_bool GC_incremental; /* Using incremental/generational collection. */ #else -# define GC_incremental TRUE +# define GC_incremental FALSE /* Hopefully allow optimizer to remove some code. */ #endif @@ -1286,10 +1236,6 @@ extern word GC_root_size; /* Total size of registered root sections */ extern GC_bool GC_debugging_started; /* GC_debug_malloc has been called. */ -extern ptr_t GC_least_plausible_heap_addr; -extern ptr_t GC_greatest_plausible_heap_addr; - /* Bounds on the heap. Guaranteed valid */ - /* Likely to include future heap expansion. */ /* Operations */ # ifndef abs @@ -1302,6 +1248,33 @@ extern ptr_t GC_greatest_plausible_heap_addr; /* with it. Only those corresponding to the beginning of an */ /* object are used. */ +/* Set mark bit correctly, even if mark bits may be concurrently */ +/* accessed. */ +#ifdef PARALLEL_MARK +# define OR_WORD(addr, bits) \ + { word old; \ + do { \ + old = *((volatile word *)addr); \ + } while (!GC_compare_and_exchange((addr), old, old | (bits))); \ + } +# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \ + { word old; \ + word my_bits = (bits); \ + do { \ + old = *((volatile word *)addr); \ + if (old & my_bits) goto exit_label; \ + } while (!GC_compare_and_exchange((addr), old, old | my_bits)); \ + } +#else +# define OR_WORD(addr, bits) *(addr) |= (bits) +# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \ + { \ + word old = *(addr); \ + word my_bits = (bits); \ + if (old & my_bits) goto exit_label; \ + *(addr) = (old | my_bits); \ + } +#endif /* Mark bit operations */ @@ -1313,75 +1286,87 @@ extern ptr_t GC_greatest_plausible_heap_addr; * relative to the beginning of the block, including unused words) */ +#ifdef USE_MARK_BYTES +# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n) >> 1]) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n)>>1]) = 1 +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n)>>1]) = 0 +#else /* !USE_MARK_BYTES */ # define mark_bit_from_hdr(hhdr,n) (((hhdr)->hb_marks[divWORDSZ(n)] \ >> (modWORDSZ(n))) & (word)1) -# define set_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ - |= (word)1 << modWORDSZ(n) - +# define set_mark_bit_from_hdr(hhdr,n) \ + OR_WORD((hhdr)->hb_marks+divWORDSZ(n), \ + (word)1 << modWORDSZ(n)) # define clear_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ &= ~((word)1 << modWORDSZ(n)) +#endif /* !USE_MARK_BYTES */ /* Important internal collector routines */ -ptr_t GC_approx_sp(); - -GC_bool GC_should_collect(); -#ifdef PRESERVE_LAST - GC_bool GC_in_last_heap_sect(/* ptr_t */); - /* In last added heap section? If so, avoid breaking up. */ -#endif -void GC_apply_to_all_blocks(/*fn, client_data*/); - /* Invoke fn(hbp, client_data) for each */ - /* allocated heap block. */ -struct hblk * GC_next_used_block(/* struct hblk * h */); - /* Return first in-use block >= h */ -struct hblk * GC_prev_block(/* struct hblk * h */); - /* Return last block <= h. Returned block */ - /* is managed by GC, but may or may not be in */ +ptr_t GC_approx_sp GC_PROTO((void)); + +GC_bool GC_should_collect GC_PROTO((void)); + +void GC_apply_to_all_blocks GC_PROTO(( \ + void (*fn) GC_PROTO((struct hblk *h, word client_data)), \ + word client_data)); + /* Invoke fn(hbp, client_data) for each */ + /* allocated heap block. */ +struct hblk * GC_next_used_block GC_PROTO((struct hblk * h)); + /* Return first in-use block >= h */ +struct hblk * GC_prev_block GC_PROTO((struct hblk * h)); + /* Return last block <= h. Returned block */ + /* is managed by GC, but may or may not be in */ /* use. */ -void GC_mark_init(); -void GC_clear_marks(); /* Clear mark bits for all heap objects. */ -void GC_invalidate_mark_state(); /* Tell the marker that marked */ - /* objects may point to unmarked */ - /* ones, and roots may point to */ - /* unmarked objects. */ - /* Reset mark stack. */ -void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ - /* Return after about one pages worth of */ - /* work. */ -GC_bool GC_mark_stack_empty(); -GC_bool GC_mark_some(/* cold_gc_frame */); - /* Perform about one pages worth of marking */ - /* work of whatever kind is needed. Returns */ - /* quickly if no collection is in progress. */ - /* Return TRUE if mark phase finished. */ -void GC_initiate_gc(); /* initiate collection. */ - /* If the mark state is invalid, this */ - /* becomes full colleection. Otherwise */ - /* it's partial. */ -void GC_push_all(/*b,t*/); /* Push everything in a range */ - /* onto mark stack. */ -void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ - /* subintervals of [b,t) onto */ - /* mark stack. */ +void GC_mark_init GC_PROTO((void)); +void GC_clear_marks GC_PROTO((void)); /* Clear mark bits for all heap objects. */ +void GC_invalidate_mark_state GC_PROTO((void)); + /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. */ + /* Reset mark stack. */ +GC_bool GC_mark_stack_empty GC_PROTO((void)); +GC_bool GC_mark_some GC_PROTO((ptr_t cold_gc_frame)); + /* Perform about one pages worth of marking */ + /* work of whatever kind is needed. Returns */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +void GC_initiate_gc GC_PROTO((void)); + /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full colleection. Otherwise */ + /* it's partial. */ +void GC_push_all GC_PROTO((ptr_t bottom, ptr_t top)); + /* Push everything in a range */ + /* onto mark stack. */ +void GC_push_selected GC_PROTO(( \ + ptr_t bottom, \ + ptr_t top, \ + int (*dirty_fn) GC_PROTO((struct hblk *h)), \ + void (*push_fn) GC_PROTO((ptr_t bottom, ptr_t top)) )); + /* Push all pages h in [b,t) s.t. */ + /* select_fn(h) != 0 onto mark stack. */ #ifndef SMALL_CONFIG - void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); + void GC_push_conditional GC_PROTO((ptr_t b, ptr_t t, GC_bool all)); #else # define GC_push_conditional(b, t, all) GC_push_all(b, t) #endif /* Do either of the above, depending */ /* on the third arg. */ -void GC_push_all_stack(/*b,t*/); /* As above, but consider */ +void GC_push_all_stack GC_PROTO((ptr_t b, ptr_t t)); + /* As above, but consider */ /* interior pointers as valid */ -void GC_push_all_eager(/*b,t*/); /* Same as GC_push_all_stack, but */ +void GC_push_all_eager GC_PROTO((ptr_t b, ptr_t t)); + /* Same as GC_push_all_stack, but */ /* ensures that stack is scanned */ /* immediately, not just scheduled */ /* for scanning. */ #ifndef THREADS - void GC_push_all_stack_partially_eager(/* bottom, top, cold_gc_frame */); + void GC_push_all_stack_partially_eager GC_PROTO(( \ + ptr_t bottom, ptr_t top, ptr_t cold_gc_frame )); /* Similar to GC_push_all_eager, but only the */ /* part hotter than cold_gc_frame is scanned */ - /* immediately. Needed to endure that callee- */ + /* immediately. Needed to ensure that callee- */ /* save registers are not missed. */ #else /* In the threads case, we push part of the current thread stack */ @@ -1390,163 +1375,240 @@ void GC_push_all_eager(/*b,t*/); /* Same as GC_push_all_stack, but */ /* stacks are scheduled for scanning in *GC_push_other_roots, which */ /* is thread-package-specific. */ #endif -void GC_push_current_stack(/* ptr_t cold_gc_frame */); - /* Push enough of the current stack eagerly to */ - /* ensure that callee-save registers saved in */ - /* GC frames are scanned. */ - /* In the non-threads case, schedule entire */ - /* stack for scanning. */ -void GC_push_roots(/* GC_bool all, ptr_t cold_gc_frame */); - /* Push all or dirty roots. */ -extern void (*GC_push_other_roots)(); - /* Push system or application specific roots */ - /* onto the mark stack. In some environments */ - /* (e.g. threads environments) this is */ - /* predfined to be non-zero. A client supplied */ - /* replacement should also call the original */ - /* function. */ -extern void (*GC_start_call_back)(/* void */); - /* Called at start of full collections. */ - /* Not called if 0. Called with allocation */ - /* lock held. */ - /* 0 by default. */ -void GC_push_regs(); /* Push register contents onto mark stack. */ -void GC_remark(); /* Mark from all marked objects. Used */ - /* only if we had to drop something. */ -# if defined(MSWIN32) - void __cdecl GC_push_one(); +void GC_push_current_stack GC_PROTO((ptr_t cold_gc_frame)); + /* Push enough of the current stack eagerly to */ + /* ensure that callee-save registers saved in */ + /* GC frames are scanned. */ + /* In the non-threads case, schedule entire */ + /* stack for scanning. */ +void GC_push_roots GC_PROTO((GC_bool all, ptr_t cold_gc_frame)); + /* Push all or dirty roots. */ +extern void (*GC_push_other_roots) GC_PROTO((void)); + /* Push system or application specific roots */ + /* onto the mark stack. In some environments */ + /* (e.g. threads environments) this is */ + /* predfined to be non-zero. A client supplied */ + /* replacement should also call the original */ + /* function. */ +extern void GC_push_gc_structures GC_PROTO((void)); + /* Push GC internal roots. These are normally */ + /* included in the static data segment, and */ + /* Thus implicitly pushed. But we must do this */ + /* explicitly if normal root processing is */ + /* disabled. Calls the following: */ + extern void GC_push_finalizer_structures GC_PROTO((void)); + extern void GC_push_stubborn_structures GC_PROTO((void)); +# ifdef THREADS + extern void GC_push_thread_structures GC_PROTO((void)); +# endif +extern void (*GC_start_call_back) GC_PROTO((void)); + /* Called at start of full collections. */ + /* Not called if 0. Called with allocation */ + /* lock held. */ + /* 0 by default. */ +# if defined(USE_GENERIC_PUSH_REGS) + void GC_generic_push_regs GC_PROTO((ptr_t cold_gc_frame)); +# else + void GC_push_regs GC_PROTO((void)); +# endif + /* Push register contents onto mark stack. */ + /* If NURSERY is defined, the default push */ + /* action can be overridden with GC_push_proc */ + +# ifdef NURSERY + extern void (*GC_push_proc)(ptr_t); +# endif +# if defined(MSWIN32) || defined(MSWINCE) + void __cdecl GC_push_one GC_PROTO((word p)); # else - void GC_push_one(/*p*/); /* If p points to an object, mark it */ + void GC_push_one GC_PROTO((word p)); + /* If p points to an object, mark it */ /* and push contents on the mark stack */ + /* Pointer recognition test always */ + /* accepts interior pointers, i.e. this */ + /* is appropriate for pointers found on */ + /* stack. */ # endif -void GC_push_one_checked(/*p*/); /* Ditto, omits plausibility test */ -void GC_push_marked(/* struct hblk h, hdr * hhdr */); +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + void GC_mark_and_push_stack GC_PROTO((word p, ptr_t source)); + /* Ditto, omits plausibility test */ +# else + void GC_mark_and_push_stack GC_PROTO((word p)); +# endif +void GC_push_marked GC_PROTO((struct hblk * h, hdr * hhdr)); /* Push contents of all marked objects in h onto */ /* mark stack. */ #ifdef SMALL_CONFIG # define GC_push_next_marked_dirty(h) GC_push_next_marked(h) #else - struct hblk * GC_push_next_marked_dirty(/* h */); + struct hblk * GC_push_next_marked_dirty GC_PROTO((struct hblk * h)); /* Invoke GC_push_marked on next dirty block above h. */ /* Return a pointer just past the end of this block. */ #endif /* !SMALL_CONFIG */ -struct hblk * GC_push_next_marked(/* h */); - /* Ditto, but also mark from clean pages. */ -struct hblk * GC_push_next_marked_uncollectable(/* h */); - /* Ditto, but mark only from uncollectable pages. */ -GC_bool GC_stopped_mark(); /* Stop world and mark from all roots */ - /* and rescuers. */ -void GC_clear_hdr_marks(/* hhdr */); /* Clear the mark bits in a header */ -void GC_set_hdr_marks(/* hhdr */); /* Set the mark bits in a header */ -void GC_add_roots_inner(); -GC_bool GC_is_static_root(/* ptr_t p */); - /* Is the address p in one of the registered static */ +struct hblk * GC_push_next_marked GC_PROTO((struct hblk * h)); + /* Ditto, but also mark from clean pages. */ +struct hblk * GC_push_next_marked_uncollectable GC_PROTO((struct hblk * h)); + /* Ditto, but mark only from uncollectable pages. */ +GC_bool GC_stopped_mark GC_PROTO((GC_stop_func stop_func)); + /* Stop world and mark from all roots */ + /* and rescuers. */ +void GC_clear_hdr_marks GC_PROTO((hdr * hhdr)); + /* Clear the mark bits in a header */ +void GC_set_hdr_marks GC_PROTO((hdr * hhdr)); + /* Set the mark bits in a header */ +void GC_set_fl_marks GC_PROTO((ptr_t p)); + /* Set all mark bits associated with */ + /* a free list. */ +void GC_add_roots_inner GC_PROTO((char * b, char * e, GC_bool tmp)); +GC_bool GC_is_static_root GC_PROTO((ptr_t p)); + /* Is the address p in one of the registered static */ + /* root sections? */ +# if defined(MSWIN32) || defined(_WIN32_WCE_EMULATION) +GC_bool GC_is_tmp_root GC_PROTO((ptr_t p)); + /* Is the address p in one of the temporary static */ /* root sections? */ -void GC_register_dynamic_libraries(); - /* Add dynamic library data sections to the root set. */ - +# endif +void GC_register_dynamic_libraries GC_PROTO((void)); + /* Add dynamic library data sections to the root set. */ + /* Machine dependent startup routines */ -ptr_t GC_get_stack_base(); -void GC_register_data_segments(); - +ptr_t GC_get_stack_base GC_PROTO((void)); /* Cold end of stack */ +#ifdef IA64 + ptr_t GC_get_register_stack_base GC_PROTO((void)); + /* Cold end of register stack. */ +#endif +void GC_register_data_segments GC_PROTO((void)); + /* Black listing: */ -void GC_bl_init(); -# ifndef ALL_INTERIOR_POINTERS - void GC_add_to_black_list_normal(/* bits, maybe source */); +void GC_bl_init GC_PROTO((void)); +# ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_normal GC_PROTO((word p, ptr_t source)); /* Register bits as a possible future false */ /* reference from the heap or static data */ -# ifdef PRINT_BLACK_LIST # define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ - GC_add_to_black_list_normal(bits, source) -# else -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ - GC_add_to_black_list_normal(bits) -# endif + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack(bits, (ptr_t)(source)); \ + } else { \ + GC_add_to_black_list_normal(bits, (ptr_t)(source)); \ + } # else -# ifdef PRINT_BLACK_LIST -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ - GC_add_to_black_list_stack(bits, source) -# else + void GC_add_to_black_list_normal GC_PROTO((word p)); # define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ - GC_add_to_black_list_stack(bits) -# endif + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack(bits); \ + } else { \ + GC_add_to_black_list_normal(bits); \ + } # endif -void GC_add_to_black_list_stack(/* bits, maybe source */); -struct hblk * GC_is_black_listed(/* h, len */); - /* If there are likely to be false references */ - /* to a block starting at h of the indicated */ - /* length, then return the next plausible */ - /* starting location for h that might avoid */ - /* these false references. */ -void GC_promote_black_lists(); - /* Declare an end to a black listing phase. */ -void GC_unpromote_black_lists(); - /* Approximately undo the effect of the above. */ - /* This actually loses some information, but */ - /* only in a reasonably safe way. */ -word GC_number_stack_black_listed(/*struct hblk *start, struct hblk *endp1 */); - /* Return the number of (stack) blacklisted */ - /* blocks in the range for statistical */ - /* purposes. */ - -ptr_t GC_scratch_alloc(/*bytes*/); - /* GC internal memory allocation for */ - /* small objects. Deallocation is not */ - /* possible. */ - +# ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_stack GC_PROTO((word p, ptr_t source)); +# else + void GC_add_to_black_list_stack GC_PROTO((word p)); +# endif +struct hblk * GC_is_black_listed GC_PROTO((struct hblk * h, word len)); + /* If there are likely to be false references */ + /* to a block starting at h of the indicated */ + /* length, then return the next plausible */ + /* starting location for h that might avoid */ + /* these false references. */ +void GC_promote_black_lists GC_PROTO((void)); + /* Declare an end to a black listing phase. */ +void GC_unpromote_black_lists GC_PROTO((void)); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ +word GC_number_stack_black_listed GC_PROTO(( \ + struct hblk *start, struct hblk *endp1)); + /* Return the number of (stack) blacklisted */ + /* blocks in the range for statistical */ + /* purposes. */ + +ptr_t GC_scratch_alloc GC_PROTO((word bytes)); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. */ + /* Heap block layout maps: */ -void GC_invalidate_map(/* hdr */); - /* Remove the object map associated */ - /* with the block. This identifies */ - /* the block as invalid to the mark */ - /* routines. */ -GC_bool GC_add_map_entry(/*sz*/); - /* Add a heap block map for objects of */ - /* size sz to obj_map. */ - /* Return FALSE on failure. */ -void GC_register_displacement_inner(/*offset*/); - /* Version of GC_register_displacement */ - /* that assumes lock is already held */ - /* and signals are already disabled. */ - +void GC_invalidate_map GC_PROTO((hdr * hhdr)); + /* Remove the object map associated */ + /* with the block. This identifies */ + /* the block as invalid to the mark */ + /* routines. */ +GC_bool GC_add_map_entry GC_PROTO((word sz)); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +void GC_register_displacement_inner GC_PROTO((word offset)); + /* Version of GC_register_displacement */ + /* that assumes lock is already held */ + /* and signals are already disabled. */ + /* hblk allocation: */ -void GC_new_hblk(/*size_in_words, kind*/); - /* Allocate a new heap block, and build */ - /* a free list in it. */ -struct hblk * GC_allochblk(/*size_in_words, kind*/); - /* Allocate a heap block, clear it if */ - /* for composite objects, inform */ +void GC_new_hblk GC_PROTO((word size_in_words, int kind)); + /* Allocate a new heap block, and build */ + /* a free list in it. */ + +ptr_t GC_build_fl GC_PROTO((struct hblk *h, word sz, + GC_bool clear, ptr_t list)); + /* Build a free list for objects of */ + /* size sz in block h. Append list to */ + /* end of the free lists. Possibly */ + /* clear objects on the list. Normally */ + /* called by GC_new_hblk, but also */ + /* called explicitly without GC lock. */ + +struct hblk * GC_allochblk GC_PROTO(( \ + word size_in_words, int kind, unsigned flags)); + /* Allocate a heap block, inform */ /* the marker that block is valid */ /* for objects of indicated size. */ - /* sz < 0 ==> atomic. */ -void GC_freehblk(); /* Deallocate a heap block and mark it */ - /* as invalid. */ - + +ptr_t GC_alloc_large GC_PROTO((word lw, int k, unsigned flags)); + /* Allocate a large block of size lw words. */ + /* The block is not cleared. */ + /* Flags is 0 or IGNORE_OFF_PAGE. */ + /* Calls GC_allchblk to do the actual */ + /* allocation, but also triggers GC and/or */ + /* heap expansion as appropriate. */ + /* Does not update GC_words_allocd, but does */ + /* other accounting. */ + +ptr_t GC_alloc_large_and_clear GC_PROTO((word lw, int k, unsigned flags)); + /* As above, but clear block if appropriate */ + /* for kind k. */ + +void GC_freehblk GC_PROTO((struct hblk * p)); + /* Deallocate a heap block and mark it */ + /* as invalid. */ + /* Misc GC: */ -void GC_init_inner(); -GC_bool GC_expand_hp_inner(); -void GC_start_reclaim(/*abort_if_found*/); - /* Restore unmarked objects to free */ - /* lists, or (if abort_if_found is */ - /* TRUE) report them. */ - /* Sweeping of small object pages is */ - /* largely deferred. */ -void GC_continue_reclaim(/*size, kind*/); - /* Sweep pages of the given size and */ - /* kind, as long as possible, and */ - /* as long as the corr. free list is */ - /* empty. */ -void GC_reclaim_or_delete_all(); - /* Arrange for all reclaim lists to be */ - /* empty. Judiciously choose between */ - /* sweeping and discarding each page. */ -GC_bool GC_reclaim_all(/* GC_stop_func f*/); - /* Reclaim all blocks. Abort (in a */ - /* consistent state) if f returns TRUE. */ -GC_bool GC_block_empty(/* hhdr */); /* Block completely unmarked? */ -GC_bool GC_never_stop_func(); /* Returns FALSE. */ -GC_bool GC_try_to_collect_inner(/* GC_stop_func f */); +void GC_init_inner GC_PROTO((void)); +GC_bool GC_expand_hp_inner GC_PROTO((word n)); +void GC_start_reclaim GC_PROTO((int abort_if_found)); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +void GC_continue_reclaim GC_PROTO((word sz, int kind)); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corr. free list is */ + /* empty. */ +void GC_reclaim_or_delete_all GC_PROTO((void)); + /* Arrange for all reclaim lists to be */ + /* empty. Judiciously choose between */ + /* sweeping and discarding each page. */ +GC_bool GC_reclaim_all GC_PROTO((GC_stop_func stop_func, GC_bool ignore_old)); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if f returns TRUE. */ +GC_bool GC_block_empty GC_PROTO((hdr * hhdr)); + /* Block completely unmarked? */ +GC_bool GC_never_stop_func GC_PROTO((void)); + /* Returns FALSE. */ +GC_bool GC_try_to_collect_inner GC_PROTO((GC_stop_func f)); + /* Collect; caller must have acquired */ /* lock and disabled signals. */ /* Collection is aborted if f returns */ @@ -1554,87 +1616,133 @@ GC_bool GC_try_to_collect_inner(/* GC_stop_func f */); /* successfully. */ # define GC_gcollect_inner() \ (void) GC_try_to_collect_inner(GC_never_stop_func) -void GC_finish_collection(); /* Finish collection. Mark bits are */ - /* consistent and lock is still held. */ -GC_bool GC_collect_or_expand(/* needed_blocks */); - /* Collect or expand heap in an attempt */ - /* make the indicated number of free */ - /* blocks available. Should be called */ - /* until the blocks are available or */ - /* until it fails by returning FALSE. */ -GC_API void GC_init(); /* Initialize collector. */ -void GC_collect_a_little_inner(/* int n */); - /* Do n units worth of garbage */ - /* collection work, if appropriate. */ - /* A unit is an amount appropriate for */ - /* HBLKSIZE bytes of allocation. */ -ptr_t GC_generic_malloc(/* bytes, kind */); - /* Allocate an object of the given */ - /* kind. By default, there are only */ - /* a few kinds: composite(pointerfree), */ +void GC_finish_collection GC_PROTO((void)); + /* Finish collection. Mark bits are */ + /* consistent and lock is still held. */ +GC_bool GC_collect_or_expand GC_PROTO(( \ + word needed_blocks, GC_bool ignore_off_page)); + /* Collect or expand heap in an attempt */ + /* make the indicated number of free */ + /* blocks available. Should be called */ + /* until the blocks are available or */ + /* until it fails by returning FALSE. */ +GC_API void GC_init GC_PROTO((void)); /* Initialize collector. */ + +#if defined(MSWIN32) || defined(MSWINCE) + void GC_deinit GC_PROTO((void)); + /* Free any resources allocated by */ + /* GC_init */ +#endif + +void GC_collect_a_little_inner GC_PROTO((int n)); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ +ptr_t GC_generic_malloc GC_PROTO((word lb, int k)); + /* Allocate an object of the given */ + /* kind. By default, there are only */ + /* a few kinds: composite(pointerfree), */ /* atomic, uncollectable, etc. */ /* We claim it's possible for clever */ /* client code that understands GC */ /* internals to add more, e.g. to */ /* communicate object layout info */ /* to the collector. */ -ptr_t GC_generic_malloc_ignore_off_page(/* bytes, kind */); - /* As above, but pointers past the */ - /* first page of the resulting object */ - /* are ignored. */ -ptr_t GC_generic_malloc_inner(/* bytes, kind */); - /* Ditto, but I already hold lock, etc. */ -ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind)); - /* As above, but size in units of words */ - /* Bypasses MERGE_SIZES. Assumes */ - /* words <= MAXOBJSZ. */ -ptr_t GC_generic_malloc_inner_ignore_off_page(/* bytes, kind */); - /* Allocate an object, where */ - /* the client guarantees that there */ - /* will always be a pointer to the */ - /* beginning of the object while the */ - /* object is live. */ -ptr_t GC_allocobj(/* sz_inn_words, kind */); - /* Make the indicated */ - /* free list nonempty, and return its */ - /* head. */ - -void GC_init_headers(); -GC_bool GC_install_header(/*h*/); - /* Install a header for block h. */ - /* Return FALSE on failure. */ -GC_bool GC_install_counts(/*h, sz*/); - /* Set up forwarding counts for block */ - /* h of size sz. */ - /* Return FALSE on failure. */ -void GC_remove_header(/*h*/); - /* Remove the header for block h. */ -void GC_remove_counts(/*h, sz*/); - /* Remove forwarding counts for h. */ -hdr * GC_find_header(/*p*/); /* Debugging only. */ - -void GC_finalize(); /* Perform all indicated finalization actions */ - /* on unmarked objects. */ - /* Unreachable finalizable objects are enqueued */ - /* for processing by GC_invoke_finalizers. */ - /* Invoked with lock. */ - -void GC_add_to_heap(/*p, bytes*/); - /* Add a HBLKSIZE aligned chunk to the heap. */ - -void GC_print_obj(/* ptr_t p */); - /* P points to somewhere inside an object with */ - /* debugging info. Print a human readable */ - /* description of the object to stderr. */ -extern void (*GC_check_heap)(); - /* Check that all objects in the heap with */ - /* debugging info are intact. Print */ - /* descriptions of any that are not. */ -extern void (*GC_print_heap_obj)(/* ptr_t p */); - /* If possible print s followed by a more */ - /* detailed description of the object */ - /* referred to by p. */ - +ptr_t GC_generic_malloc_ignore_off_page GC_PROTO((size_t b, int k)); + /* As above, but pointers past the */ + /* first page of the resulting object */ + /* are ignored. */ +ptr_t GC_generic_malloc_inner GC_PROTO((word lb, int k)); + /* Ditto, but I already hold lock, etc. */ +ptr_t GC_generic_malloc_words_small GC_PROTO((size_t lw, int k)); + /* As above, but size in units of words */ + /* Bypasses MERGE_SIZES. Assumes */ + /* words <= MAXOBJSZ. */ +ptr_t GC_generic_malloc_inner_ignore_off_page GC_PROTO((size_t lb, int k)); + /* Allocate an object, where */ + /* the client guarantees that there */ + /* will always be a pointer to the */ + /* beginning of the object while the */ + /* object is live. */ +ptr_t GC_allocobj GC_PROTO((word sz, int kind)); + /* Make the indicated */ + /* free list nonempty, and return its */ + /* head. */ + +void GC_init_headers GC_PROTO((void)); +struct hblkhdr * GC_install_header GC_PROTO((struct hblk *h)); + /* Install a header for block h. */ + /* Return 0 on failure, or the header */ + /* otherwise. */ +GC_bool GC_install_counts GC_PROTO((struct hblk * h, word sz)); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +void GC_remove_header GC_PROTO((struct hblk * h)); + /* Remove the header for block h. */ +void GC_remove_counts GC_PROTO((struct hblk * h, word sz)); + /* Remove forwarding counts for h. */ +hdr * GC_find_header GC_PROTO((ptr_t h)); /* Debugging only. */ + +void GC_finalize GC_PROTO((void)); + /* Perform all indicated finalization actions */ + /* on unmarked objects. */ + /* Unreachable finalizable objects are enqueued */ + /* for processing by GC_invoke_finalizers. */ + /* Invoked with lock. */ + +void GC_notify_or_invoke_finalizers GC_PROTO((void)); + /* If GC_finalize_on_demand is not set, invoke */ + /* eligible finalizers. Otherwise: */ + /* Call *GC_finalizer_notifier if there are */ + /* finalizers to be run, and we haven't called */ + /* this procedure yet this GC cycle. */ + +void GC_add_to_heap GC_PROTO((struct hblk *p, word bytes)); + /* Add a HBLKSIZE aligned chunk to the heap. */ + +void GC_print_obj GC_PROTO((ptr_t p)); + /* P points to somewhere inside an object with */ + /* debugging info. Print a human readable */ + /* description of the object to stderr. */ +extern void (*GC_check_heap) GC_PROTO((void)); + /* Check that all objects in the heap with */ + /* debugging info are intact. Print */ + /* descriptions of any that are not. */ +extern void (*GC_print_heap_obj) GC_PROTO((ptr_t p)); + /* If possible print s followed by a more */ + /* detailed description of the object */ + /* referred to by p. */ + +extern GC_bool GC_print_stats; /* Produce at least some logging output */ + /* Set from environment variable. */ + +/* Macros used for collector internal allocation. */ +/* These assume the collector lock is held. */ +#ifdef DBG_HDRS_ALL + extern GC_PTR GC_debug_generic_malloc_inner(size_t lb, int k); + extern GC_PTR GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, + int k); +# define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_debug_generic_malloc_inner_ignore_off_page +# ifdef THREADS +# define GC_INTERNAL_FREE GC_debug_free_inner +# else +# define GC_INTERNAL_FREE GC_debug_free +# endif +#else +# define GC_INTERNAL_MALLOC GC_generic_malloc_inner +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ + GC_generic_malloc_inner_ignore_off_page +# ifdef THREADS +# define GC_INTERNAL_FREE GC_free_inner +# else +# define GC_INTERNAL_FREE GC_free +# endif +#endif + /* Memory unmapping: */ #ifdef USE_MUNMAP void GC_unmap_old(void); @@ -1646,35 +1754,38 @@ extern void (*GC_print_heap_obj)(/* ptr_t p */); /* Virtual dirty bit implementation: */ /* Each implementation exports the following: */ -void GC_read_dirty(); /* Retrieve dirty bits. */ -GC_bool GC_page_was_dirty(/* struct hblk * h */); - /* Read retrieved dirty bits. */ -GC_bool GC_page_was_ever_dirty(/* struct hblk * h */); - /* Could the page contain valid heap pointers? */ -void GC_is_fresh(/* struct hblk * h, word number_of_blocks */); - /* Assert the region currently contains no */ - /* valid pointers. */ -void GC_write_hint(/* struct hblk * h */); - /* h is about to be written. */ -void GC_dirty_init(); - +void GC_read_dirty GC_PROTO((void)); + /* Retrieve dirty bits. */ +GC_bool GC_page_was_dirty GC_PROTO((struct hblk *h)); + /* Read retrieved dirty bits. */ +GC_bool GC_page_was_ever_dirty GC_PROTO((struct hblk *h)); + /* Could the page contain valid heap pointers? */ +void GC_is_fresh GC_PROTO((struct hblk *h, word n)); + /* Assert the region currently contains no */ + /* valid pointers. */ +void GC_write_hint GC_PROTO((struct hblk *h)); + /* h is about to be written. */ +void GC_dirty_init GC_PROTO((void)); + /* Slow/general mark bit manipulation: */ -GC_API GC_bool GC_is_marked(); -void GC_clear_mark_bit(); -void GC_set_mark_bit(); - +GC_API GC_bool GC_is_marked GC_PROTO((ptr_t p)); +void GC_clear_mark_bit GC_PROTO((ptr_t p)); +void GC_set_mark_bit GC_PROTO((ptr_t p)); + /* Stubborn objects: */ -void GC_read_changed(); /* Analogous to GC_read_dirty */ -GC_bool GC_page_was_changed(/* h */); /* Analogous to GC_page_was_dirty */ -void GC_clean_changing_list(); /* Collect obsolete changing list entries */ -void GC_stubborn_init(); - +void GC_read_changed GC_PROTO((void)); /* Analogous to GC_read_dirty */ +GC_bool GC_page_was_changed GC_PROTO((struct hblk * h)); + /* Analogous to GC_page_was_dirty */ +void GC_clean_changing_list GC_PROTO((void)); + /* Collect obsolete changing list entries */ +void GC_stubborn_init GC_PROTO((void)); + /* Debugging print routines: */ -void GC_print_block_list(); -void GC_print_hblkfreelist(); -void GC_print_heap_sects(); -void GC_print_static_roots(); -void GC_dump(); +void GC_print_block_list GC_PROTO((void)); +void GC_print_hblkfreelist GC_PROTO((void)); +void GC_print_heap_sects GC_PROTO((void)); +void GC_print_static_roots GC_PROTO((void)); +void GC_dump GC_PROTO((void)); #ifdef KEEP_BACK_PTRS void GC_store_back_pointer(ptr_t source, ptr_t dest); @@ -1688,15 +1799,19 @@ void GC_dump(); /* Make arguments appear live to compiler */ # ifdef __WATCOMC__ - void GC_noop(void*, ...); + void GC_noop(void*, ...); # else - GC_API void GC_noop(); +# ifdef __DMC__ + GC_API void GC_noop(...); +# else + GC_API void GC_noop(); +# endif # endif -void GC_noop1(/* word arg */); +void GC_noop1 GC_PROTO((word)); /* Logging and diagnostic output: */ -GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long)); +GC_API void GC_printf GC_PROTO((GC_CONST char * format, long, long, long, long, long, long)); /* A version of printf that doesn't allocate, */ /* is restricted to long arguments, and */ /* (unfortunately) doesn't use varargs for */ @@ -1715,7 +1830,7 @@ GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, lon # define GC_printf6(f,a,b,c,d,e,g) GC_printf(f, (long)a, (long)b, (long)c, \ (long)d, (long)e, (long)g) -void GC_err_printf(/* format, a, b, c, d, e, f */); +GC_API void GC_err_printf GC_PROTO((GC_CONST char * format, long, long, long, long, long, long)); # define GC_err_printf0(f) GC_err_puts(f) # define GC_err_printf1(f,a) GC_err_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) # define GC_err_printf2(f,a,b) GC_err_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) @@ -1731,18 +1846,86 @@ void GC_err_printf(/* format, a, b, c, d, e, f */); (long)e, (long)g) /* Ditto, writes to stderr. */ -void GC_err_puts(/* char *s */); +void GC_err_puts GC_PROTO((GC_CONST char *s)); /* Write s to stderr, don't buffer, don't add */ /* newlines, don't ... */ +#if defined(LINUX) && !defined(SMALL_CONFIG) + void GC_err_write GC_PROTO((GC_CONST char *buf, size_t len)); + /* Write buf to stderr, don't buffer, don't add */ + /* newlines, don't ... */ +#endif -# ifdef GC_ASSERTIONS + +# ifdef GC_ASSERTIONS # define GC_ASSERT(expr) if(!(expr)) {\ GC_err_printf2("Assertion failure: %s:%ld\n", \ __FILE__, (unsigned long)__LINE__); \ ABORT("assertion failure"); } -# else +# else # define GC_ASSERT(expr) +# endif + +# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) + /* We need additional synchronization facilities from the thread */ + /* support. We believe these are less performance critical */ + /* than the main garbage collector lock; standard pthreads-based */ + /* implementations should be sufficient. */ + + /* The mark lock and condition variable. If the GC lock is also */ + /* acquired, the GC lock must be acquired first. The mark lock is */ + /* used to both protect some variables used by the parallel */ + /* marker, and to protect GC_fl_builder_count, below. */ + /* GC_notify_all_marker() is called when */ + /* the state of the parallel marker changes */ + /* in some significant way (see gc_mark.h for details). The */ + /* latter set of events includes incrementing GC_mark_no. */ + /* GC_notify_all_builder() is called when GC_fl_builder_count */ + /* reaches 0. */ + + extern void GC_acquire_mark_lock(); + extern void GC_release_mark_lock(); + extern void GC_notify_all_builder(); + /* extern void GC_wait_builder(); */ + extern void GC_wait_for_reclaim(); + + extern word GC_fl_builder_count; /* Protected by mark lock. */ +# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ +# ifdef PARALLEL_MARK + extern void GC_notify_all_marker(); + extern void GC_wait_marker(); + extern word GC_mark_no; /* Protected by mark lock. */ + + extern void GC_help_marker(word my_mark_no); + /* Try to help out parallel marker for mark cycle */ + /* my_mark_no. Returns if the mark cycle finishes or */ + /* was already done, or there was nothing to do for */ + /* some other reason. */ +# endif /* PARALLEL_MARK */ + +# if defined(LINUX_THREADS) || defined(IRIX_THREADS) \ + || defined(HPUX_THREADS) || defined(OSF1_THREADS) + /* We define the thread suspension signal here, so that we can refer */ + /* to it in the dirty bit implementation, if necessary. Ideally we */ + /* would allocate a (real-time ?) signal using the standard mechanism.*/ + /* unfortunately, there is no standard mechanism. (There is one */ + /* in Linux glibc, but it's not exported.) Thus we continue to use */ + /* the same hard-coded signals we've always used. */ +# if !defined(SIG_SUSPEND) +# if defined(LINUX_THREADS) +# if defined(SPARC) && !defined(SIGPWR) + /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>. + * It is aliased to SIGLOST in asm/signal.h, though. */ +# define SIG_SUSPEND SIGLOST +# else + /* Linuxthreads uses SIGUSR1 and SIGUSR2. */ +# define SIG_SUSPEND SIGPWR +# endif +# else /* !LINUX_THREADS */ +# define SIG_SUSPEND _SIGRTMIN + 6 # endif +# endif /* !SIG_SUSPEND */ + +# endif # endif /* GC_PRIVATE_H */ diff --git a/gc/include/private/gcconfig.h b/gc/include/private/gcconfig.h index c9017d3..a0d12c8 100644 --- a/gc/include/private/gcconfig.h +++ b/gc/include/private/gcconfig.h @@ -2,6 +2,7 @@ * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * 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. @@ -13,9 +14,9 @@ * modified is included with the above copyright notice. */ -#ifndef CONFIG_H +#ifndef GCCONFIG_H -# define CONFIG_H +# define GCCONFIG_H /* Machine dependent parameters. Some tuning parameters can be found */ /* near the top of gc_private.h. */ @@ -27,6 +28,11 @@ # define LINUX # endif +/* And one for NetBSD: */ +# if defined(__NetBSD__) +# define NETBSD +# endif + /* Determine the machine type: */ # if defined(sun) && defined(mc68000) # define M68K @@ -50,7 +56,14 @@ # endif # if defined(__NetBSD__) && defined(m68k) # define M68K -# define NETBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__powerpc__) +# define POWERPC +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__arm32__) +# define ARM32 # define mach_type_known # endif # if defined(vax) @@ -64,23 +77,29 @@ # endif # if defined(mips) || defined(__mips) # define MIPS -# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) -# define ULTRIX -# else -# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) -# define IRIX5 /* or IRIX 6.X */ -# else -# define RISCOS /* or IRIX 4.X */ -# endif +# if !defined(LINUX) +# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) +# define ULTRIX +# else +# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) \ + || defined(__SYSTYPE_SVR4__) +# define IRIX5 /* or IRIX 6.X */ +# else +# define RISCOS /* or IRIX 4.X */ +# endif +# endif +# endif /* !LINUX */ +# if defined(__NetBSD__) && defined(__MIPSEL__) +# undef ULTRIX # endif # define mach_type_known # endif -# if defined(sequent) && defined(i386) +# if defined(sequent) && (defined(i386) || defined(__i386__)) # define I386 # define SEQUENT # define mach_type_known # endif -# if defined(sun) && defined(i386) +# if defined(sun) && (defined(i386) || defined(__i386__)) # define I386 # define SUNOS5 # define mach_type_known @@ -106,7 +125,7 @@ # define mach_type_known # endif # if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ - && !defined(__OpenBSD__) + && !defined(__OpenBSD__) && !(__NetBSD__) # define SPARC # define DRSNX # define mach_type_known @@ -115,6 +134,10 @@ # define RS6000 # define mach_type_known # endif +# if defined(__NetBSD__) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif # if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) /* The above test may need refinement */ # define I386 @@ -130,16 +153,28 @@ # define SYSV # define mach_type_known # endif -# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \ || defined(hppa) || defined(__hppa__) # define HP_PA +# ifndef LINUX +# define HPUX +# endif # define mach_type_known # endif +# if defined(__BEOS__) && defined(_X86_) +# define I386 +# define BEOS +# define mach_type_known +# endif # if defined(LINUX) && (defined(i386) || defined(__i386__)) # define I386 # define mach_type_known # endif -# if defined(LINUX) && defined(powerpc) +# if defined(LINUX) && (defined(__ia64__) || defined(__ia64)) +# define IA64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__)) # define POWERPC # define mach_type_known # endif @@ -147,13 +182,21 @@ # define M68K # define mach_type_known # endif -# if defined(LINUX) && defined(sparc) +# if defined(LINUX) && (defined(sparc) || defined(__sparc__)) # define SPARC # define mach_type_known # endif +# if defined(LINUX) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__sh__) +# define SH +# define mach_type_known +# endif # if defined(__alpha) || defined(__alpha__) # define ALPHA -# if !defined(LINUX) +# if !defined(LINUX) && !defined(NETBSD) # define OSF1 /* a.k.a Digital Unix */ # endif # define mach_type_known @@ -175,37 +218,42 @@ # define MACOS # define mach_type_known # endif -# if defined(macosx) +# if defined(macosx) || \ + defined(__APPLE__) && defined(__MACH__) && defined(__ppc__) # define MACOSX # define POWERPC # define mach_type_known # endif +# if defined(__APPLE__) && defined(__MACH__) && defined(__i386__) +# define MACOSX +# define I386 + --> Not really supported, but at least we recognize it. +# endif # if defined(NeXT) && defined(mc68000) # define M68K # define NEXT # define mach_type_known # endif -# if defined(NeXT) && defined(i386) +# if defined(NeXT) && (defined(i386) || defined(__i386__)) # define I386 # define NEXT # define mach_type_known # endif -# if defined(__OpenBSD__) && defined(i386) +# if defined(__OpenBSD__) && (defined(i386) || defined(__i386__)) # define I386 # define OPENBSD # define mach_type_known # endif -# if defined(__FreeBSD__) && defined(i386) +# if defined(__FreeBSD__) && (defined(i386) || defined(__i386__)) # define I386 # define FREEBSD # define mach_type_known # endif -# if defined(__NetBSD__) && defined(i386) +# if defined(__NetBSD__) && (defined(i386) || defined(__i386__)) # define I386 -# define NETBSD # define mach_type_known # endif -# if defined(bsdi) && defined(i386) +# if defined(bsdi) && (defined(i386) || defined(__i386__)) # define I386 # define BSDI # define mach_type_known @@ -225,11 +273,26 @@ /* DGUX defined */ # define mach_type_known # endif -# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ - || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) -# define I386 -# define MSWIN32 /* or Win32s */ +# if defined(_WIN32_WCE) + /* SH3, SH4, MIPS already defined for corresponding architectures */ +# if defined(SH3) || defined(SH4) +# define SH +# endif +# if defined(x86) +# define I386 +# endif +# if defined(ARM) +# define ARM32 +# endif +# define MSWINCE # define mach_type_known +# else +# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define I386 +# define MSWIN32 /* or Win32s */ +# define mach_type_known +# endif # endif # if defined(__DJGPP__) # define I386 @@ -243,6 +306,11 @@ # define CYGWIN32 # define mach_type_known # endif +# if defined(__MINGW32__) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif # if defined(__BORLANDC__) # define I386 # define MSWIN32 @@ -253,6 +321,15 @@ # define UTS4 # define mach_type_known # endif +# if defined(__pj__) +# define PJ +# define mach_type_known +# endif +# if defined(__embedded__) && defined(PPC) +# define POWERPC +# define NOSYS +# define mach_type_known +# endif /* Ivan Demakov */ # if defined(__WATCOMC__) && defined(__386__) # define I386 @@ -269,6 +346,18 @@ # endif # define mach_type_known # endif +# if defined(__s390__) && defined(LINUX) +# define S370 +# define mach_type_known +# endif +# if defined(__GNU__) +# if defined(__i386__) +/* The Debian Hurd running on generic PC */ +# define HURD +# define I386 +# define mach_type_known +# endif +# endif /* Feel free to add more clauses here */ @@ -297,9 +386,9 @@ /* RS6000 ==> IBM RS/6000 AIX3.X */ /* RT ==> IBM PC/RT */ /* HP_PA ==> HP9000/700 & /800 */ - /* HP/UX */ - /* SPARC ==> SPARC under SunOS */ - /* (SUNOS4, SUNOS5, */ + /* HP/UX, LINUX */ + /* SPARC ==> SPARC v7/v8/v9 */ + /* (SUNOS4, SUNOS5, LINUX, */ /* DRSNX variants) */ /* ALPHA ==> DEC Alpha */ /* (OSF1 and LINUX variants) */ @@ -307,6 +396,12 @@ /* (CX_UX and DGUX) */ /* S370 ==> 370-like machine */ /* running Amdahl UTS4 */ + /* or a 390 running LINUX */ + /* ARM32 ==> Intel StrongARM */ + /* IA64 ==> Intel IA64 */ + /* (e.g. Itanium) */ + /* SH ==> Hitachi SuperH */ + /* (LINUX & MSWINCE) */ /* @@ -355,6 +450,13 @@ * to the nearest plausible page boundary, and use that instead * of STACKBOTTOM. * + * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines, + * the value of environ is a pointer that can serve as STACKBOTTOM. + * I expect that HEURISTIC2 can be replaced by this approach, which + * interferes far less with debugging. However it has the disadvantage + * that it's confused by a putenv call before the collector is initialized. + * This could be dealt with by intercepting putenv ... + * * If no expression for STACKBOTTOM can be found, and neither of the above * heuristics are usable, the collector can still be used with all of the above * undefined, provided one of the following is done: @@ -392,8 +494,25 @@ * * An architecture may define DYNAMIC_LOADING if dynamic_load.c * defined GC_register_dynamic_libraries() for the architecture. + * + * An architecture may define PREFETCH(x) to preload the cache with *x. + * This defaults to a no-op. + * + * PREFETCH_FOR_WRITE(x) is used if *x is about to be written. + * + * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to + * clear the two words at GC_malloc-aligned address x. By default, + * word stores of 0 are used instead. */ +/* If we are using a recent version of gcc, we can use __builtin_unwind_init() + * to push the relevant registers onto the stack. This generally makes + * USE_GENERIC_PUSH_REGS the preferred approach for marking from registers. + */ +# if defined(__GNUC__) && ((__GNUC__ >= 3) || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) +# define HAVE_BUILTIN_UNWIND_INIT +# endif # define STACK_GRAN 0x1000000 # ifdef M68K @@ -417,8 +536,12 @@ # define MPROTECT_VDB # ifdef __ELF__ # define DYNAMIC_LOADING - extern char **__environ; -# define DATASTART ((ptr_t)(&__environ)) +# include <features.h> +# if defined(__GLIBC__)&& __GLIBC__>=2 +# define LINUX_DATA_START +# else /* !GLIBC2 */ + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) /* hideous kludge: __environ is the first */ /* word in crt0.o, and delimits the start */ /* of the data segment, no matter which */ @@ -427,6 +550,7 @@ /* would include .rodata, which may */ /* contain large read-only data tables */ /* that we'd rather not scan. */ +# endif /* !GLIBC2 */ extern int _end; # define DATAEND (&_end) # else @@ -513,19 +637,43 @@ /* This was 2, but that didn't sound right. */ # define OS_TYPE "LINUX" # define HEURISTIC1 +# define DYNAMIC_LOADING # undef STACK_GRAN # define STACK_GRAN 0x10000000 /* Stack usually starts at 0x80000000 */ -# define DATASTART GC_data_start +# define LINUX_DATA_START extern int _end; # define DATAEND (&_end) # endif # ifdef MACOSX + /* There are reasons to suspect this may not be reliable. */ # define ALIGNMENT 4 # define OS_TYPE "MACOSX" # define DATASTART ((ptr_t) get_etext()) # define STACKBOTTOM ((ptr_t) 0xc0000000) # define DATAEND /* not needed */ +# define MPROTECT_VDB +# include <unistd.h> +# define GETPAGESIZE() getpagesize() +# endif +# ifdef NETBSD +# define ALIGNMENT 4 +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# endif +# ifdef NOSYS +# define ALIGNMENT 4 +# define OS_TYPE "NOSYS" + extern void __end, __dso_handle; +# define DATASTART (&__dso_handle) /* OK, that's ugly. */ +# define DATAEND (&__end) + /* Stack starts at 0xE0000000 for the simulator. */ +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define HEURISTIC1 # endif # endif @@ -554,9 +702,12 @@ # ifdef SPARC # define MACH_TYPE "SPARC" -# define ALIGNMENT 4 /* Required by hardware */ +# if defined(__arch64__) || defined(__sparcv9) +# define ALIGNMENT 8 +# else +# define ALIGNMENT 4 /* Required by hardware */ +# endif # define ALIGN_DOUBLE - extern int etext; # ifdef SUNOS5 # define OS_TYPE "SUNOS5" extern int _etext; @@ -573,10 +724,18 @@ # define HEAP_START DATAEND # endif # define PROC_VDB -/* HEURISTIC1 reportedly no longer works under 2.7. Thus we */ -/* switched to HEURISTIC2, eventhough it creates some debugging */ -/* issues. */ -# define HEURISTIC2 +/* HEURISTIC1 reportedly no longer works under 2.7. */ +/* HEURISTIC2 probably works, but this appears to be preferable. */ +/* Apparently USRSTACK is defined to be USERLIMIT, but in some */ +/* installations that's undefined. We work around this with a */ +/* gross hack: */ +# include <sys/vmparam.h> +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM USRSTACK +# else +# define HEURISTIC2 +# endif # include <unistd.h> # define GETPAGESIZE() sysconf(_SC_PAGESIZE) /* getpagesize() appeared to be missing from at least one */ @@ -615,21 +774,40 @@ # ifdef LINUX # define OS_TYPE "LINUX" # ifdef __ELF__ -# define DATASTART GC_data_start -# define DYNAMIC_LOADING +# define DYNAMIC_LOADING # else - Linux Sparc non elf ? + Linux Sparc/a.out not supported # endif extern int _end; + extern int _etext; # define DATAEND (&_end) # define SVR4 -# define STACKBOTTOM ((ptr_t) 0xf0000000) +# ifdef __arch64__ +# define STACKBOTTOM ((ptr_t) 0x80000000000ULL) +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x100000, &_etext) +# define CPP_WORDSZ 64 +# else +# define STACKBOTTOM ((ptr_t) 0xf0000000) +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +# endif # endif # ifdef OPENBSD # define OS_TYPE "OPENBSD" # define STACKBOTTOM ((ptr_t) 0xf8000000) + extern int etext; # define DATASTART ((ptr_t)(&etext)) # endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# endif # endif # ifdef I386 @@ -642,18 +820,34 @@ # define ALIGN_DOUBLE /* Not strictly necessary, but may give speed */ /* improvement on Pentiums. */ # endif +# ifdef HAVE_BUILTIN_UNWIND_INIT +# define USE_GENERIC_PUSH_REGS +# endif # ifdef SEQUENT # define OS_TYPE "SEQUENT" extern int etext; # define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) # define STACKBOTTOM ((ptr_t) 0x3ffff000) # endif +# ifdef BEOS +# define OS_TYPE "BEOS" +# include <OS.h> +# define GETPAGESIZE() B_PAGE_SIZE + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif # ifdef SUNOS5 # define OS_TYPE "SUNOS5" extern int etext, _start; extern char * GC_SysVGetDataStart(); # define DATASTART GC_SysVGetDataStart(0x1000, &etext) -# define STACKBOTTOM ((ptr_t)(&_start)) +/* # define STACKBOTTOM ((ptr_t)(&_start)) worked through 2.7, */ +/* but reportedly breaks under 2.8. It appears that the stack */ +/* base is a property of the executable, so this should not break */ +/* old executables. */ +/* HEURISTIC2 probably works, but this appears to be preferable. */ +# include <sys/vmparam.h> +# define STACKBOTTOM USRSTACK /** At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ /*# define PROC_VDB*/ # define DYNAMIC_LOADING @@ -684,13 +878,16 @@ # endif # ifdef LINUX # define OS_TYPE "LINUX" -# define HEURISTIC1 -# undef STACK_GRAN -# define STACK_GRAN 0x10000000 - /* STACKBOTTOM is usually 0xc0000000, but this changes with */ - /* different kernel configurations. In particular, systems */ - /* with 2GB physical memory will usually move the user */ - /* address space limit, and hence initial SP to 0x80000000. */ +# define LINUX_STACKBOTTOM +# if 0 +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 + /* STACKBOTTOM is usually 0xc0000000, but this changes with */ + /* different kernel configurations. In particular, systems */ + /* with 2GB physical memory will usually move the user */ + /* address space limit, and hence initial SP to 0x80000000. */ +# endif # if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC) # define MPROTECT_VDB # else @@ -706,8 +903,7 @@ # endif # include <features.h> # if defined(__GLIBC__) && __GLIBC__ >= 2 - extern int __data_start; -# define DATASTART ((ptr_t)(&__data_start)) +# define LINUX_DATA_START # else extern char **__environ; # define DATASTART ((ptr_t)(&__environ)) @@ -726,6 +922,26 @@ extern int etext; # define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) # endif +# ifdef USE_I686_PREFETCH +# define PREFETCH(x) \ + __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(x))) + /* Empirically prefetcht0 is much more effective at reducing */ + /* cache miss stalls for the targetted load instructions. But it */ + /* seems to interfere enough with other cache traffic that the net */ + /* result is worse than prefetchnta. */ +# if 0 + /* Using prefetches for write seems to have a slight negative */ + /* impact on performance, at least for a PIII/500. */ +# define PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ (" prefetcht0 %0": : "m"(*(char *)(x))) +# endif +# endif +# ifdef USE_3DNOW_PREFETCH +# define PREFETCH(x) \ + __asm__ __volatile__ (" prefetch %0": : "m"(*(char *)(x))) +# define PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ (" prefetchw %0": : "m"(*(char *)(x))) +# endif # endif # ifdef CYGWIN32 # define OS_TYPE "CYGWIN32" @@ -755,6 +971,7 @@ /* os_dep.c. OS2 actually has the right */ /* system call! */ # define DATAEND /* not needed */ +# define USE_GENERIC_PUSH_REGS # endif # ifdef MSWIN32 # define OS_TYPE "MSWIN32" @@ -765,6 +982,10 @@ # endif # define DATAEND /* not needed */ # endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif # ifdef DJGPP # define OS_TYPE "DJGPP" # include "stubinfo.h" @@ -783,6 +1004,7 @@ # ifdef FREEBSD # define OS_TYPE "FREEBSD" # define MPROTECT_VDB +# define FREEBSD_STACKBOTTOM # endif # ifdef NETBSD # define OS_TYPE "NETBSD" @@ -793,7 +1015,7 @@ # ifdef BSDI # define OS_TYPE "BSDI" # endif -# if defined(OPENBSD) || defined(FREEBSD) || defined(NETBSD) \ +# if defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ || defined(THREE86BSD) || defined(BSDI) # define HEURISTIC2 extern char etext; @@ -820,6 +1042,17 @@ # define DATASTART ((ptr_t) &__nullarea) # define DATAEND ((ptr_t) &_end) # endif +# ifdef HURD +# define OS_TYPE "HURD" +# define STACK_GROWS_DOWN +# define HEURISTIC2 + extern int __data_start; +# define DATASTART ( (ptr_t) (&__data_start)) + extern int _end; +# define DATAEND ( (ptr_t) (&_end)) +/* # define MPROTECT_VDB Not quite working yet? */ +# define DYNAMIC_LOADING +# endif # endif # ifdef NS32K @@ -836,35 +1069,48 @@ # ifdef MIPS # define MACH_TYPE "MIPS" -# ifndef IRIX5 -# define DATASTART (ptr_t)0x10000000 +# ifdef LINUX + /* This was developed for a linuxce style platform. Probably */ + /* needs to be tweaked for workstation class machines. */ +# define OS_TYPE "LINUX" + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# define ALIGNMENT 4 +# define USE_GENERIC_PUSH_REGS +# define STACKBOTTOM ((ptr_t)0x7fff8000) + /* Older toolchains may need 0x80000000. */ + /* In many cases, this should probably use LINUX_STACKBOTTOM */ + /* instead. But some kernel versions seem to give the wrong */ + /* value from /proc. */ +# endif /* Linux */ +# ifdef ULTRIX +# define HEURISTIC2 +# define DATASTART (ptr_t)0x10000000 /* Could probably be slightly higher since */ /* startup code allocates lots of stuff. */ -# else - extern int _fdata; -# define DATASTART ((ptr_t)(&_fdata)) -# ifdef USE_MMAP -# define HEAP_START (ptr_t)0x30000000 -# else -# define HEAP_START DATASTART -# endif - /* Lowest plausible heap address. */ - /* In the MMAP case, we map there. */ - /* In either case it is used to identify */ - /* heap sections so they're not */ - /* considered as roots. */ -# endif /* IRIX5 */ -# define HEURISTIC2 -/* # define STACKBOTTOM ((ptr_t)0x7fff8000) sometimes also works. */ -# ifdef ULTRIX # define OS_TYPE "ULTRIX" # define ALIGNMENT 4 # endif # ifdef RISCOS +# define HEURISTIC2 +# define DATASTART (ptr_t)0x10000000 # define OS_TYPE "RISCOS" # define ALIGNMENT 4 /* Required by hardware */ # endif # ifdef IRIX5 +# define HEURISTIC2 + extern int _fdata; +# define DATASTART ((ptr_t)(&_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ # define OS_TYPE "IRIX5" # define MPROTECT_VDB # ifdef _MIPS_SZPTR @@ -879,6 +1125,28 @@ # endif # define DYNAMIC_LOADING # endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif +# if defined(NETBSD) + /* This also checked for __MIPSEL__ . Why? NETBSD recognition */ + /* should be handled at the top of the file. */ +# define ALIGNMENT 4 +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# define USE_GENERIC_PUSH_REGS +# ifdef __ELF__ + extern int etext; +# define DATASTART GC_data_start +# define NEED_FIND_LIMIT +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x10000000) +# define STACKBOTTOM ((ptr_t) 0x7ffff000) +# endif /* _ELF_ */ +# endif # endif # ifdef RS6000 @@ -893,11 +1161,34 @@ # ifdef HP_PA # define MACH_TYPE "HP_PA" -# define ALIGNMENT 4 -# define ALIGN_DOUBLE - extern int __data_start; -# define DATASTART ((ptr_t)(&__data_start)) -# if 0 +# define OS_TYPE "HPUX" +# ifdef __LP64__ +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define ALIGN_DOUBLE +# endif +# if !defined(GC_HPUX_THREADS) && !defined(HPUX_THREADS) \ + && !defined(GC_LINUX_THREADS) && !defined(LINUX_THREADS) +# ifndef LINUX /* For now. */ +# define MPROTECT_VDB +# endif +# else +# define GENERIC_COMPARE_AND_SWAP + /* No compare-and-swap instruction. Use pthread mutexes */ + /* when we absolutely have to. */ +# ifdef PARALLEL_MARK +# define USE_MARK_BYTES + /* Minimize compare-and-swap usage. */ +# endif +# endif +# define STACK_GROWS_UP +# ifdef HPUX + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# if 0 /* The following appears to work for 7xx systems running HP/UX */ /* 9.xx Furthermore, it might result in much faster */ /* collections than HEURISTIC2, which may involve scanning */ @@ -906,28 +1197,66 @@ /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ /* this.) */ # define STACKBOTTOM ((ptr_t) 0x7b033000) /* from /etc/conf/h/param.h */ -# else -# define HEURISTIC2 -# endif -# define STACK_GROWS_UP -# define DYNAMIC_LOADING -# include <unistd.h> -# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) - /* They misspelled the Posix macro? */ -# endif +# else + /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2 */ + /* to this. Note that the GC must be initialized before the */ + /* first putenv call. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# endif +# define DYNAMIC_LOADING +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) +# ifndef __GNUC__ +# define PREFETCH(x) { \ + register long addr = (long)(x); \ + (void) _asm ("LDW", 0, 0, addr, 0); \ + } +# endif +# endif /* HPUX */ +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# define LINUX_DATA_START + extern int _end; +# define DATAEND (&_end) +# endif /* LINUX */ +# endif /* HP_PA */ # ifdef ALPHA # define MACH_TYPE "ALPHA" # define ALIGNMENT 8 +# define USE_GENERIC_PUSH_REGS + /* Gcc and probably the DEC/Compaq compiler spill pointers to preserved */ + /* fp registers in some cases when the target is a 21264. The assembly */ + /* code doesn't handle that yet, and version dependencies make that a */ + /* bit tricky. Do the easy thing for now. */ +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# define DATASTART GC_data_start +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# define CPP_WORDSZ 64 +# define DYNAMIC_LOADING +# endif # ifdef OSF1 # define OS_TYPE "OSF1" # define DATASTART ((ptr_t) 0x140000000) - extern _end; + extern int _end; # define DATAEND ((ptr_t) &_end) -# define HEURISTIC2 + extern char ** environ; + /* round up from the value of environ to the nearest page boundary */ + /* Probably breaks if putenv is called before collector */ + /* initialization. */ +# define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1)) +/* # define HEURISTIC2 */ /* Normally HEURISTIC2 is too conervative, since */ /* the text segment immediately follows the stack. */ /* Hence we give an upper pound. */ + /* This is currently unused, since we disabled HEURISTIC2 */ extern int __start; # define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) # define CPP_WORDSZ 64 @@ -939,12 +1268,8 @@ # define CPP_WORDSZ 64 # define STACKBOTTOM ((ptr_t) 0x120000000) # ifdef __ELF__ -# if 0 - /* __data_start apparently disappeared in some recent releases. */ - extern int __data_start; -# define DATASTART &__data_start -# endif -# define DATASTART GC_data_start +# define SEARCH_FOR_DATA_START +# define DATASTART GC_data_start # define DYNAMIC_LOADING # else # define DATASTART ((ptr_t) 0x140000000) @@ -957,6 +1282,55 @@ # endif # endif +# ifdef IA64 +# define MACH_TYPE "IA64" +# define ALIGN_DOUBLE + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# define USE_GENERIC_PUSH_REGS + /* We need to get preserved registers in addition to register */ + /* windows. That's easiest to do with setjmp. */ +# ifdef PARALLEL_MARK +# define USE_MARK_BYTES + /* Compare-and-exchange is too expensive to use for */ + /* setting mark bits. */ +# endif +# ifdef HPUX + --> needs work +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define CPP_WORDSZ 64 + /* The following works on NUE and older kernels: */ +/* # define STACKBOTTOM ((ptr_t) 0xa000000000000000l) */ + /* This does not work on NUE: */ +# define LINUX_STACKBOTTOM + /* We also need the base address of the register stack */ + /* backing store. This is computed in */ + /* GC_linux_register_stack_base based on the following */ + /* constants: */ +# define BACKING_STORE_ALIGNMENT 0x100000 +# define BACKING_STORE_DISPLACEMENT 0x80000000 + extern char * GC_register_stackbottom; +# define BACKING_STORE_BASE ((ptr_t)GC_register_stackbottom) +# define SEARCH_FOR_DATA_START +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# define MPROTECT_VDB + /* Requires Linux 2.3.47 or later. */ + extern int _end; +# define DATAEND (&_end) +# ifdef __GNUC__ +# define PREFETCH(x) \ + __asm__ (" lfetch [%0]": : "r"((void *)(x))) +# define PREFETCH_FOR_WRITE(x) \ + __asm__ (" lfetch.excl [%0]": : "r"((void *)(x))) +# define CLEAR_DOUBLE(x) \ + __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x))) +# endif +# endif +# endif + # ifdef M88K # define MACH_TYPE "M88K" # define ALIGNMENT 4 @@ -976,17 +1350,127 @@ # ifdef S370 # define MACH_TYPE "S370" -# define OS_TYPE "UTS4" # define ALIGNMENT 4 /* Required by hardware */ - extern int etext; +# define USE_GENERIC_PUSH_REGS +# ifdef UTS4 +# define OS_TYPE "UTS4" + extern int etext; extern int _etext; extern int _end; extern char * GC_SysVGetDataStart(); # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) # define DATAEND (&_end) # define HEURISTIC2 +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# define DYNAMIC_LOADING + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# endif +# endif + +# if defined(PJ) +# define ALIGNMENT 4 + extern int _etext; +# define DATASTART ((ptr_t)(&_etext)) +# define HEURISTIC1 +# endif + +# ifdef ARM32 +# define CPP_WORDSZ 32 +# define MACH_TYPE "ARM32" +# define ALIGNMENT 4 +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# define USE_GENERIC_PUSH_REGS +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define USE_GENERIC_PUSH_REGS +# ifdef __ELF__ +# define DYNAMIC_LOADING +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 +# define LINUX_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif +#endif + +# ifdef SH +# define MACH_TYPE "SH" +# define ALIGNMENT 4 +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t) 0x7c000000) +# define USE_GENERIC_PUSH_REGS +# define DYNAMIC_LOADING +# define LINUX_DATA_START + extern int _end; +# define DATAEND (&_end) +# endif # endif + +# ifdef SH4 +# define MACH_TYPE "SH4" +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif + +#ifdef LINUX_DATA_START + /* Some Linux distributions arrange to define __data_start. Some */ + /* define data_start as a weak symbol. The latter is technically */ + /* broken, since the user program may define data_start, in which */ + /* case we lose. Nonetheless, we try both, prefering __data_start. */ + /* We assume gcc. */ +# pragma weak __data_start + extern int __data_start; +# pragma weak data_start + extern int data_start; +# define DATASTART ((ptr_t)(&__data_start != 0? &__data_start : &data_start)) +#endif +#if defined(LINUX) && defined(REDIRECT_MALLOC) + /* Rld appears to allocate some memory with its own allocator, and */ + /* some through malloc, which might be redirected. To make this */ + /* work with collectable memory, we have to scan memory allocated */ + /* by rld's internal malloc. */ +# define USE_PROC_FOR_LIBRARIES +#endif + # ifndef STACK_GROWS_UP # define STACK_GROWS_DOWN # endif @@ -1029,6 +1513,16 @@ # define SUNOS5SIGS # endif +# if defined(HPUX) +# define SUNOS5SIGS +# endif + +# if defined(SVR4) || defined(LINUX) || defined(IRIX) || defined(HPUX) \ + || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ + || defined(BSD) || defined(AIX) || defined(MACOSX) || defined(OSF1) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +# endif + # if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 -> bad word size # endif @@ -1059,41 +1553,107 @@ # undef MPROTECT_VDB /* Can't deal with address space holes. */ # endif +# ifdef PARALLEL_MARK +# undef MPROTECT_VDB /* For now. */ +# endif + # if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) # define DEFAULT_VDB # endif +# ifndef PREFETCH +# define PREFETCH(x) +# define NO_PREFETCH +# endif + +# ifndef PREFETCH_FOR_WRITE +# define PREFETCH_FOR_WRITE(x) +# define NO_PREFETCH_FOR_WRITE +# endif + +# ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 32 /* Wild guess */ +# endif + +# ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) \ + ((word*)x)[0] = 0; \ + ((word*)x)[1] = 0; +# endif /* CLEAR_DOUBLE */ + +/* Internally to the collector we test only the XXX_THREADS macros */ +/* not the GC_XXX_THREADS versions. Here we make sure the latter */ +/* are treated as equivalent. */ +#if defined(GC_SOLARIS_THREADS) && !defined(_SOLARIS_THREADS) +# define _SOLARIS_THREADS +#endif +#if defined(GC_SOLARIS_THREADS) && !defined(_SOLARIS_PTHREADS) +# define _SOLARIS_PTHREADS +#endif +#if defined(GC_IRIX_THREADS) && !defined(IRIX_THREADS) +# define IRIX_THREADS +#endif +#if defined(GC_LINUX_THREADS) && !defined(LINUX_THREADS) +# define LINUX_THREADS +#endif +#if defined(GC_WIN32_THREADS) && !defined(WIN32_THREADS) +# define WIN32_THREADS +#endif +#if defined(GC_HPUX_THREADS) && !defined(HPUX_THREADS) +# define HPUX_THREADS +#endif +#if defined(GC_OSF1_THREADS) && !defined(OSF1_THREADS) +# define OSF1_THREADS +#endif + +/* Internally we use SOLARIS_THREADS to test for either old or pthreads. */ # if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) # define SOLARIS_THREADS # endif # if defined(IRIX_THREADS) && !defined(IRIX5) --> inconsistent configuration # endif -# if defined(IRIX_JDK_THREADS) && !defined(IRIX5) ---> inconsistent configuration -# endif # if defined(LINUX_THREADS) && !defined(LINUX) --> inconsistent configuration # endif # if defined(SOLARIS_THREADS) && !defined(SUNOS5) --> inconsistent configuration # endif +# if defined(HPUX_THREADS) && !defined(HPUX) +--> inconsistent configuration +# endif +# if defined(WIN32_THREADS) && !defined(MSWIN32) + /* Ideally CYGWIN32 should work, in addition to MSWIN32. I suspect the necessary code */ + /* is mostly there, but nobody has actually made sure the right combination of pieces is */ + /* compiled in, etc. */ +--> inconsistent configuration +# endif # if defined(PCR) || defined(SRC_M3) || \ defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ - defined(IRIX_JDK_THREADS) + defined(HPUX_THREADS) || defined(OSF1_THREADS) # define THREADS # endif -# if defined(HP_PA) || defined(M88K) || defined(POWERPC) \ - || (defined(I386) && defined(OS2)) || defined(UTS4) || defined(LINT) - /* Use setjmp based hack to mark from callee-save registers. */ +# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(MACOSX) \ + || defined(LINT) || defined(MSWINCE) \ + || (defined(I386) && defined(__LCC__)) + /* Use setjmp based hack to mark from callee-save registers. */ + /* The define should move to the individual platform */ + /* descriptions. */ # define USE_GENERIC_PUSH_REGS # endif -# if defined(SPARC) && !defined(LINUX) +# if defined(I386) && defined(LINUX) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +# ifdef SAVE_CALL_COUNT +# define SAVE_CALL_CHAIN +# endif +# endif +# if defined(SPARC) # define SAVE_CALL_CHAIN # define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ /* include assembly code to do it well. */ # endif -# endif +# endif /* GCCONFIG_H */ diff --git a/gc/solaris_threads.h b/gc/include/private/solaris_threads.h index b2cdb36..b2cdb36 100644 --- a/gc/solaris_threads.h +++ b/gc/include/private/solaris_threads.h diff --git a/gc/include/private/specific.h b/gc/include/private/specific.h new file mode 100644 index 0000000..60c152c --- /dev/null +++ b/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); +} + + |