diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2011-07-26 15:20:24 +0400 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2011-07-26 15:20:24 +0400 |
commit | c521d2cf164f9d7e1fa8be9c0b21d223e265fa64 (patch) | |
tree | 82fbd7e60e6be39fbabd946717cf065a698cd038 | |
parent | 7d3768dbd2a1cd4d5c14f773f23aec43bc0651a5 (diff) | |
download | bdwgc-c521d2cf164f9d7e1fa8be9c0b21d223e265fa64.tar.gz |
gc4.13 tarball importgc4_13
-rw-r--r-- | BCC_MAKEFILE | 2 | ||||
-rw-r--r-- | EMX_MAKEFILE | 2 | ||||
-rw-r--r-- | MacOS.c | 22 | ||||
-rw-r--r-- | Mac_files/MacOS_Test_config.h | 19 | ||||
-rw-r--r-- | Mac_files/MacOS_config.h | 13 | ||||
-rw-r--r-- | Makefile | 106 | ||||
-rw-r--r-- | Makefile.dj | 28 | ||||
-rw-r--r-- | PCR-Makefile | 2 | ||||
-rw-r--r-- | README | 122 | ||||
-rw-r--r-- | README.Mac | 15 | ||||
-rw-r--r-- | README.alpha | 7 | ||||
-rw-r--r-- | README.hp | 4 | ||||
-rw-r--r-- | README.linux | 49 | ||||
-rw-r--r-- | README.rs6000 | 3 | ||||
-rw-r--r-- | README.sgi | 17 | ||||
-rw-r--r-- | README.solaris2 | 14 | ||||
-rw-r--r-- | README.win32 | 83 | ||||
-rw-r--r-- | SMakefile.amiga | 2 | ||||
-rw-r--r-- | WCC_MAKEFILE | 179 | ||||
-rw-r--r-- | allchblk.c | 36 | ||||
-rw-r--r-- | alloc.c | 118 | ||||
-rw-r--r-- | blacklst.c | 54 | ||||
-rw-r--r-- | checksums.c | 4 | ||||
-rw-r--r-- | cord/README | 4 | ||||
-rw-r--r-- | cord/cordbscs.c | 1 | ||||
-rw-r--r-- | cord/cordprnt.c | 3 | ||||
-rw-r--r-- | cord/cordxtra.c | 6 | ||||
-rw-r--r-- | cord/de_win.c | 1 | ||||
-rw-r--r-- | cord/gc.h | 103 | ||||
-rw-r--r-- | dbg_mlc.c | 134 | ||||
-rw-r--r-- | dyn_load.c | 10 | ||||
-rw-r--r-- | finalize.c | 12 | ||||
-rw-r--r-- | gc.h | 103 | ||||
-rw-r--r-- | gc.mak | 92 | ||||
-rw-r--r-- | gc_alloc.h | 18 | ||||
-rw-r--r-- | gc_cpp.cc | 14 | ||||
-rw-r--r-- | gc_cpp.h | 3 | ||||
-rw-r--r-- | gc_mark.h | 35 | ||||
-rw-r--r-- | gc_priv.h | 376 | ||||
-rw-r--r-- | gc_watcom.asm | 51 | ||||
-rw-r--r-- | gcconfig.h (renamed from config.h) | 212 | ||||
-rw-r--r-- | headers.c | 21 | ||||
-rw-r--r-- | if_mach.c | 6 | ||||
-rw-r--r-- | if_not_there.c | 2 | ||||
-rw-r--r-- | include/gc.h | 103 | ||||
-rw-r--r-- | include/gc_alloc.h | 18 | ||||
-rw-r--r-- | include/gc_cpp.h | 3 | ||||
-rw-r--r-- | include/javaxfc.h | 41 | ||||
-rw-r--r-- | include/new_gc_alloc.h | 456 | ||||
-rw-r--r-- | include/private/gc_priv.h | 376 | ||||
-rw-r--r-- | include/private/gcconfig.h (renamed from include/private/config.h) | 432 | ||||
-rw-r--r-- | irix_threads.c | 176 | ||||
-rw-r--r-- | linux_threads.c | 665 | ||||
-rw-r--r-- | mach_dep.c | 11 | ||||
-rw-r--r-- | malloc.c | 13 | ||||
-rw-r--r-- | mallocx.c | 8 | ||||
-rw-r--r-- | mark.c | 81 | ||||
-rw-r--r-- | mark_rts.c | 153 | ||||
-rw-r--r-- | mips_sgi_mach_dep.s | 1 | ||||
-rw-r--r-- | misc.c | 75 | ||||
-rw-r--r-- | new_hblk.c | 2 | ||||
-rw-r--r-- | obj_map.c | 2 | ||||
-rw-r--r-- | os_dep.c | 291 | ||||
-rw-r--r-- | pcr_interface.c | 6 | ||||
-rw-r--r-- | ptr_chck.c | 4 | ||||
-rw-r--r-- | reclaim.c | 26 | ||||
-rw-r--r-- | setjmp_t.c | 2 | ||||
-rw-r--r-- | solaris_pthreads.c | 6 | ||||
-rw-r--r-- | solaris_threads.c | 14 | ||||
-rw-r--r-- | solaris_threads.h | 2 | ||||
-rw-r--r-- | stubborn.c | 6 | ||||
-rw-r--r-- | test.c | 167 | ||||
-rw-r--r-- | test_cpp.cc | 34 | ||||
-rw-r--r-- | threadlibs.c | 4 | ||||
-rw-r--r-- | typd_mlc.c | 9 | ||||
-rw-r--r-- | version.h | 2 | ||||
-rwxr-xr-x | win32_threads.c | 55 |
77 files changed, 4238 insertions, 1114 deletions
diff --git a/BCC_MAKEFILE b/BCC_MAKEFILE index c65a73e2..225a1ed4 100644 --- a/BCC_MAKEFILE +++ b/BCC_MAKEFILE @@ -39,7 +39,7 @@ OBJS= $(XXXOBJS:XXX=) all: gctest.exe cord\de.exe test_cpp.exe
-$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h config.h MAKEFILE
+$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h gcconfig.h MAKEFILE
gc.lib: $(OBJS)
-del gc.lib
diff --git a/EMX_MAKEFILE b/EMX_MAKEFILE index 1ade12ad..54a06ce3 100644 --- a/EMX_MAKEFILE +++ b/EMX_MAKEFILE @@ -72,7 +72,7 @@ SPECIALCFLAGS = all: gc.a gctest.exe $(OBJS) test.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ - $(srcdir)/config.h $(srcdir)/gc_typed.h + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h # The dependency on Makefile is needed. Changing # options such as -DSILENT affects the size of GC_arrays, # invalidating all .o files that rely on gc_priv.h @@ -8,7 +8,8 @@ 11/22/94 pcb StripAddress the temporary memory handle for 24-bit mode. 11/30/94 pcb Tracking all memory usage so we can deallocate it all at once. - 02/10/96 pcb Added routine to perform a final collection when
unloading shared library. + 02/10/96 pcb Added routine to perform a final collection when +unloading shared library. by Patrick C. Beard. */ @@ -127,10 +128,27 @@ void GC_MacFreeTemporaryMemory() } theTemporaryMemory = NULL; -# if !defined(SHARED_LIBRARY_BUILD) +# if !defined(SILENT) && !defined(SHARED_LIBRARY_BUILD) fprintf(stdout, "[total memory used: %ld bytes.]\n", totalMemoryUsed); fprintf(stdout, "[total collections: %ld.]\n", GC_gc_no); # endif } } + +#if __option(far_data) + + void* GC_MacGetDataEnd() + { + CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0); + if (code0) { + long aboveA5Size = (**code0).aboveA5; + ReleaseResource((Handle)code0); + return (LMGetCurrentA5() + aboveA5Size); + } + fprintf(stderr, "Couldn't load the jump table."); + exit(-1); + return 0; + } + +#endif /* __option(far_data) */ diff --git a/Mac_files/MacOS_Test_config.h b/Mac_files/MacOS_Test_config.h index 94db03f0..c95f4bb2 100644 --- a/Mac_files/MacOS_Test_config.h +++ b/Mac_files/MacOS_Test_config.h @@ -14,11 +14,14 @@ /* Boehm, November 17, 1995 12:05 pm PST */ #ifdef __MWERKS__ -#if defined(__powerc) -#include <MacHeadersPPC> -#else -#include <MacHeaders68K> + +// for CodeWarrior Pro with Metrowerks Standard Library (MSL). +// #define MSL_USE_PRECOMPILED_HEADERS 0 +#include <ansi_prefix.mac.h> +#ifndef __STDC__ +#define __STDC__ 0 #endif + #endif // these are defined again in gc_priv.h. @@ -26,10 +29,10 @@ #undef FALSE #define ALL_INTERIOR_POINTERS // follows interior pointers. -//#define SILENT // want collection messages. +//#define SILENT // want collection messages. //#define DONT_ADD_BYTE_AT_END // no padding. -//#define SMALL_CONFIG // whether to a smaller heap. -#define NO_SIGNALS // signals aren't real on the Macintosh. +//#define SMALL_CONFIG // whether to a smaller heap. +#define NO_SIGNALS // signals aren't real on the Macintosh. #define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. // CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT @@ -85,4 +88,4 @@ // since some ports use malloc or calloc to obtain system memory. // (Probably works for UNIX, and win32.) // -DNO_DEBUG removes GC_dump and the debugging routines it calls. -// Reduces code size slightly at the expense of debuggability.
\ No newline at end of file +// Reduces code size slightly at the expense of debuggability. diff --git a/Mac_files/MacOS_config.h b/Mac_files/MacOS_config.h index 838be591..93c3c97a 100644 --- a/Mac_files/MacOS_config.h +++ b/Mac_files/MacOS_config.h @@ -12,13 +12,16 @@ /* Boehm, November 17, 1995 12:10 pm PST */ #ifdef __MWERKS__ -#if defined(__powerc) -#include <MacHeadersPPC> -#else -#include <MacHeaders68K> -#endif + +// for CodeWarrior Pro with Metrowerks Standard Library (MSL). +// #define MSL_USE_PRECOMPILED_HEADERS 0 +#include <ansi_prefix.mac.h> +#ifndef __STDC__ +#define __STDC__ 0 #endif +#endif /* __MWERKS__ */ + // these are defined again in gc_priv.h. #undef TRUE #undef FALSE @@ -7,15 +7,19 @@ # and runs some tests of collector and cords. Does not add cords or # c++ interface to gc.a # cord/de - builds dumb editor based on cords. -CC=cc -CXX=CC -AS=as +ABI_FLAG= +CC=cc $(ABI_FLAG) +CXX=CC $(ABI_FLAG) +AS=as $(ABI_FLAG) # The above doesn't work with gas, which doesn't run cpp. # Define AS as `gcc -c -x assembler-with-cpp' instead. -# Under Irix 6, you will have to specify the ABI for as if you specify -# it for the C compiler. +# Under Irix 6, you will have to specify the ABI (-o32, -n32, or -64) +# if you use something other than the default ABI on your machine. -CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DSILENT +CFLAGS= -O -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION -DSILENT + +# For dynamic library builds, it may be necessary to add flags to generate +# PIC code, e.g. -fPIC on Linux. # Setjmp_test may yield overly optimistic results when compiled # without optimization. @@ -29,10 +33,12 @@ CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DATOMIC_UNCOLLECTABLE -DNO_EXEC # -DSOLARIS_THREADS enables support for Solaris (thr_) threads. # (Clients should also define SOLARIS_THREADS and then include # gc.h before performing thr_ or dl* or GC_ operations.) -# Must also define -D_REENTRANT +# Must also define -D_REENTRANT. # -D_SOLARIS_PTHREADS enables support for Solaris pthreads. # Define SOLARIS_THREADS as well. # -DIRIX_THREADS enables support for Irix pthreads. See README.irix. +# -DLINUX_THREADS enables support for Xavier Leroy's Linux threads. +# see README.linux. -D_REENTRANT may also be required. # -DALL_INTERIOR_POINTERS allows all pointers to the interior # of objects to be recognized. (See gc_priv.h for consequences.) # -DSMALL_CONFIG tries to tune the collector for small heap sizes, @@ -80,6 +86,8 @@ CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DATOMIC_UNCOLLECTABLE -DNO_EXEC # in a sepearte postpass, and hence their memory won't be reclaimed. # Not recommended unless you are implementing a language that specifies # these semantics. +# -DFINALIZE_ON_DEMAND causes finalizers to be run only in response +# to explicit GC_invoke_finalizers() calls. # -DATOMIC_UNCOLLECTABLE includes code for GC_malloc_atomic_uncollectable. # This is useful if either the vendor malloc implementation is poor, # or if REDIRECT_MALLOC is used. @@ -92,6 +100,18 @@ CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DATOMIC_UNCOLLECTABLE -DNO_EXEC # Works for Solaris and Irix. # -DMMAP_STACKS (for Solaris threads) Use mmap from /dev/zero rather than # GC_scratch_alloc() to get stack memory. +# -DPRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever +# the garbage collector detects a value that looks almost, but not quite, +# like a pointer, print both the address containing the value, and the +# value of the near-bogus-pointer. Can be used to identifiy regions of +# memory that are likely to contribute misidentified pointers. +# -DOLD_BLOCK_ALLOC Use the old, possibly faster, large block +# allocation strategy. The new strategy tries harder to minimize +# fragmentation, sometimes at the expense of spending more time in the +# large block allocator and/or collecting more frequently. +# If you expect the allocator to promtly use an explicitly expanded +# heap, this is highly recommended. +# @@ -111,9 +131,9 @@ RANLIB= ranlib srcdir = . VPATH = $(srcdir) -OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o -CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c cord/cord.h cord/ec.h cord/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC cord/SCOPTIONS.amiga cord/SMakefile.amiga @@ -121,16 +141,17 @@ CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ sparc_mach_dep.s gc.h gc_typed.h gc_hdrs.h gc_priv.h gc_private.h \ - config.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ + gcconfig.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ threadlibs.c if_mach.c if_not_there.c gc_cpp.cc gc_cpp.h weakpointer.h \ gcc_support.c mips_ultrix_mach_dep.s include/gc_alloc.h gc_alloc.h \ - sparc_sunos4_mach_dep.s solaris_threads.h $(CORD_SRCS) + include/new_gc_alloc.h include/javaxfc.h sparc_sunos4_mach_dep.s \ + solaris_threads.h $(CORD_SRCS) OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \ SCoptions.amiga README.amiga README.win32 cord/README \ cord/gc.h include/gc.h include/gc_typed.h include/cord.h \ - include/ec.h include/private/cord_pos.h include/private/config.h \ + include/ec.h include/private/cord_pos.h include/private/gcconfig.h \ include/private/gc_hdrs.h include/private/gc_priv.h \ include/gc_cpp.h README.rs6000 \ include/weakpointer.h README.QUICK callprocs pc_excludes \ @@ -140,7 +161,7 @@ OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ add_gc_prefix.c README.solaris2 README.sgi README.hp README.uts \ win32_threads.c NT_THREADS_MAKEFILE gc.mak README.dj Makefile.dj \ - README.alpha README.linux version.h Makefile.DLLs gc_watcom.asm \ + README.alpha README.linux version.h Makefile.DLLs \ WCC_MAKEFILE CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \ @@ -164,12 +185,12 @@ SPECIALCFLAGS = all: gc.a gctest -pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h config.h mach_dep.o $(SRCS) +pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h gcconfig.h mach_dep.o $(SRCS) make -f PCR-Makefile depend make -f PCR-Makefile $(OBJS) test.o dyn_load.o dyn_load_sunos53.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ - $(srcdir)/config.h $(srcdir)/gc_typed.h Makefile + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h Makefile # The dependency on Makefile is needed. Changing # options such as -DSILENT affects the size of GC_arrays, # invalidating all .o files that rely on gc_priv.h @@ -178,28 +199,19 @@ mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h base_lib gc.a: $(OBJS) dyn_load.o $(UTILS) echo > base_lib - rm -f on_sparc_sunos5 - ./if_mach SPARC SUNOS5 touch on_sparc_sunos5 + rm -f on_sparc_sunos5_1 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_1 ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(OBJS) dyn_load.o - ./if_not_there on_sparc_sunos5 $(AR) ru gc.a $(OBJS) dyn_load.o - ./if_not_there on_sparc_sunos5 $(RANLIB) gc.a || cat /dev/null + ./if_not_there on_sparc_sunos5_1 $(AR) ru gc.a $(OBJS) dyn_load.o + ./if_not_there on_sparc_sunos5_1 $(RANLIB) gc.a || cat /dev/null # ignore ranlib failure; that usually means it doesn't exist, and isn't needed -libgc.a: - make CFLAGS="$(LIBGC_CFLAGS)" clean gc.a gcc_support.o - mv gc.a libgc.a - rm -f on_sparc_sunos5 - ./if_mach SPARC SUNOS5 touch on_sparc_sunos5 - ./if_mach SPARC SUNOS5 $(AR) rus libgc.a gcc_support.o - ./if_not_there on_sparc_sunos5 $(AR) ru libgc.a gcc_support.o - ./if_not_there on_sparc_sunos5 $(RANLIB) libgc.a || cat /dev/null - cords: $(CORD_OBJS) cord/cordtest $(UTILS) - rm -f on_sparc_sunos5 - ./if_mach SPARC SUNOS5 touch on_sparc_sunos5 + rm -f on_sparc_sunos5_3 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_3 ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(CORD_OBJS) - ./if_not_there on_sparc_sunos5 $(AR) ru gc.a $(CORD_OBJS) - ./if_not_there on_sparc_sunos5 $(RANLIB) gc.a || cat /dev/null + ./if_not_there on_sparc_sunos5_3 $(AR) ru gc.a $(CORD_OBJS) + ./if_not_there on_sparc_sunos5_3 $(RANLIB) gc.a || cat /dev/null gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h $(srcdir)/gc.h Makefile $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc @@ -211,11 +223,11 @@ base_lib $(UTILS) ./if_not_there test_cpp $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a `./threadlibs` c++: gc_cpp.o $(srcdir)/gc_cpp.h test_cpp - rm -f on_sparc_sunos5 - ./if_mach SPARC SUNOS5 touch on_sparc_sunos5 + rm -f on_sparc_sunos5_4 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_4 ./if_mach SPARC SUNOS5 $(AR) rus gc.a gc_cpp.o - ./if_not_there on_sparc_sunos5 $(AR) ru gc.a gc_cpp.o - ./if_not_there on_sparc_sunos5 $(RANLIB) gc.a || cat /dev/null + ./if_not_there on_sparc_sunos5_4 $(AR) ru gc.a gc_cpp.o + ./if_not_there on_sparc_sunos5_4 $(RANLIB) gc.a || cat /dev/null ./test_cpp 1 echo > c++ @@ -223,16 +235,24 @@ dyn_load_sunos53.o: dyn_load.c $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ # SunOS5 shared library version of the collector -libgc.so: $(OBJS) dyn_load_sunos53.o - $(CC) -G -o libgc.so $(OBJS) dyn_load_sunos53.o -ldl +sunos5gc.so: $(OBJS) dyn_load_sunos53.o + $(CC) -G -o sunos5gc.so $(OBJS) dyn_load_sunos53.o -ldl + ln sunos5gc.so libgc.so # Alpha/OSF shared library version of the collector libalphagc.so: $(OBJS) ld -shared -o libalphagc.so $(OBJS) dyn_load.o -lc + ln libalphagc.so libgc.so # IRIX shared library version of the collector libirixgc.so: $(OBJS) dyn_load.o - ld -shared -o libirixgc.so $(OBJS) dyn_load.o -lc + ld -shared $(ABI_FLAG) -o libirixgc.so $(OBJS) dyn_load.o -lc + ln libirixgc.so libgc.so + +# Linux shared library version of the collector +liblinuxgc.so: $(OBJS) dyn_load.o + gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o -lo + ln liblinuxgc.so libgc.so mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s $(srcdir)/mips_ultrix_mach_dep.s $(srcdir)/rs6000_mach_dep.s $(UTILS) rm -f mach_dep.o @@ -275,17 +295,17 @@ cord/cordtest: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a $(UTILS) cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(UTILS) rm -f cord/de - ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lucb + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lucb `./threadlibs` ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -ldld ./if_mach RS6000 "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses - ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` ./if_mach ALPHA LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses ./if_not_there cord/de $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) `./threadlibs` -if_mach: $(srcdir)/if_mach.c $(srcdir)/config.h +if_mach: $(srcdir)/if_mach.c $(srcdir)/gcconfig.h $(CC) $(CFLAGS) -o if_mach $(srcdir)/if_mach.c -threadlibs: $(srcdir)/threadlibs.c $(srcdir)/config.h Makefile +threadlibs: $(srcdir)/threadlibs.c $(srcdir)/gcconfig.h Makefile $(CC) $(CFLAGS) -o threadlibs $(srcdir)/threadlibs.c if_not_there: $(srcdir)/if_not_there.c diff --git a/Makefile.dj b/Makefile.dj index 9187f73a..8e0a66b6 100644 --- a/Makefile.dj +++ b/Makefile.dj @@ -108,7 +108,7 @@ CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ sparc_mach_dep.s gc.h gc_typed.h gc_hdrs.h gc_priv.h gc_private.h \ - config.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ + gcconfig.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ threadlibs.c if_mach.c if_not_there.c gc_cpp.cc gc_cpp.h weakpointer.h \ gcc_support.c mips_ultrix_mach_dep.s include/gc_alloc.h gc_alloc.h \ $(CORD_SRCS) @@ -117,7 +117,7 @@ OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \ SCoptions.amiga README.amiga README.win32 cord/README \ cord/gc.h include/gc.h include/gc_typed.h include/cord.h \ - include/ec.h include/private/cord_pos.h include/private/config.h \ + include/ec.h include/private/cord_pos.h include/private/gcconfig.h \ include/private/gc_hdrs.h include/private/gc_priv.h \ include/gc_cpp.h README.rs6000 \ include/weakpointer.h README.QUICK callprocs pc_excludes \ @@ -147,14 +147,14 @@ SPECIALCFLAGS = # not time-critical anyway. # Set SPECIALCFLAGS to -q nodirect_code on Encore. -all: gc.a gctest +all: gc.a gctest$(EXE_SUFFIX) -pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h config.h mach_dep.o $(SRCS) +pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h gcconfig.h mach_dep.o $(SRCS) make -f PCR-Makefile depend make -f PCR-Makefile $(OBJS) test.o dyn_load.o dyn_load_sunos53.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ - $(srcdir)/config.h $(srcdir)/gc_typed.h Makefile + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h Makefile # The dependency on Makefile is needed. Changing # options such as -DSILENT affects the size of GC_arrays, # invalidating all .o files that rely on gc_priv.h @@ -194,19 +194,19 @@ base_lib $(UTILS) -$(RM) test_cpp test_cpp$(EXE_SUFFIX) ./if_mach HP_PA "" $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a -ldld ./if_not_there test_cpp$(EXE_SUFFIX) $(CXXLD) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a + $(RM) test_cpp c++: gc_cpp.o $(srcdir)/gc_cpp.h test_cpp -$(RM) on_sparc_sunos5 $(AR) ru gc.a gc_cpp.o $(RANLIB) gc.a - ./test_cpp 1 + ./test_cpp$(EXE_SUFFIX) 1 echo > c++ dyn_load_sunos53.o: dyn_load.c $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ mach_dep.o: $(srcdir)/mach_dep.c -# $(srcdir)/mips_mach_dep.s $(srcdir)/rs6000_mach_dep.s if_mach if_not_there -$(RM) mach_dep.o $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c @@ -249,17 +249,22 @@ cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) ./if_not_there cord/de$(EXE_SUFFIX) \ $(MV) de$(EXE_SUFFIX) cord/de$(EXE_SUFFIX) -if_mach$(EXE_SUFFIX): $(srcdir)/if_mach.c $(srcdir)/config.h +if_mach$(EXE_SUFFIX): $(srcdir)/if_mach.c $(srcdir)/gcconfig.h $(CC) $(CFLAGS) -o if_mach $(srcdir)/if_mach.c + -$(RM) if_mach -threadlibs$(EXE_SUFFIX): $(srcdir)/threadlibs.c $(srcdir)/config.h Makefile +threadlibs$(EXE_SUFFIX): $(srcdir)/threadlibs.c $(srcdir)/gcconfig.h Makefile $(CC) $(CFLAGS) -o threadlibs $(srcdir)/threadlibs.c + -$(RM) threadlibs if_not_there$(EXE_SUFFIX): $(srcdir)/if_not_there.c $(CC) $(CFLAGS) -o if_not_there $(srcdir)/if_not_there.c + -$(RM) if_not_there clean: - -$(RM) gc.a *.o gctest gctest_dyn_link test_cpp \ + -$(RM) gc.a *.o + -$(RM) *.o + -$(RM) gctest gctest_dyn_link test_cpp \ setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ $(CORD_OBJS) cordtest cord/cordtest de cord/de -$(RM) gctest$(EXE_SUFFIX) gctest_dyn_link$(EXE_SUFFIX) test_cpp$(EXE_SUFFIX) \ @@ -270,6 +275,7 @@ clean: gctest$(EXE_SUFFIX): test.o gc.a -$(RM) gctest$(EXE_SUFFIX) $(CC) $(CFLAGS) -o gctest test.o gc.a + $(RM) gctest # If an optimized setjmp_test generates a segmentation fault, # odds are your compiler is broken. Gctest may still work. @@ -278,6 +284,7 @@ setjmp_test$(EXE_SUFFIX): $(srcdir)/setjmp_t.c $(srcdir)/gc.h \ if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX) -$(RM) setjmp_test$(EXE_SUFFIX) $(CC) $(CFLAGS) -o setjmp_test $(srcdir)/setjmp_t.c + $(RM) setjmp_test test: KandRtest cord/cordtest$(EXE_SUFFIX) ./cord/cordtest$(EXE_SUFFIX) @@ -287,4 +294,3 @@ KandRtest: setjmp_test$(EXE_SUFFIX) gctest$(EXE_SUFFIX) ./setjmp_test$(EXE_SUFFIX) ./gctest$(EXE_SUFFIX) - diff --git a/PCR-Makefile b/PCR-Makefile index 7b80637f..1eae3672 100644 --- a/PCR-Makefile +++ b/PCR-Makefile @@ -59,7 +59,7 @@ mach_dep.o: mach_dep.c mips_mach_dep.s rs6000_mach_dep.s if_mach if_not_there ./if_mach SPARC SUNOS5 as -o mach_dep.o sparc_mach_dep.s ./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) mach_dep.c -if_mach: if_mach.c config.h +if_mach: if_mach.c gcconfig.h $(CC) $(CFLAGS) -o if_mach if_mach.c if_not_there: if_not_there.c @@ -1,6 +1,6 @@ Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. -Copyright (c) 1996 by Silicon Graphics. All rights reserved. +Copyright (c) 1996-1998 by Silicon Graphics. 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 +11,11 @@ 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 version 4.12 of a conservative garbage collector for C and C++. +This is version 4.13 of a conservative garbage collector for C and C++. + +You might find a more recent version of this at + +http://reality.sgi.com/boehm/gc.html HISTORY - @@ -144,18 +148,19 @@ See README.debugging for details. seen by the garbage collector. Thus objects pointed to only from such a region may be prematurely deallocated. It is thus suggested that the standard "malloc" be used only for memory regions, such as I/O buffers, that -are guaranteed not to contain pointers. Pointers in C language automatic, -static, or register variables, are correctly recognized. (Note that -GC_malloc_uncollectable has semantics similar to standard malloc, -but allocates objects that are traced by the collector.) +are guaranteed not to contain pointers to garbage collectable memory. +Pointers in C language automatic, static, or register variables, +are correctly recognized. (Note that GC_malloc_uncollectable has semantics +similar to standard malloc, but allocates objects that are traced by the +collector.) The collector does not always know how to find pointers in data areas that are associated with dynamic libraries. This is easy to remedy IF you know how to find those data areas on your operating system (see GC_add_roots). Code for doing this under SunOS, IRIX 5.X and 6.X, -HP/UX, Alpha OSF/1 and win32 is included and used by default. (See -README.win32 for win32 details.) On other systems pointers from dynamic library -data areas may not be considered by the collector. +HP/UX, Alpha OSF/1, Linux, and win32 is included and used by default. (See +README.win32 for win32 details.) On other systems pointers from dynamic +library data areas may not be considered by the collector. Note that the garbage collector does not need to be informed of shared read-only data. However if the shared library mechanism can introduce @@ -251,7 +256,7 @@ or win16 is hard. For machines not already mentioned, or for nonstandard compilers, the following are likely to require change: -1. The parameters in config.h. +1. The parameters in gcconfig.h. The parameters that will usually require adjustment are STACKBOTTOM, ALIGNMENT and DATASTART. Setjmp_test prints its guesses of the first two. @@ -268,7 +273,7 @@ following are likely to require change: On some machines, it is difficult to obtain such a value that is valid across a variety of MMUs, OS releases, etc. A number of alternatives exist for using the collector in spite of this. See the - discussion in config.h immediately preceding the various + discussion in gcconfig.h immediately preceding the various definitions of STACKBOTTOM. 2. mach_dep.c. @@ -305,7 +310,7 @@ following are likely to require change: in gc_priv.h will need to be suitably redefined. The incremental collector requires page dirty information, which is acquired through routines defined in os_dep.c. Unless directed - otherwise by config.h, these are implemented as stubs that simply + otherwise by gcconfig.h, these are implemented as stubs that simply treat all pages as dirty. (This of course makes the incremental collector much less useful.) @@ -317,7 +322,7 @@ following are likely to require change: For a different version of UN*X or different machines using the Motorola 68000, Vax, SPARC, 80386, NS 32000, PC/RT, or MIPS architecture, -it should frequently suffice to change definitions in config.h. +it should frequently suffice to change definitions in gcconfig.h. THE C INTERFACE TO THE ALLOCATOR @@ -1324,6 +1329,87 @@ Since 4.11: On the other hand, it seems to be needed often enough that it's worth adding as a standard facility. +Since 4.12: + - Fixed a crucial bug in the Watcom port. There was a redundant decl + of GC_push_one in gc_priv.h. + - Added FINALIZE_ON_DEMAND. + - Fixed some pre-ANSI cc problems in test.c. + - Removed getpagesize() use for Solaris. It seems to be missing in one + or two versions. + - Fixed bool handling for SPARCCompiler version 4.2. + - Fixed some files in include that had gotten unlinked from the main + copy. + - Some RS/6000 fixes (missing casts). Thanks to Toralf Foerster. + - Fixed several problems in GC_debug_realloc, affecting mostly the + FIND_LEAK case. + - GC_exclude_static_roots contained a buggy unsigned comparison to + terminate a loop. (Thanks to Wilson Ho.) + - CORD_str failed if the substring occurred at the last possible position. + (Only affects cord users.) + - Fixed Linux code to deal with RedHat 5.0 and integrated Peter Bigot's + os_dep.c code for dealing with various Linux versions. + - Added workaround for Irix pthreads sigaction bug and possible signal + misdirection problems. +Since alpha1: + - Changed RS6000 STACKBOTTOM. + - Integrated Patrick Beard's Mac changes. + - Alpha1 didn't compile on Irix m.n, m < 6. + - Replaced Makefile.dj with a new one from Gary Leavens. + - Added Andrew Stitcher's changes to support SCO OpenServer. + - Added PRINT_BLACK_LIST, to allow debugging of high densities of false + pointers. + - Added code to debug allocator to keep track of return address + in GC_malloc caller, thus giving a bit more context. + - Changed default behavior of large block allocator to more + aggressively avoid fragmentation. This is likely to slow down the + collector when it succeeds at reducing space cost. + - Integrated Fergus Henderson's CYGWIN32 changes. They are untested, + but needed for newer versions. + - USE_MMAP had some serious bugs. This caused the collector to fail + consistently on Solaris with -DSMALL_CONFIG. + - Added Linux threads support, thanks largely to Fergus Henderson. +Since alpha2: + - Fixed more Linux threads problems. + - Changed default GC_free_space_divisor to 3 with new large block allocation. + (Thanks to Matthew Flatt for some measurements that suggest the old + value sometimes favors space too much over time.) + - More CYGWIN32 fixes. + - Integrated Tyson-Dowd's Linux-M68K port. + - Minor HP PA and DEC UNIX fixes from Fergus Henderson. + - Integrated Christoffe Raffali's Linux-SPARC changes. + - Allowed for one more GC fixup iteration after a full GC in incremental + mode. Some quick measurements suggested that this significantly + reduces pause times even with smaller GC_RATE values. + - Moved some more GC data structures into GC_arrays. This decreases + pause times and GC overhead, but makes debugging slightly less convenient. + - Fixed namespace pollution problem ("excl_table"). + - Made GC_incremental a constant for -DSMALL_CONFIG, hopefully shrinking + that slightly. + - Added some win32 threads fixes. + - Integrated Ivan Demakov and David Stes' Watcom fixes. + - Various other minor fixes contributed by many people. + - Renamed config.h to gcconfig.h, since config.h tends to be used for + many other things. + - Integrated Matthew Flatt's support for 68K MacOS "far globals". + - Fixed up some of the dynamic library Makefile targets for consistency + across platforms. + - Fixed a USE_MMAP typo that caused out-of-memory handling to fail + on Solaris. + - Added code to test.c to test thread creation a bit more. + - Integrated GC_win32_free_heap, as suggested by Ivan Demakov. + - Fixed Solaris 2.7 stack base finding problem. (This may actually + have been done in an earlier alpha release.) +Since alpha3: + - Fixed MSWIN32 recognition test, which interfered with cygwin. + - Removed unnecessary gc_watcom.asm from distribution. Removed + some obsolete README.win32 text. + - Added Alpha Linux incremental GC support. (Thanks to Philipp Tomsich + for code for retrieving the fault address in a signal handler.) + Changed Linux signal handler context argument to be a pointer. + - Took care of some new warnings generated by the 7.3 SGI compiler. + - Integrated Phillip Musumeci's FreeBSD/ELF fixes. + - -DIRIX_THREADS was broken with the -o32 ABI (typo in gc_priv.h> + To do: - Very large root set sizes (> 16 MB or so) could cause the collector to abort with an unexpected mark stack overflow. (Thanks again to @@ -1336,6 +1422,14 @@ To do: off DYNAMIC_LOADING in the collector as a workaround. It may also be possible to conditionally intercept mmap and use GC_exclude_static_roots. The real fix is to walk rld data structures, which looks possible. - - SGI pthreads and incremental collection don't mix yet. - Integrate MIT and DEC pthreads ports. + - Deal with very uneven black-listing distributions. If all the black listed + blocks reside in the newly allocated heap section, the heuristic for + temporarily ignoring black-listing fails, and the heap grows too much. + (This was observed in only one case, and could be worked around, but ...) + - I've started work on rewriting the large block allocator to use approximate + best fit. There are rare cases in which the current allocator results in + excessive large block fragmentation, even with the 4.13 fixes. This should + also reduce large block allocation time, whcih has become occasionally + noticable in 4.13. @@ -1,3 +1,14 @@ +Patrick Beard's Notes for building GC v4.12 with CodeWarrior Pro 2: +---------------------------------------------------------------------------- +The current build environment for the collector is CodeWarrior Pro 2. +Projects for CodeWarrior Pro 2 (and for quite a few older versions) +are distributed in the file Mac_projects.sit.hqx. The project file +:Mac_projects:gc.prj builds static library versions of the collector. +:Mac_projects:gctest.prj builds the GC test suite. + +Configuring the collector is still done by editing the files +:Mac_files:MacOS_config.h and :Mac_files:MacOS_Test_config.h. + Lars Farm's suggestions on building the collector: ---------------------------------------------------------------------------- Garbage Collection on MacOS - a manual 'MakeFile' @@ -240,7 +251,7 @@ prefix: #include <ansi_prefix.mac.h> #undef NDEBUG -#define ALL_INTERIOR_POINTERS /* for GC_priv.h +#define ALL_INTERIOR_POINTERS /* for GC_priv.h */ ---- ( cut here ) ---- 3) Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc'. @@ -309,7 +320,7 @@ Very few. Just one tiny in the GC, not strictly needed. alloc dummy_to_fool_the_compiler_into_doing_things_it_currently_cant_handle; ------------ -- config.h +- config.h [now gcconfig.h] __MWERKS__ does not have to mean MACOS. You can use Codewarrior to build a Win32 or BeOS library and soon a Rhapsody library. You may have to change that #if... diff --git a/README.alpha b/README.alpha index b187fd5b..213a13e3 100644 --- a/README.alpha +++ b/README.alpha @@ -5,6 +5,13 @@ Incremental gc not yet supported under Linux because signal handler for SIGSEGV can't get a hold of fault address. Dynamic library support is also missing from Linux/alpha, probably for no good reason. +Currently there is no thread support in the standard distribution. There +exists a separate port to DEC Unix pthreads. It should be possible to +port the X86 Linux threads support to Alpha without much trouble. + +If you get asssembler errors, be sure to read the first few lines of the +Makefile. + From Philippe Queinnec: System: DEC/Alpha OSF1 v3.2, vendor cc @@ -1,9 +1,11 @@ Dynamic loading support requires that executables be linked with -ldld. The alternative is to build the collector without defining DYNAMIC_LOADING -in config.h and ensuring that all garbage collectable objects are +in gcconfig.h and ensuring that all garbage collectable objects are accessible without considering statically allocated variables in dynamic libraries. The collector should compile with either plain cc or cc -Ae. CC -Aa fails to define _HPUX_SOURCE and thus will not configure the collector correctly. + +There is currently no thread support. diff --git a/README.linux b/README.linux index 9ec4161f..b4f136af 100644 --- a/README.linux +++ b/README.linux @@ -1,7 +1,50 @@ -See README.alpha for Linux on DEC AXP info. This file applies to -Linux/Intel. +See README.alpha for Linux on DEC AXP info. -Incremental GC is supported. +This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K +and PowerPC are also integrated. They should behave similarly, except that +the PowerPC port lacks incremental GC support, and it is unknown to what +extent the Linux threads code is functional. + +Incremental GC is supported on Intel IA32 and M68K. Dynamic libraries are supported on an ELF system. A static executable should be linked with the gcc option "-Wl,-defsym,_DYNAMIC=0". + +The collector appears to work with Linux threads. We have seen +intermittent hangs in sem_wait. So far we have been unable to reproduce +these unless the process was being debugged or traced. Thus it's +possible that the only real issue is that the debugger loses +signals on rare occasions. + +The garbage collector uses SIGPWR and SIGXCPU if it is used with +Linux threads. These should not be touched by the client program. + +To use threads, you need to abide by the following requirements: + +1) You need to use LinuxThreads (which are included in libc6). + + The collector relies on some implementation details of the LinuxThreads + package. It is unlikely that this code will work on other + pthread implementations (in particular it will *not* work with + MIT pthreads). + +2) You must compile the collector with -DLINUX_THREADS and -D_REENTRANT + specified in the Makefile. + +3) Every file that makes thread calls should define LINUX_THREADS and + _REENTRANT and then include gc.h. Gc.h redefines some of the + pthread primitives as macros which also provide the collector with + information it requires. + +4) Currently dlopen() is probably not safe. The collector must traverse + the list of libraries maintained by the runtime loader. That can + probably be an inconsistent state when a thread calling the loader is + is stopped for GC. (It's possible that this is fixable in the + same way it is handled for SOLARIS_THREADS, with GC_dlopen.) + +5) The combination of LINUX_THREADS, REDIRECT_MALLOC, and incremental + collection fails in seemingly random places. This hasn't been tracked + down yet, but is perhaps not completely astonishing. The thread package + uses malloc, and thus can presumably get SIGSEGVs while inside the + package. There is no real guarantee that signals are handled properly + at that point. diff --git a/README.rs6000 b/README.rs6000 index 0444ac4c..f5630b20 100644 --- a/README.rs6000 +++ b/README.rs6000 @@ -4,3 +4,6 @@ startup. The supplied value sometimes causes failure under AIX 4.1, though it appears to work under 3.X. HEURISTIC2 seems to work under 4.1, but involves a substantial performance penalty, and will fail if there is no limit on stack size. + +There is no thread support. (I assume recent versions of AIX provide +pthreads? I no longer have access to a machine ...) @@ -11,8 +11,7 @@ version of malloc is linked in. Sproc threads are not supported in this version, though there may exist other ports. -Pthreads are somewhat supported without incremental collection. This -requires that: +Pthreads support is provided. This requires that: 1) You compile the collector with -DIRIX_THREADS specified in the Makefile. @@ -27,4 +26,16 @@ will run on other pthreads platforms. But please tell me if it does.) include gc.h. Gc.h redefines some of the pthread primitives as macros which also provide the collector with information it requires. -4) For the time being, you should not use dlopen. +4) pthread_cond_wait and pthread_cond_timed_wait should be prepared for +premature wakeups. (I believe the pthreads and realted standards require this +anyway. Irix pthreads often terminate a wait if a signal arrives. +The garbage collector uses signals to stop threads.) + +5) It is expensive to stop a thread waiting in IO at the time the request is +initiated. Applications with many such threads may not exhibit acceptable +performance with the collector. (Increasing the heap size may help.) + +6) The collector should not be compiled with -DREDIRECT_MALLOC. This +confuses some library calls made by the pthreads implementation, which +expect the standard malloc. + diff --git a/README.solaris2 b/README.solaris2 index 1edd6e66..e5935131 100644 --- a/README.solaris2 +++ b/README.solaris2 @@ -1,7 +1,7 @@ The collector supports both incremental collection and threads under Solaris 2. The incremental collector normally retrieves page dirty information through the appropriate /proc calls. But it can also be configured -(by defining MPROTECT_VDB instead of PROC_VDB in config.h) to use mprotect +(by defining MPROTECT_VDB instead of PROC_VDB in gcconfig.h) to use mprotect and signals. This may result in shorter pause times, but it is no longer safe to issue arbitrary system calls that write to the heap. @@ -14,7 +14,7 @@ and sbrk() only when you know that malloc() definitely will not be used by any library routine." This doesn't make a lot of sense to me, since there seems to be no documentation as to which routines can transitively call malloc. Nonetheless, under Solaris2, the collector now (since 4.12) allocates -memory using mmap by default. (It defines USE_MMAP in config.h.) +memory using mmap by default. (It defines USE_MMAP in gcconfig.h.) You may want to reverse this decisions if you use -DREDIRECT_MALLOC=... @@ -50,6 +50,16 @@ GC_malloc, it is necessary to call GC_thr_init explicitly before forking the first thread. (This avoids a deadlock arising from calling GC_thr_init with the allocation lock held.) +It appears that there is a problem in using gc_cpp.h in conjunction with +Solaris threads and Sun's C++ runtime. Apparently the overloaded new operator +is invoked by some iostream initialization code before threads are correctly +initialized. As a result, call to thr_self() in garbage collector +initialization segfaults. Currently the only known workaround is to not +invoke the garbage collector from a user defined global operator new, or to +have it invoke the garbage-collector's allocators only after main has started. +(Note that the latter requires a moderately expensive test in operator +delete.) + Hans-J. Boehm (The above contains my personal opinions, which are probably not shared by anyone else.) diff --git a/README.win32 b/README.win32 index 95bf50fe..d78816b5 100644 --- a/README.win32 +++ b/README.win32 @@ -23,7 +23,11 @@ the two systems, and under different versions of win32s.) The collector test program "gctest" is linked as a GUI application, but does not open any windows. Its output appears in the file "gc.log". It may be started from the file manager. The hour glass -cursor will appear as long as it's running. +cursor will appear as long as it's running. If it is started from the +command line, it will usually run in the background. Wait a few +minutes (a few seconds on a modern machine) before you check the output. +You should see either a failure indication or a "Collector appears to +work" message. The cord test program has not been ported (but should port easily). A toy editor (cord/de.exe) based on cords (heavyweight @@ -43,7 +47,10 @@ to be i386.) For GNU-win32, use the regular makefile, possibly after uncommenting the line "include Makefile.DLLs". The latter should be necessary only -if you want to package the collector as a DLL. +if you want to package the collector as a DLL. The GNU-win32 port is +believed to work only for b18, not b19, probably dues to linker changes +in b19. This is probably fixable with a different definition of +DATASTART and DATAEND in gcconfig.h. For Borland tools, use BCC_MAKEFILE. Note that Borland's compiler defaults to 1 byte alignment in structures (-a1), @@ -53,7 +60,7 @@ LEAST 4 BYTE ALIGNMENT. Thus the BORLAND DEFAULT MUST BE OVERRIDDEN. (In my opinion, it should usually be anyway. I expect that -a1 introduces major performance penalties on a 486 or Pentium.) Note that this changes structure layouts. (As a last -resort, config.h can be changed to allow 1 byte alignment. But +resort, gcconfig.h can be changed to allow 1 byte alignment. But this has significant negative performance implications.) The Makefile is set up to assume Borland 4.5. If you have another version, change the line near the top. By default, it does not @@ -94,67 +101,49 @@ test with VC++ from the command line, use nmake /F ".\gc.mak" CFG="gctest - Win32 Release" This requires that the subdirectory gctest\Release exist. +The test program and DLL will reside in the Release directory. This version relies on the collector residing in a dll. This version currently supports incremental collection only if it is enabled before any additional threads are created. -It is known to not be completely solid. At a minimum it can deadlock -if a thread starts in the middle of an allocation. There may be -other problems. If you need solid support for win32 threads, you -check with Geodesic Systems. I haven't tried it, but they claim -to support it. +Version 4.13 attempts to fix some of the earlier problems, but there +may be other issues. If you need solid support for win32 threads, you +might check with Geodesic Systems. Their collector must be licensed, +but they have invested far more time in win32-specific issues. Hans Ivan V. Demakov's README for the Watcom port: -[ He points out in a later message that there may be a problem compiling - under Windows-3.11 for Windows NT. ] +The collector has been compiled with Watcom C 10.6 and 11.0. +It runs under win32, win32s, and even under msdos with dos4gw +dos-extender. It should also run under OS/2, though this isn't +tested. Under win32 the collector can be built either as dll +or as static library. -Watcom C/C++ 10.5, 10.6, 11.0 tested. +Note that all compilations were done under Windows 95 or NT. +For unknown reason compiling under Windows 3.11 for NT (one +attempt has been made) leads to broken executables. -The collector runs on WIN32 and DOS4GW dos-extender with both -stack and register based calling conventions (options -5r and -5s). -Incremental collection not supported. +Incremental collection is not supported. -OS/2 not tested, but should work (only some #ifdef's added for OS/2 port). +cord is not ported. -cord not ported. Watcom C fails to compile it, from first attempt. -Since I don't use it, I don't try to fix it. +Before compiling you may need to edit WCC_MAKEFILE to set target +platform, library type (dynamic or static), calling conventions, and +optimization options. -cpp_test succeeds, but not compiled automaticaly with WCC_MAKEFILE. +To compile the collector and testing programs use the command: + wmake -f WCC_MAKEFILE +All programs using gc should be compiled with 4-byte alignment. +For further explanations on this see comments about Borland. -My changes: +If gc compiled as dll, the macro ``GC_DLL'' should be defined before +including "gc.h" (for example, with -DGC_DLL compiler option). It's +important, otherwise resulting programs will not run. - * config.h Added definitions for Watcom C/C++. - Undefined MPROTECT_VDB for Watcom C/C++ MSWIN32, - I don't have idea why it not work. - - * gc.h Explicitly declared GC_noop. This prevents - program crash, compiled with -5r option. - - * gc_priv.h Changed declaration for GC_push_one to make - compiler happy. - Added GC_dos4gw_get_mem declaration and - GET_MEM uses it in DOS4GW environment. - - * os_dep.c Added __WATCOMC__ and DOS4GW #ifdef's. - Added GC_dos4gw_get_mem. - - * mach_dep.c For Watcom used setjmp method of marking registers. - - * WCC_MAKEFILE New file. Makefile for Watcom C/C++. - - * gc_watcom.asm New file. Some functions for DOS4GW. - This functions may (probably) be done in C, - but I can't figure out how do this for all - possible options of compiler. - - * README.watcom This file. - - - Ivan Demakov (email: dem@tgrad.nsk.su) +Ivan Demakov (email: ivan@tgrad.nsk.su) diff --git a/SMakefile.amiga b/SMakefile.amiga index b1aa340e..e9602c08 100644 --- a/SMakefile.amiga +++ b/SMakefile.amiga @@ -1,6 +1,6 @@ OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.o -INC= gc_private.h gc_hdrs.h gc.h config.h +INC= gc_private.h gc_hdrs.h gc.h gcconfig.h all: gctest setjmp_t diff --git a/WCC_MAKEFILE b/WCC_MAKEFILE index cc0ef136..087ff6a3 100644 --- a/WCC_MAKEFILE +++ b/WCC_MAKEFILE @@ -1,95 +1,130 @@ -# Makefile for Watcom C/C++ 10.5, 10.6, 11.0 on NT, OS2 and DOS4GW . -# May work with Watcom 10.0 . -# +# Makefile for Watcom C/C++ 10.5, 10.6, 11.0 on NT, OS2 and DOS4GW. +# May work with Watcom 10.0. -# -# Uncoment one of line for cross compiling +# Uncoment one of the lines below for cross compilation. +SYSTEM=MSWIN32 #SYSTEM=DOS4GW -#SYSTEM=MSWIN32 #SYSTEM=OS2 -!ifndef SYSTEM +# The collector can be built either as dynamic or as static library. +# Select the library type you need. +#MAKE_AS_DLL=1 +MAKE_AS_LIB=1 -!ifdef __MSDOS__ -SYSTEM=DOS4GW -!endif +# Select calling conventions. +# Possible choices are r and s. +CALLING=s -!ifdef __NT__ -SYSTEM=MSWIN32 -!endif +# Select target CPU. +# Possible choices are 3, 4, 5, and 6. +# The last choice available only since version 11.0. +CPU=5 -!ifdef __OS2__ -SYSTEM=OS2 -!endif +# Set optimization options. +# Watcom before 11.0 does not support option "-oh". +OPTIM=-oneatx -s +#OPTIM=-ohneatx -s -D_SYSTEM= +DEFS=-DALL_INTERIOR_POINTERS -DSILENT -DNO_SIGNALS #-DSMALL_CONFIG #-DGC_DEBUG -!else -D_SYSTEM=-D$(SYSTEM) +##### +!ifndef SYSTEM +!ifdef __MSDOS__ +SYSTEM=DOS4GW +!else ifdef __NT__ +SYSTEM=MSWIN32 +!else ifdef __OS2__ +SYSTEM=OS2 +!else +SYSTEM=Unknown +!endif !endif !define $(SYSTEM) +!ifdef DOS4GW +SYSFLAG=-DDOS4GW -bt=dos +!else ifdef MSWIN32 +SYSFLAG=-DMSWIN32 -bt=nt +!else ifdef OS2 +SYSFLAG=-DOS2 -bt=os2 +!else +!error undefined or unsupported target platform: $(SYSTEM) +!endif +!ifdef MAKE_AS_DLL +DLLFLAG=-bd -DGC_DLL +TEST_DLLFLAG=-DGC_DLL +!else ifdef MAKE_AS_LIB +DLLFLAG= +TEST_DLLFLAG= +!else +!error Either MAKE_AS_LIB or MAKE_AS_DLL should be defined +!endif CC=wcc386 CXX=wpp386 -AS=wasm - - -# Watcom before 11.0 not support option -oh -# Remove it if you get error -OPTIM=-oneatxh -s -CALLING=-5s - -DEFS=-DALL_INTERIOR_POINTERS -DSILENT #-DSMALL_CONFIG #-DGC_DEBUG - -# ! -DUSE_GENERIC required ! -CFLAGS=$(OPTIM) -zp4 $(CALLING) -zc -DUSE_GENERIC $(D_SYSTEM) $(DEFS) +# -DUSE_GENERIC is required ! +CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(DLLFLAG) -DGC_BUILD -DUSE_GENERIC $(DEFS) CXXFLAGS= $(CFLAGS) -ASFLAGS=$(CALLING) +TEST_CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(TEST_DLLFLAG) $(DEFS) +TEST_CXXFLAGS= $(TEST_CFLAGS) OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj & mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & obj_map.obj blacklst.obj finalize.obj new_hblk.obj & dbg_mlc.obj malloc.obj stubborn.obj dyn_load.obj & - typd_mlc.obj ptr_chck.obj gc_cpp.obj mallocx.obj + typd_mlc.obj ptr_chck.obj mallocx.obj +all: gc.lib gctest.exe test_cpp.exe -all: gc.lib gctest.exe +!ifdef MAKE_AS_DLL -# this file required for DOS4GW only -gc_watcom.obj: gc_watcom.asm WCC_MAKEFILE - $(AS) $(ASFLAGS) gc_watcom.asm +gc.lib: gc.dll gc_cpp.obj + *wlib -b -c -n -p=512 $@ +gc.dll +gc_cpp.obj +gc.dll: $(OBJS) .AUTODEPEND + @%create $*.lnk !ifdef DOS4GW -gc.lib: $(OBJS) gc_watcom.obj - @%create $*.lb1 - @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' - @@%append $*.lb1 +'gc_watcom.obj' - *wlib -b -c -n -p=512 $@ @$*.lb1 + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk name $* + @for %i in ($(OBJS)) do @%append $*.lnk file '%i' +!ifeq CALLING s + @%append $*.lnk export GC_is_marked + @%append $*.lnk export GC_incr_words_allocd + @%append $*.lnk export GC_incr_mem_freed + @%append $*.lnk export GC_generic_malloc_words_small +!else + @%append $*.lnk export GC_is_marked_ + @%append $*.lnk export GC_incr_words_allocd_ + @%append $*.lnk export GC_incr_mem_freed_ + @%append $*.lnk export GC_generic_malloc_words_small_ +!endif + *wlink @$*.lnk !else -gc.lib: $(OBJS) +gc.lib: $(OBJS) gc_cpp.obj @%create $*.lb1 @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' + @%append $*.lb1 +'gc_cpp.obj' *wlib -b -c -n -p=512 $@ @$*.lb1 -!endif +!endif -test.obj: test.c - $(CC) $(CFLAGS) $*.c gctest.exe: test.obj gc.lib %create $*.lnk !ifdef DOS4GW @%append $*.lnk sys dos4g -!endif -!ifdef MSWIN32 +!else ifdef MSWIN32 @%append $*.lnk sys nt -!endif -!ifdef OS2 +!else ifdef OS2 @%append $*.lnk sys os2v2 !endif @%append $*.lnk op case @@ -97,8 +132,47 @@ gctest.exe: test.obj gc.lib @%append $*.lnk name $* @%append $*.lnk file test.obj @%append $*.lnk library gc.lib +!ifdef MAKE_AS_DLL +!ifeq CALLING s + @%append $*.lnk import GC_is_marked gc +!else + @%append $*.lnk import GC_is_marked_ gc +!endif +!endif + *wlink @$*.lnk +test_cpp.exe: test_cpp.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test_cpp.obj + @%append $*.lnk library gc.lib +!ifdef MAKE_AS_DLL +!ifeq CALLING s + @%append $*.lnk import GC_incr_words_allocd gc + @%append $*.lnk import GC_incr_mem_freed gc + @%append $*.lnk import GC_generic_malloc_words_small gc +!else + @%append $*.lnk import GC_incr_words_allocd_ gc + @%append $*.lnk import GC_incr_mem_freed_ gc + @%append $*.lnk import GC_generic_malloc_words_small_ gc +!endif +!endif *wlink @$*.lnk +gc_cpp.obj: gc_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc +test.obj: test.c .AUTODEPEND + $(CC) $(TEST_CFLAGS) $*.c +test_cpp.obj: test_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc .c.obj: .AUTODEPEND @@ -107,9 +181,6 @@ gctest.exe: test.obj gc.lib .cc.obj: .AUTODEPEND $(CXX) $(CXXFLAGS) $*.cc -.cpp.obj: .AUTODEPEND - $(CXX) $(CXXFLAGS) $*.cpp - clean : .SYMBOLIC @if exist *.obj del *.obj @if exist *.map del *.map @@ -121,3 +192,5 @@ clean : .SYMBOLIC @if exist *.lst del *.lst @if exist *.exe del *.exe @if exist *.log del *.log + @if exist *.lib del *.lib + @if exist *.dll del *.dll @@ -1,6 +1,7 @@ /* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998 by Silicon Graphics. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -72,7 +73,7 @@ void GC_print_hblkfreelist() /* Initialize hdr for a block containing the indicated size and */ /* kind of objects. */ /* Return FALSE on failure. */ -static bool setup_header(hhdr, sz, kind, flags) +static GC_bool setup_header(hhdr, sz, kind, flags) register hdr * hhdr; word sz; /* object size in words */ int kind; @@ -99,6 +100,14 @@ unsigned char flags; return(TRUE); } +#ifdef EXACT_FIRST +# define LAST_TRIP 2 +#else +# define LAST_TRIP 1 +#endif + +word GC_max_hblk_size = HBLKSIZE; + /* * Allocate (and return pointer to) a heap block * for objects of size sz words. @@ -123,9 +132,11 @@ unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ register hdr * phdr; /* Header corr. to prevhbp */ signed_word size_needed; /* number of bytes in requested objects */ signed_word size_avail; /* bytes available in this block */ - bool first_time = TRUE; + int trip_count = 0; size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); + if ((word)size_needed > GC_max_hblk_size) + GC_max_hblk_size = size_needed; /* search for a big enough block in free list */ hbp = GC_savhbp; @@ -137,16 +148,26 @@ unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ hbp = (prevhbp == 0? GC_hblkfreelist : phdr->hb_next); hhdr = HDR(hbp); - if( prevhbp == GC_savhbp && !first_time) { - return(0); + if( prevhbp == GC_savhbp) { + if (trip_count == LAST_TRIP) return(0); + ++trip_count; } - first_time = FALSE; - if( hbp == 0 ) continue; size_avail = hhdr->hb_sz; +# ifdef EXACT_FIRST + if (trip_count <= 1 && size_avail != size_needed) continue; +# endif if (size_avail < size_needed) continue; +# ifdef PRESERVE_LAST + if (size_avail != size_needed + && !GC_incremental + && (word)size_needed <= GC_max_hblk_size/2 + && GC_in_last_heap_sect(hbp) && GC_should_collect()) { + continue; + } +# endif /* If the next heap block is obviously better, go on. */ /* This prevents us from disassembling a single large block */ /* to get tiny blocks. */ @@ -240,7 +261,7 @@ unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ if (GC_savhbp == hbp) GC_savhbp = prevhbp; hbp = prevhbp; hhdr = phdr; - if (hbp == GC_savhbp) first_time = TRUE; + if (hbp == GC_savhbp) --trip_count; } # endif } @@ -280,6 +301,7 @@ unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ /* Notify virtual dirty bit implementation that we are about to write. */ GC_write_hint(thishbp); + /* This should deal better with large blocks. */ /* Add it to map of valid blocks */ if (!GC_install_counts(thishbp, (word)size_needed)) return(0); @@ -1,6 +1,7 @@ /* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998 by Silicon Graphics. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -12,7 +13,6 @@ * modified is included with the above copyright notice. * */ -/* Boehm, February 16, 1996 2:26 pm PST */ # include "gc_priv.h" @@ -58,7 +58,9 @@ word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ word GC_gc_no = 0; -int GC_incremental = 0; /* By default, stop the world. */ +#ifndef SMALL_CONFIG + int GC_incremental = 0; /* By default, stop the world. */ +#endif int GC_full_freq = 4; /* Every 5th collection is a full */ /* collection. */ @@ -66,7 +68,7 @@ int GC_full_freq = 4; /* Every 5th collection is a full */ char * GC_copyright[] = {"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", "Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", -"Copyright (c) 1996-1997 by Silicon Graphics. All rights reserved. ", +"Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. ", "THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY", " EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.", "See source code for details." }; @@ -78,18 +80,25 @@ char * GC_copyright[] = extern signed_word GC_mem_found; /* Number of reclaimed longwords */ /* after garbage collection */ -bool GC_dont_expand = 0; +GC_bool GC_dont_expand = 0; word GC_free_space_divisor = 4; -extern bool GC_collection_in_progress(); +extern GC_bool GC_collection_in_progress(); int GC_never_stop_func GC_PROTO((void)) { return(0); } -CLOCK_TYPE GC_start_time; +CLOCK_TYPE GC_start_time; /* Time at which we stopped world. */ + /* used only in GC_timeout_stop_func. */ -int GC_timeout_stop_func GC_PROTO((void)) -{ +int GC_n_attempts = 0; /* Number of attempts at finishing */ + /* collection within TIME_LIMIT */ + +#ifdef SMALL_CONFIG +# define GC_timeout_stop_func GC_never_stop_func +#else + int GC_timeout_stop_func GC_PROTO((void)) + { CLOCK_TYPE current_time; static unsigned count = 0; unsigned long time_diff; @@ -100,12 +109,14 @@ int GC_timeout_stop_func GC_PROTO((void)) if (time_diff >= TIME_LIMIT) { # ifdef PRINTSTATS GC_printf0("Abandoning stopped marking after "); - GC_printf1("%lu msecs\n", (unsigned long)time_diff); + GC_printf1("%lu msecs", (unsigned long)time_diff); + GC_printf1("(attempt %d)\n", (unsigned long) GC_n_attempts); # endif return(1); } return(0); -} + } +#endif /* !SMALL_CONFIG */ /* Return the minimum number of words that must be allocated between */ /* collections to amortize the collection cost. */ @@ -189,7 +200,7 @@ void GC_clear_a_few_frames() } /* Have we allocated enough to amortize a collection? */ -bool GC_should_collect() +GC_bool GC_should_collect() { return(GC_adj_words_allocd() >= min_words_allocd()); } @@ -210,6 +221,8 @@ void GC_notify_full_gc() void GC_maybe_gc() { static int n_partial_gcs = 0; + GC_bool is_full_gc = FALSE; + if (GC_should_collect()) { if (!GC_incremental) { GC_notify_full_gc(); @@ -228,6 +241,7 @@ void GC_maybe_gc() GC_clear_marks(); n_partial_gcs = 0; GC_notify_full_gc(); + is_full_gc = TRUE; } else { n_partial_gcs++; } @@ -240,7 +254,12 @@ void GC_maybe_gc() GC_save_callers(GC_last_stack); # endif GC_finish_collection(); - } + } else { + if (!is_full_gc) { + /* Count this as the first attempt */ + GC_n_attempts++; + } + } } } @@ -249,7 +268,7 @@ void GC_maybe_gc() * Stop the world garbage collection. Assumes lock held, signals disabled. * If stop_func is not GC_never_stop_func, then abort if stop_func returns TRUE. */ -bool GC_try_to_collect_inner(stop_func) +GC_bool GC_try_to_collect_inner(stop_func) GC_stop_func stop_func; { if (GC_collection_in_progress()) { @@ -302,13 +321,20 @@ GC_stop_func stop_func; /* * Perform n units of garbage collection work. A unit is intended to touch - * roughly a GC_RATE pages. Every once in a while, we do more than that. + * roughly GC_RATE pages. Every once in a while, we do more than that. + * This needa to be a fairly large number with our current incremental + * GC strategy, since otherwise we allocate too much during GC, and the + * cleanup gets expensive. */ -# define GC_RATE 8 +# define GC_RATE 10 +# define MAX_PRIOR_ATTEMPTS 1 + /* Maximum number of prior attempts at world stop marking */ + /* A value of 1 means that we finish the seconf time, no matter */ + /* how long it takes. Doesn't count the initial root scan */ + /* for a full GC. */ int GC_deficit = 0; /* The number of extra calls to GC_mark_some */ /* that we have made. */ - /* Negative values are equivalent to 0. */ void GC_collect_a_little_inner(n) int n; @@ -322,12 +348,21 @@ int n; # ifdef SAVE_CALL_CHAIN GC_save_callers(GC_last_stack); # endif - (void) GC_stopped_mark(GC_never_stop_func); + if (GC_n_attempts < MAX_PRIOR_ATTEMPTS) { + GET_TIME(GC_start_time); + if (!GC_stopped_mark(GC_timeout_stop_func)) { + GC_n_attempts++; + break; + } + } else { + (void)GC_stopped_mark(GC_never_stop_func); + } GC_finish_collection(); break; } } if (GC_deficit > 0) GC_deficit -= GC_RATE*n; + if (GC_deficit < 0) GC_deficit = 0; } else { GC_maybe_gc(); } @@ -355,7 +390,7 @@ int GC_collect_a_little GC_PROTO(()) * Otherwise we may fail and return FALSE if this takes too long. * Increment GC_gc_no if we succeed. */ -bool GC_stopped_mark(stop_func) +GC_bool GC_stopped_mark(stop_func) GC_stop_func stop_func; { register int i; @@ -522,6 +557,7 @@ void GC_finish_collection() (unsigned long)WORDS_TO_BYTES(GC_composite_in_use)); # endif + GC_n_attempts = 0; /* Reset or increment counters for next cycle */ GC_words_allocd_before_gc += GC_words_allocd; GC_non_gc_bytes_at_gc = GC_non_gc_bytes; @@ -548,7 +584,7 @@ void GC_finish_collection() int result; DCL_LOCK_STATE; - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); ENTER_GC(); @@ -559,7 +595,7 @@ void GC_finish_collection() EXIT_GC(); UNLOCK(); ENABLE_SIGNALS(); - if(result) GC_invoke_finalizers(); + if(result) GC_INVOKE_FINALIZERS(); return(result); } @@ -610,6 +646,27 @@ word bytes; } } +#ifdef PRESERVE_LAST + +GC_bool GC_protect_last_block = FALSE; + +GC_bool GC_in_last_heap_sect(p) +ptr_t p; +{ + struct HeapSect * last_heap_sect; + ptr_t start; + ptr_t end; + + if (!GC_protect_last_block) return FALSE; + last_heap_sect = &(GC_heap_sects[GC_n_heap_sects-1]); + start = last_heap_sect -> hs_start; + if (p < start) return FALSE; + end = start + last_heap_sect -> hs_bytes; + if (p >= end) return FALSE; + return TRUE; +} +#endif + # if !defined(NO_DEBUGGING) void GC_print_heap_sects() { @@ -667,7 +724,7 @@ GC_word GC_max_retries = 0; * Tiny values of n are rounded up. * Returns FALSE on failure. */ -bool GC_expand_hp_inner(n) +GC_bool GC_expand_hp_inner(n) word n; { word bytes; @@ -740,6 +797,9 @@ word n; LOCK(); if (!GC_is_initialized) GC_init_inner(); result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); +# ifdef PRESERVE_LAST + if (result) GC_protect_last_block = FALSE; +# endif UNLOCK(); ENABLE_SIGNALS(); return(result); @@ -749,9 +809,9 @@ unsigned GC_fail_count = 0; /* How many consecutive GC/expansion failures? */ /* Reset by GC_allochblk. */ -bool GC_collect_or_expand(needed_blocks, ignore_off_page) +GC_bool GC_collect_or_expand(needed_blocks, ignore_off_page) word needed_blocks; -bool ignore_off_page; +GC_bool ignore_off_page; { if (!GC_incremental && !GC_dont_gc && GC_should_collect()) { @@ -786,10 +846,18 @@ bool ignore_off_page; WARN("Out of Memory! Returning NIL!\n", 0); return(FALSE); } - } else if (GC_fail_count) { + } else { # ifdef PRINTSTATS - GC_printf0("Memory available again ...\n"); + if (GC_fail_count) { + GC_printf0("Memory available again ...\n"); + } # endif +# ifdef PRESERVE_LAST + if (needed_blocks > 1) GC_protect_last_block = TRUE; + /* We were forced to expand the heap as the result */ + /* of a large block allocation. Avoid breaking up */ + /* new block into small pieces. */ +# endif } } return(TRUE); @@ -52,6 +52,28 @@ word GC_black_list_spacing = MINHINCR*HBLKSIZE; /* Initial rough guess */ void GC_clear_bl(); +void GC_default_print_heap_obj_proc(p) +ptr_t p; +{ + ptr_t base = GC_base(p); + + GC_err_printf2("start: 0x%lx, appr. length: %ld", base, GC_size(base)); +} + +void (*GC_print_heap_obj)(/* char * s, ptr_t p */) = + GC_default_print_heap_obj_proc; + +void GC_print_source_ptr(ptr_t p) +{ + ptr_t base = GC_base(p); + if (0 == base) { + GC_err_printf0("in root set"); + } else { + GC_err_printf0("in object at "); + (*GC_print_heap_obj)(base); + } +} + void GC_bl_init() { # ifndef ALL_INTERIOR_POINTERS @@ -132,7 +154,12 @@ void GC_unpromote_black_lists() /* P is not a valid pointer reference, but it falls inside */ /* the plausible heap bounds. */ /* Add it to the normal incomplete black list if appropriate. */ -void GC_add_to_black_list_normal(p) +#ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_normal(p, source) + ptr_t source; +#else + void GC_add_to_black_list_normal(p) +#endif word p; { if (!(GC_modws_valid_offsets[p & (sizeof(word)-1)])) return; @@ -140,10 +167,13 @@ word p; register int index = PHT_HASH(p); if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) { -# ifdef PRINTBLACKLIST +# ifdef PRINT_BLACK_LIST if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) { - GC_printf1("Black listing (normal) 0x%lx\n", - (unsigned long) p); + GC_err_printf2( + "Black listing (normal) 0x%lx referenced from 0x%lx ", + (unsigned long) p, (unsigned long) source); + GC_print_source_ptr(source); + GC_err_puts("\n"); } # endif set_pht_entry_from_index(GC_incomplete_normal_bl, index); @@ -154,16 +184,24 @@ word p; # endif /* And the same for false pointers from the stack. */ -void GC_add_to_black_list_stack(p) +#ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_stack(p, source) + ptr_t source; +#else + void GC_add_to_black_list_stack(p) +#endif word p; { register int index = PHT_HASH(p); if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) { -# ifdef PRINTBLACKLIST +# ifdef PRINT_BLACK_LIST if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { - GC_printf1("Black listing (stack) 0x%lx\n", - (unsigned long)p); + GC_err_printf2( + "Black listing (stack) 0x%lx referenced from 0x%lx ", + (unsigned long)p, (unsigned long)source); + GC_print_source_ptr(source); + GC_err_puts("\n"); } # endif set_pht_entry_from_index(GC_incomplete_stack_bl, index); diff --git a/checksums.c b/checksums.c index 98ef08fe..212655f4 100644 --- a/checksums.c +++ b/checksums.c @@ -25,7 +25,7 @@ # define OFFSET 0x10000 typedef struct { - bool new_valid; + GC_bool new_valid; word old_sum; word new_sum; struct hblk * block; /* Block to which this refers + OFFSET */ @@ -50,7 +50,7 @@ struct hblk *h; # ifdef STUBBORN_ALLOC /* Check whether a stubborn object from the given block appears on */ /* the appropriate free list. */ -bool GC_on_free_list(h) +GC_bool GC_on_free_list(h) struct hblk *h; { register hdr * hhdr = HDR(h); diff --git a/cord/README b/cord/README index 865725ee..62101452 100644 --- a/cord/README +++ b/cord/README @@ -9,10 +9,10 @@ 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. -Please send bug reports to Hans-J. Boehm (boehm@parc.xerox.com). +Please send bug reports to Hans-J. Boehm (boehm@sgi.com). This is a string packages that uses a tree-based representation. -See gc.h for a description of the functions provided. Ec.h describes +See cord.h for a description of the functions provided. Ec.h describes "extensible cords", which are essentially output streams that write to a cord. These allow for efficient construction of cords without requiring a bound on the size of a cord. diff --git a/cord/cordbscs.c b/cord/cordbscs.c index b75f5812..9fc894d4 100644 --- a/cord/cordbscs.c +++ b/cord/cordbscs.c @@ -361,7 +361,6 @@ CORD CORD_substr_checked(CORD x, size_t i, size_t n) return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); } else { register char * result = GC_MALLOC_ATOMIC(n+1); - register char * p = result; if (result == 0) OUT_OF_MEMORY; strncpy(result, x+i, n); diff --git a/cord/cordprnt.c b/cord/cordprnt.c index 667560f2..9c8cc873 100644 --- a/cord/cordprnt.c +++ b/cord/cordprnt.c @@ -231,8 +231,9 @@ int CORD_vsprintf(CORD * out, CORD format, va_list args) goto done; case 'c': if (width == NONE && prec == NONE) { - register char c = va_arg(args, char); + register char c; + c = va_arg(args, char); CORD_ec_append(result, c); goto done; } diff --git a/cord/cordxtra.c b/cord/cordxtra.c index 8566e146..b306fbac 100644 --- a/cord/cordxtra.c +++ b/cord/cordxtra.c @@ -390,7 +390,7 @@ size_t CORD_str(CORD x, size_t start, CORD s) x_buf |= CORD_pos_fetch(xpos); CORD_next(xpos); } - for (match_pos = start; match_pos < xlen - slen; match_pos++) { + for (match_pos = start; ; match_pos++) { if ((x_buf & mask) == s_buf) { if (slen == start_len || CORD_ncmp(x, match_pos + start_len, @@ -398,11 +398,13 @@ size_t CORD_str(CORD x, size_t start, CORD s) return(match_pos); } } + if ( match_pos == xlen - slen ) { + return(CORD_NOT_FOUND); + } x_buf <<= 8; x_buf |= CORD_pos_fetch(xpos); CORD_next(xpos); } - return(CORD_NOT_FOUND); } void CORD_ec_flush_buf(CORD_ec x) diff --git a/cord/de_win.c b/cord/de_win.c index 119d0fa0..fedbfbe6 100644 --- a/cord/de_win.c +++ b/cord/de_win.c @@ -268,6 +268,7 @@ LRESULT CALLBACK WndProc (HWND hwnd, UINT message, case WM_DESTROY: PostQuitMessage (0); + GC_win32_free_heap(); return 0; case WM_PAINT: @@ -36,11 +36,19 @@ #endif #if defined(_MSC_VER) && defined(_DLL) -#ifdef GC_BUILD -#define GC_API __declspec(dllexport) -#else -#define GC_API __declspec(dllimport) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif #endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif #endif #ifndef GC_API @@ -126,7 +134,19 @@ GC_API GC_word GC_max_retries; /* reporting out of memory after heap */ /* expansion fails. Initially 0. */ - + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + /* Public procedures */ /* * general purpose allocation routines, with roughly malloc calling conv. @@ -193,8 +213,8 @@ GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); /* If the argument is stubborn, the result will have changes enabled. */ /* It is an error to have changes enabled for the original object. */ /* Follows ANSI comventions for NULL old_object. */ -GC_API GC_PTR GC_realloc GC_PROTO((GC_PTR old_object, - size_t new_size_in_bytes)); +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); /* Explicitly increase the heap size. */ /* Returns 0 on failure, 1 on success. */ @@ -293,37 +313,48 @@ GC_API int GC_collect_a_little GC_PROTO((void)); GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#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 +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + /* Debugging (annotated) allocation. GC_gcollect will check */ /* objects allocated in this way for overwrites, etc. */ GC_API GC_PTR GC_debug_malloc - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_atomic - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_uncollectable - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_stubborn - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); GC_API GC_PTR GC_debug_realloc GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, - char * descr_string, int descr_int)); + GC_EXTRA_PARAMS)); GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); # ifdef GC_DEBUG -# define GC_MALLOC(sz) GC_debug_malloc(sz, __FILE__, __LINE__) -# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, __FILE__, __LINE__) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) # define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ - __FILE__, __LINE__) -# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, __FILE__, \ - __LINE__) + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) # define GC_FREE(p) GC_debug_free(p) # define GC_REGISTER_FINALIZER(p, f, d, of, od) \ 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_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, __FILE__, \ - __LINE__) +# 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) # define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ @@ -473,6 +504,14 @@ 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)); +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 */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* FINALIZE_ON_DEMAND is defined, it must be called */ + /* explicitly. */ + /* GC_set_warn_proc can be used to redirect or filter warning messages. */ /* p may not be a NULL pointer. */ typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); @@ -585,6 +624,10 @@ 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. */ @@ -624,7 +667,7 @@ GC_API void (*GC_is_visible_print_proc) # endif /* SOLARIS_THREADS */ -#ifdef IRIX_THREADS +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) /* We treat these similarly. */ # include <pthread.h> # include <signal.h> @@ -639,9 +682,12 @@ GC_API void (*GC_is_visible_print_proc) # define pthread_sigmask GC_pthread_sigmask # define pthread_join GC_pthread_join -#endif /* IRIX_THREADS */ +#endif /* IRIX_THREADS || LINUX_THREADS */ -#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_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. */ @@ -650,7 +696,7 @@ GC_PTR GC_malloc_many(size_t lb); /* in returned list. */ extern void GC_thr_init(); /* Needed for Solaris/X86 */ -#endif /* SOLARIS_THREADS */ +#endif /* THREADS && !SRC_M3 */ /* * If you are planning on putting @@ -672,12 +718,11 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif #endif -#ifdef __WATCOMC__ - /* Ivan Demakov: Programs compiled by Watcom C with -5r option - * crash without this declaration - * HB: Could this go into gc_priv.h? - */ - void GC_noop(void*, ...); +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); #endif #ifdef __cplusplus @@ -1,6 +1,7 @@ /* * 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. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -14,6 +15,11 @@ /* Boehm, October 9, 1995 1:16 pm PDT */ # include "gc_priv.h" +void GC_default_print_heap_obj_proc(); +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)); + /* Do we want to and know how to save the call stack at the time of */ /* an allocation? How much space do we want to use in each object? */ @@ -27,7 +33,7 @@ typedef struct { char * oh_string; /* object descriptor string */ word oh_int; /* object descriptor integers */ -# ifdef SAVE_CALL_CHAIN +# ifdef NEED_CALLINFO struct callinfo oh_ci[NFRAMES]; # endif word oh_sz; /* Original malloc arg. */ @@ -42,17 +48,22 @@ typedef struct { #ifdef SAVE_CALL_CHAIN -# define ADD_CALL_CHAIN(base) GC_save_callers(((oh *)(base)) -> oh_ci) +# 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 -# define ADD_CALL_CHAIN(base) +# 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 /* 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. */ -bool GC_has_debug_info(p) +GC_bool GC_has_debug_info(p) ptr_t p; { register oh * ohdr = (oh *)p; @@ -134,6 +145,17 @@ ptr_t p; (unsigned long)(ohdr -> oh_sz)); PRINT_CALL_CHAIN(ohdr); } + +void GC_debug_print_heap_obj_proc(p) +ptr_t p; +{ + if (GC_has_debug_info(p)) { + GC_print_obj(p); + } else { + GC_default_print_heap_obj_proc(p); + } +} + void GC_print_smashed_obj(p, clobbered_addr) ptr_t p, clobbered_addr; { @@ -162,6 +184,7 @@ void GC_check_heap_proc(); void GC_start_debugging() { GC_check_heap = GC_check_heap_proc; + GC_print_heap_obj = GC_debug_print_heap_obj_proc; GC_debugging_started = TRUE; GC_register_displacement((word)sizeof(oh)); } @@ -177,13 +200,24 @@ void GC_start_debugging() GC_register_displacement((word)sizeof(oh) + offset); } +# ifdef GC_ADD_CALLER +# define EXTRA_ARGS word ra, char * s, int i +# define OPT_RA ra, +# else +# define EXTRA_ARGS char * s, int i +# define OPT_RA +# endif + # ifdef __STDC__ - GC_PTR GC_debug_malloc(size_t lb, char * s, int i) + GC_PTR GC_debug_malloc(size_t lb, EXTRA_ARGS) # else GC_PTR GC_debug_malloc(lb, s, i) size_t lb; char * s; int i; +# ifdef GC_ADD_CALLER + --> GC_ADD_CALLER not implemented for K&R C +# endif # endif { GC_PTR result = GC_malloc(lb + DEBUG_BYTES); @@ -198,13 +232,13 @@ void GC_start_debugging() if (!GC_debugging_started) { GC_start_debugging(); } - ADD_CALL_CHAIN(result); + ADD_CALL_CHAIN(result, ra); return (GC_store_debug_info(result, (word)lb, s, (word)i)); } #ifdef STUBBORN_ALLOC # ifdef __STDC__ - GC_PTR GC_debug_malloc_stubborn(size_t lb, char * s, int i) + GC_PTR GC_debug_malloc_stubborn(size_t lb, EXTRA_ARGS) # else GC_PTR GC_debug_malloc_stubborn(lb, s, i) size_t lb; @@ -224,7 +258,7 @@ void GC_start_debugging() if (!GC_debugging_started) { GC_start_debugging(); } - ADD_CALL_CHAIN(result); + ADD_CALL_CHAIN(result, ra); return (GC_store_debug_info(result, (word)lb, s, (word)i)); } @@ -271,7 +305,7 @@ GC_PTR p; #endif /* STUBBORN_ALLOC */ # ifdef __STDC__ - GC_PTR GC_debug_malloc_atomic(size_t lb, char * s, int i) + GC_PTR GC_debug_malloc_atomic(size_t lb, EXTRA_ARGS) # else GC_PTR GC_debug_malloc_atomic(lb, s, i) size_t lb; @@ -291,12 +325,12 @@ GC_PTR p; if (!GC_debugging_started) { GC_start_debugging(); } - ADD_CALL_CHAIN(result); + ADD_CALL_CHAIN(result, ra); return (GC_store_debug_info(result, (word)lb, s, (word)i)); } # ifdef __STDC__ - GC_PTR GC_debug_malloc_uncollectable(size_t lb, char * s, int i) + GC_PTR GC_debug_malloc_uncollectable(size_t lb, EXTRA_ARGS) # else GC_PTR GC_debug_malloc_uncollectable(lb, s, i) size_t lb; @@ -316,10 +350,37 @@ GC_PTR p; if (!GC_debugging_started) { GC_start_debugging(); } - ADD_CALL_CHAIN(result); + ADD_CALL_CHAIN(result, ra); return (GC_store_debug_info(result, (word)lb, s, (word)i)); } +#ifdef ATOMIC_UNCOLLECTABLE +# ifdef __STDC__ + GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc_atomic_uncollectable(lb, s, i) + size_t lb; + char * s; + int i; +# endif +{ + GC_PTR result = GC_malloc_atomic_uncollectable(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1( + "GC_debug_malloc_atomic_uncollectable(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} +#endif /* ATOMIC_UNCOLLECTABLE */ # ifdef __STDC__ void GC_debug_free(GC_PTR p) @@ -359,7 +420,7 @@ GC_PTR p; # else { register hdr * hhdr = HDR(p); - bool uncollectable = FALSE; + GC_bool uncollectable = FALSE; if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { uncollectable = TRUE; @@ -375,7 +436,7 @@ GC_PTR p; } # ifdef __STDC__ - GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, char *s, int i) + GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, EXTRA_ARGS) # else GC_PTR GC_debug_realloc(p, lb, s, i) GC_PTR p; @@ -386,15 +447,15 @@ GC_PTR p; { register GC_PTR base = GC_base(p); register ptr_t clobbered; - register GC_PTR result = GC_debug_malloc(lb, s, i); + register GC_PTR result; register size_t copy_sz = lb; register size_t old_sz; register hdr * hhdr; - if (p == 0) return(GC_debug_malloc(lb, s, i)); + if (p == 0) return(GC_debug_malloc(lb, OPT_RA s, i)); if (base == 0) { GC_err_printf1( - "Attempt to free invalid pointer %lx\n", (unsigned long)p); + "Attempt to reallocate invalid pointer %lx\n", (unsigned long)p); ABORT("realloc(invalid pointer)"); } if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { @@ -407,18 +468,23 @@ GC_PTR p; switch (hhdr -> hb_obj_kind) { # ifdef STUBBORN_ALLOC case STUBBORN: - result = GC_debug_malloc_stubborn(lb, s, i); + result = GC_debug_malloc_stubborn(lb, OPT_RA s, i); break; # endif case NORMAL: - result = GC_debug_malloc(lb, s, i); + result = GC_debug_malloc(lb, OPT_RA s, i); break; case PTRFREE: - result = GC_debug_malloc_atomic(lb, s, i); + result = GC_debug_malloc_atomic(lb, OPT_RA s, i); break; case UNCOLLECTABLE: - result = GC_debug_malloc_uncollectable(lb, s, i); + result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); break; +# ifdef ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); + break; +# endif default: GC_err_printf0("GC_debug_realloc: encountered bad kind\n"); ABORT("bad kind"); @@ -432,6 +498,7 @@ GC_PTR p; if (old_sz < copy_sz) copy_sz = old_sz; if (result == 0) return(0); BCOPY(p, result, copy_sz); + GC_debug_free(p); return(result); } @@ -542,6 +609,31 @@ struct closure { } # ifdef __STDC__ + void GC_debug_register_finalizer_no_order + (GC_PTR obj, GC_finalization_proc fn, + GC_PTR cd, GC_finalization_proc *ofn, + GC_PTR *ocd) +# else + void GC_debug_register_finalizer_no_order + (obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc *ofn; + GC_PTR *ocd; +# endif +{ + ptr_t base = GC_base(obj); + if (0 == base || (ptr_t)obj - base != sizeof(oh)) { + GC_err_printf1( + "GC_register_finalizer_no_order called with non-base-pointer 0x%lx\n", + obj); + } + GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, + GC_make_closure(fn,cd), ofn, ocd); + } + +# ifdef __STDC__ void GC_debug_register_finalizer_ignore_self (GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, GC_finalization_proc *ofn, @@ -47,8 +47,8 @@ #if (defined(DYNAMIC_LOADING) || defined(MSWIN32)) && !defined(PCR) #if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \ !defined(MSWIN32) && !(defined(ALPHA) && defined(OSF1)) && \ - !defined(HP_PA) && (!defined(LINUX) && !defined(__ELF__)) && \ - !defined(RS6000) + !defined(HP_PA) && !(defined(LINUX) && defined(__ELF__)) && \ + !defined(RS6000) && !defined(SCO_ELF) --> We only know how to find data segments of dynamic libraries for the --> above. Additional SVR4 variants might not be too --> hard to add. @@ -260,7 +260,7 @@ void GC_register_dynamic_libraries() # endif /* !USE_PROC ... */ # endif /* SUNOS */ -#if defined(LINUX) && defined(__ELF__) +#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) /* Dynamic loading code for Linux running ELF. Somewhat tested on * Linux/x86, untested but hopefully should work on Linux/Alpha. @@ -468,7 +468,7 @@ void GC_register_dynamic_libraries() /* that could possibly have been written to. */ DWORD GC_allocation_granularity; - extern bool GC_is_heap_base (ptr_t p); + extern GC_bool GC_is_heap_base (ptr_t p); # ifdef WIN32_THREADS extern void GC_get_next_stack(char *start, char **lo, char **hi); @@ -503,7 +503,7 @@ void GC_register_dynamic_libraries() # endif } - extern bool GC_win32s; + extern GC_bool GC_win32s; void GC_register_dynamic_libraries() { @@ -648,8 +648,9 @@ void GC_enqueue_all_finalizers() * which can make the runtime guarantee that all finalizers are run. * Unfortunately, the Java standard implies we have to keep running * finalizers until there are no more left, a potential infinite loop. - * YUCK. * This routine is externally callable, so is called without - * the allocation lock + * YUCK. + * This routine is externally callable, so is called without + * the allocation lock. */ void GC_finalize_all() { @@ -661,7 +662,7 @@ void GC_finalize_all() GC_enqueue_all_finalizers(); UNLOCK(); ENABLE_SIGNALS(); - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); } @@ -672,9 +673,10 @@ void GC_finalize_all() /* Invoke finalizers for all objects that are ready to be finalized. */ /* Should be called without allocation lock. */ -void GC_invoke_finalizers() +int GC_invoke_finalizers() { register struct finalizable_object * curr_fo; + register int count = 0; DCL_LOCK_STATE; while (GC_finalize_now != 0) { @@ -695,6 +697,7 @@ void GC_invoke_finalizers() (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), curr_fo -> fo_client_data); curr_fo -> fo_client_data = 0; + ++count; # ifdef UNDEFINED /* This is probably a bad idea. It throws off accounting if */ /* nearly all objects are finalizable. O.w. it shouldn't */ @@ -702,6 +705,7 @@ void GC_invoke_finalizers() GC_free((GC_PTR)curr_fo); # endif } + return count; } # ifdef __STDC__ @@ -36,11 +36,19 @@ #endif #if defined(_MSC_VER) && defined(_DLL) -#ifdef GC_BUILD -#define GC_API __declspec(dllexport) -#else -#define GC_API __declspec(dllimport) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif #endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif #endif #ifndef GC_API @@ -126,7 +134,19 @@ GC_API GC_word GC_max_retries; /* reporting out of memory after heap */ /* expansion fails. Initially 0. */ - + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + /* Public procedures */ /* * general purpose allocation routines, with roughly malloc calling conv. @@ -193,8 +213,8 @@ GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); /* If the argument is stubborn, the result will have changes enabled. */ /* It is an error to have changes enabled for the original object. */ /* Follows ANSI comventions for NULL old_object. */ -GC_API GC_PTR GC_realloc GC_PROTO((GC_PTR old_object, - size_t new_size_in_bytes)); +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); /* Explicitly increase the heap size. */ /* Returns 0 on failure, 1 on success. */ @@ -293,37 +313,48 @@ GC_API int GC_collect_a_little GC_PROTO((void)); GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#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 +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + /* Debugging (annotated) allocation. GC_gcollect will check */ /* objects allocated in this way for overwrites, etc. */ GC_API GC_PTR GC_debug_malloc - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_atomic - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_uncollectable - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_stubborn - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); GC_API GC_PTR GC_debug_realloc GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, - char * descr_string, int descr_int)); + GC_EXTRA_PARAMS)); GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); # ifdef GC_DEBUG -# define GC_MALLOC(sz) GC_debug_malloc(sz, __FILE__, __LINE__) -# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, __FILE__, __LINE__) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) # define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ - __FILE__, __LINE__) -# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, __FILE__, \ - __LINE__) + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) # define GC_FREE(p) GC_debug_free(p) # define GC_REGISTER_FINALIZER(p, f, d, of, od) \ 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_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, __FILE__, \ - __LINE__) +# 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) # define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ @@ -473,6 +504,14 @@ 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)); +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 */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* FINALIZE_ON_DEMAND is defined, it must be called */ + /* explicitly. */ + /* GC_set_warn_proc can be used to redirect or filter warning messages. */ /* p may not be a NULL pointer. */ typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); @@ -585,6 +624,10 @@ 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. */ @@ -624,7 +667,7 @@ GC_API void (*GC_is_visible_print_proc) # endif /* SOLARIS_THREADS */ -#ifdef IRIX_THREADS +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) /* We treat these similarly. */ # include <pthread.h> # include <signal.h> @@ -639,9 +682,12 @@ GC_API void (*GC_is_visible_print_proc) # define pthread_sigmask GC_pthread_sigmask # define pthread_join GC_pthread_join -#endif /* IRIX_THREADS */ +#endif /* IRIX_THREADS || LINUX_THREADS */ -#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_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. */ @@ -650,7 +696,7 @@ GC_PTR GC_malloc_many(size_t lb); /* in returned list. */ extern void GC_thr_init(); /* Needed for Solaris/X86 */ -#endif /* SOLARIS_THREADS */ +#endif /* THREADS && !SRC_M3 */ /* * If you are planning on putting @@ -672,12 +718,11 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif #endif -#ifdef __WATCOMC__ - /* Ivan Demakov: Programs compiled by Watcom C with -5r option - * crash without this declaration - * HB: Could this go into gc_priv.h? - */ - void GC_noop(void*, ...); +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); #endif #ifdef __cplusplus @@ -5,7 +5,7 @@ # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 !IF "$(CFG)" == "" -CFG=cord - Win32 Debug +CFG=gctest - Win32 Release !MESSAGE No configuration specified. Defaulting to cord - Win32 Debug. !ENDIF @@ -768,7 +768,7 @@ SOURCE=.\reclaim.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_RECLA=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -788,7 +788,7 @@ NODEP_CPP_RECLA=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_RECLA=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -816,7 +816,7 @@ SOURCE=.\os_dep.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_OS_DE=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -840,7 +840,7 @@ NODEP_CPP_OS_DE=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_OS_DE=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -872,7 +872,7 @@ SOURCE=.\misc.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MISC_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -893,7 +893,7 @@ NODEP_CPP_MISC_=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MISC_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -922,7 +922,7 @@ SOURCE=.\mark_rts.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MARK_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -942,7 +942,7 @@ NODEP_CPP_MARK_=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MARK_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -970,7 +970,7 @@ SOURCE=.\mach_dep.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MACH_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -990,7 +990,7 @@ NODEP_CPP_MACH_=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MACH_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1018,7 +1018,7 @@ SOURCE=.\headers.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_HEADE=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1038,7 +1038,7 @@ NODEP_CPP_HEADE=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_HEADE=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1066,7 +1066,7 @@ SOURCE=.\alloc.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_ALLOC=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1086,7 +1086,7 @@ NODEP_CPP_ALLOC=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_ALLOC=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1114,7 +1114,7 @@ SOURCE=.\allchblk.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_ALLCH=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1134,7 +1134,7 @@ NODEP_CPP_ALLCH=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_ALLCH=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1162,7 +1162,7 @@ SOURCE=.\stubborn.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_STUBB=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1182,7 +1182,7 @@ NODEP_CPP_STUBB=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_STUBB=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1210,7 +1210,7 @@ SOURCE=.\obj_map.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_OBJ_M=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1230,7 +1230,7 @@ NODEP_CPP_OBJ_M=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_OBJ_M=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1258,7 +1258,7 @@ SOURCE=.\new_hblk.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_NEW_H=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1278,7 +1278,7 @@ NODEP_CPP_NEW_H=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_NEW_H=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1306,7 +1306,7 @@ SOURCE=.\mark.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MARK_C=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1327,7 +1327,7 @@ NODEP_CPP_MARK_C=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MARK_C=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1356,7 +1356,7 @@ SOURCE=.\malloc.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MALLO=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1376,7 +1376,7 @@ NODEP_CPP_MALLO=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MALLO=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1404,7 +1404,7 @@ SOURCE=.\mallocx.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_MALLX=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1424,7 +1424,7 @@ NODEP_CPP_MALLX=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_MALLX=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1452,7 +1452,7 @@ SOURCE=.\finalize.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_FINAL=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1473,7 +1473,7 @@ NODEP_CPP_FINAL=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_FINAL=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1502,7 +1502,7 @@ SOURCE=.\dbg_mlc.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_DBG_M=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1522,7 +1522,7 @@ NODEP_CPP_DBG_M=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_DBG_M=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1550,7 +1550,7 @@ SOURCE=.\blacklst.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_BLACK=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1570,7 +1570,7 @@ NODEP_CPP_BLACK=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_BLACK=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1598,7 +1598,7 @@ SOURCE=.\typd_mlc.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_TYPD_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1620,7 +1620,7 @@ NODEP_CPP_TYPD_=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_TYPD_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1650,7 +1650,7 @@ SOURCE=.\ptr_chck.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_PTR_C=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1671,7 +1671,7 @@ NODEP_CPP_PTR_C=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_PTR_C=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_mark.h"\ @@ -1700,7 +1700,7 @@ SOURCE=.\dyn_load.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_DYN_L=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1723,7 +1723,7 @@ NODEP_CPP_DYN_L=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_DYN_L=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1754,7 +1754,7 @@ SOURCE=.\win32_threads.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_WIN32=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1774,7 +1774,7 @@ NODEP_CPP_WIN32=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_WIN32=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1802,7 +1802,7 @@ SOURCE=.\checksums.c !IF "$(CFG)" == "gc - Win32 Release" DEP_CPP_CHECK=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1822,7 +1822,7 @@ NODEP_CPP_CHECK=\ !ELSEIF "$(CFG)" == "gc - Win32 Debug" DEP_CPP_CHECK=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1878,7 +1878,7 @@ NODEP_CPP_CHECK=\ SOURCE=.\test.c DEP_CPP_TEST_=\ - ".\config.h"\ + ".\gcconfig.h"\ ".\gc.h"\ ".\gc_hdrs.h"\ ".\gc_priv.h"\ @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -32,6 +32,8 @@ #define GC_ALLOC_H #define __ALLOC_H // Prevent inclusion of the default version. Ugly. +#define __SGI_STL_ALLOC_H +#define __SGI_STL_INTERNAL_ALLOC_H #ifndef __ALLOC # define __ALLOC alloc @@ -304,8 +306,8 @@ class gc_alloc_template { static void * allocate(size_t n) { return GC_malloc(n); } static void * ptr_free_allocate(size_t n) { return GC_malloc_atomic(n); } - static void deallocate(void *p, size_t n) { } - static void ptr_free_deallocate(void *p, size_t n) { } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } }; typedef gc_alloc_template < 0 > gc_alloc; @@ -316,8 +318,8 @@ class alloc_template { static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } static void * ptr_free_allocate(size_t n) { return GC_malloc_atomic_uncollectable(n); } - static void deallocate(void *p, size_t n) { GC_free(p); } - static void ptr_free_deallocate(void *p, size_t n) { GC_free(p); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } }; typedef alloc_template < 0 > alloc; @@ -367,6 +369,12 @@ __GC_SPECIALIZE(unsigned, single_client_alloc) __GC_SPECIALIZE(float, single_client_alloc) __GC_SPECIALIZE(double, single_client_alloc) +#ifdef __STL_USE_STD_ALLOCATORS + +???copy stuff from stl_alloc.h or remove it to a different file ??? + +#endif /* __STL_USE_STD_ALLOCATORS */ + #endif /* _SGI_SOURCE */ #endif /* GC_ALLOC_H */ @@ -32,6 +32,20 @@ void* operator new( size_t size ) { void operator delete( void* obj ) { GC_FREE( obj );} +#ifdef _MSC_VER +// This new operator is used by VC++ in case of Debug builds ! +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 #ifdef OPERATOR_NEW_ARRAY @@ -133,7 +133,8 @@ uses explicit invocation. #endif #if ! defined( OPERATOR_NEW_ARRAY ) \ - && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6)) + && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6) \ + || __WATCOMC__ >= 1050) # define OPERATOR_NEW_ARRAY #endif @@ -38,12 +38,17 @@ /* subset of the places the conservative marker would. It must be safe */ /* to invoke the normal mark procedure instead. */ # define PROC_BYTES 100 -typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, - mark_stack_limit, env */); +/* The real declarations of the following are in gc_priv.h, so that */ +/* we can avoid scanning the following 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) extern mark_proc GC_mark_procs[MAX_MARK_PROCS]; +*/ + extern word GC_n_mark_procs; /* Object descriptors on mark stack or in objects. Low order two */ @@ -124,9 +129,20 @@ mse * GC_signal_mark_stack_overflow(); } \ } -/* Push the contenst of current onto the mark stack if it is a valid */ +#ifdef PRINT_BLACK_LIST +# define GC_FIND_START(current, hhdr, source) \ + GC_find_start(current, hhdr, source) +#else +# define GC_FIND_START(current, hhdr, source) \ + GC_find_start(current, hhdr) +#endif + +/* Push the contents of current onto the mark stack if it is a valid */ /* ptr to a currently unmarked object. Mark it. */ -# define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit) \ +/* 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) \ { \ register int displ; /* Displacement in block; first bytes, then words */ \ register hdr * hhdr; \ @@ -134,14 +150,14 @@ mse * GC_signal_mark_stack_overflow(); \ GET_HDR(current,hhdr); \ if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ - current = GC_find_start(current, hhdr); \ - if (current == 0) continue; \ + current = GC_FIND_START(current, hhdr, (word)source); \ + if (current == 0) goto exit_label; \ hhdr = HDR(current); \ } \ displ = HBLKDISPL(current); \ map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \ if (map_entry == OBJ_INVALID) { \ - GC_ADD_TO_BLACK_LIST_NORMAL(current); continue; \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); goto exit_label; \ } \ displ = BYTES_TO_WORDS(displ); \ displ -= map_entry; \ @@ -153,12 +169,13 @@ mse * GC_signal_mark_stack_overflow(); \ if (mark_word & mark_bit) { \ /* Mark bit is already set */ \ - continue; \ + goto exit_label; \ } \ *mark_word_addr = mark_word | mark_bit; \ } \ PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \ mark_stack_top, mark_stack_limit) \ + exit_label: ; \ } @@ -205,7 +222,7 @@ mse * GC_signal_mark_stack_overflow(); } \ } -extern bool GC_mark_stack_too_small; +extern GC_bool GC_mark_stack_too_small; /* We need a larger mark stack. May be */ /* set by client supplied mark routines.*/ @@ -42,29 +42,14 @@ typedef GC_word word; typedef GC_signed_word signed_word; # ifndef CONFIG_H -# include "config.h" +# include "gcconfig.h" # endif # ifndef HEADERS_H # include "gc_hdrs.h" # endif -# if !defined(bool) && !defined(__cplusplus) - typedef int bool; - /* This is problematic with C++ implementations that do not define bool. */ - /* By now they should. */ -# else -# if defined(_SGI_SOURCE) && !defined(_BOOL) - typedef int bool; -# endif -# if defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x410 - typedef int bool; -# endif -# if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1020 - /* Visual C++ 4.2 does not have bool type. */ - typedef int bool; -# endif -# endif +typedef int GC_bool; # define TRUE 1 # define FALSE 0 @@ -148,16 +133,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ #define PRINTBLOCKS /* Print object sizes associated with heap blocks, */ /* whether the objects are atomic or composite, and */ /* whether or not the block was found to be empty */ - /* duing the reclaim phase. Typically generates */ + /* during the reclaim phase. Typically generates */ /* about one screenful per garbage collection. */ #undef PRINTBLOCKS -#define PRINTBLACKLIST /* Print black listed blocks, i.e. values that */ - /* cause the allocator to avoid allocating certain */ - /* blocks in order to avoid introducing "false */ - /* hits". */ -#undef PRINTBLACKLIST - #ifdef SILENT # ifdef PRINTSTATS # undef PRINTSTATS @@ -174,6 +153,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() +#endif + #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 */ @@ -185,6 +170,15 @@ 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. */ +#endif + /* ALIGN_DOUBLE requires MERGE_SIZES at present. */ # if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) # define MERGE_SIZES @@ -197,6 +191,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # 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 */ # else # define MINHINCR 64 @@ -224,6 +219,8 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* */ /*********************************/ +#ifdef SAVE_CALL_CHAIN + /* * Number of frames and arguments to save in objects allocated by * debugging allocator. @@ -232,12 +229,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* alignment reasons. */ # define NARGS 2 /* Mumber of arguments to save for each call. */ - -#ifdef SAVE_CALL_CHAIN - struct callinfo { - word ci_pc; - word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ - }; +# define NEED_CALLINFO /* Fill in the pc and argument information for up to NFRAMES of my */ /* callers. Ignore my frame and my callers frame. */ @@ -245,6 +237,27 @@ void GC_save_callers (/* struct callinfo info[NFRAMES] */); void GC_print_callers (/* struct callinfo info[NFRAMES] */); +#else + +# ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +# endif + +#endif + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if defined(ALIGN_DOUBLE) && (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; #endif @@ -321,6 +334,11 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); /* HBLKSIZE aligned allocation. 0 is taken to mean failure */ /* space is assumed to be cleared. */ +/* In the case os USE_MMAP, the argument must also be a */ +/* physical page size. */ +/* GET_MEM is currently not assumed to retrieve 0 filled space, */ +/* though we should perhaps take advantage of the case in which */ +/* does. */ # ifdef PCR char * real_malloc(); # define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ @@ -416,12 +434,59 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define LOCK() mutex_lock(&GC_allocate_ml); # define UNLOCK() mutex_unlock(&GC_allocate_ml); # endif -# ifdef IRIX_THREADS +# 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)) -# define __test_and_set(l,v) test_and_set(l,v) +# 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) */ @@ -439,20 +504,27 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define LOCK() pthread_mutex_lock(&GC_allocate_ml) # define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) # else -# define LOCK() { if (__test_and_set(&GC_allocate_lock, 1)) GC_lock(); } -# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) +# 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 -# define UNLOCK() GC_allocate_lock = 0 + /* 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 bool GC_collecting; + extern GC_bool GC_collecting; # define ENTER_GC() \ { \ GC_collecting = 1; \ } # define EXIT_GC() GC_collecting = 0; -# endif +# endif /* IRIX_THREADS || IRIX_JDK_THREADS */ # ifdef WIN32_THREADS # include <windows.h> GC_API CRITICAL_SECTION GC_allocate_ml; @@ -505,7 +577,8 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # else # if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ - || defined(NO_SIGNALS) || defined(IRIX_THREADS) + || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ + || defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS) /* Also useful for debugging. */ /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ # define DISABLE_SIGNALS() @@ -532,7 +605,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); PCR_allSigsBlocked, \ PCR_waitForever); # else -# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS) +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) void GC_stop_world(); void GC_start_world(); # define STOP_WORLD() GC_stop_world() @@ -801,7 +876,69 @@ struct hblk { /* Object free list link */ # define obj_link(p) (*(ptr_t *)(p)) -/* lists of all heap blocks and free lists */ +/* 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) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 4096 +# else +# ifdef PCR +# define MAX_ROOT_SETS 1024 +# else +# ifdef MSWIN32 +# define MAX_ROOT_SETS 512 + /* Under NT, we add only written pages, which can result */ + /* in many small root sets. */ +# else +# define MAX_ROOT_SETS 64 +# endif +# endif +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start; + ptr_t r_end; +# ifndef MSWIN32 + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#ifndef MSWIN32 + /* 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 */ +#endif + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ /* These are grouped together in a struct */ /* so that they can be easily skipped by the */ /* GC_mark routine. */ @@ -841,7 +978,10 @@ struct _GC_arrays { word _mem_freed; /* Number of explicitly deallocated words of memory */ /* since last collection. */ - + 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]; /* free list for objects */ ptr_t _aobjfreelist[MAXOBJSZ+1]; @@ -923,17 +1063,24 @@ struct _GC_arrays { /* GC_modws_valid_offsets[i%sizeof(word)] */ # endif # ifdef STUBBORN_ALLOC - page_hash_table _changed_pages; + page_hash_table _changed_pages; /* Stubborn object pages that were changes since last call to */ /* GC_read_changed. */ - page_hash_table _prev_changed_pages; + page_hash_table _prev_changed_pages; /* Stubborn object pages that were changes before last call to */ /* GC_read_changed. */ # endif # if defined(PROC_VDB) || defined(MPROTECT_VDB) - page_hash_table _grungy_pages; /* Pages that were dirty at last */ + page_hash_table _grungy_pages; /* Pages that were dirty at last */ /* GC_read_dirty. */ # endif +# ifdef MPROTECT_VDB + VOLATILE page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# ifdef PROC_VDB + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif # ifdef LARGE_CONFIG # if CPP_WORDSZ > 32 # define MAX_HEAP_SECTS 4096 /* overflows at roughly 64 GB */ @@ -950,6 +1097,11 @@ struct _GC_arrays { ptr_t _heap_bases[MAX_HEAP_SECTS]; /* Start address of memory regions obtained from kernel. */ # endif + struct roots _static_roots[MAX_ROOT_SETS]; +# ifndef MSWIN32 + struct roots * _root_index[RT_SIZE]; +# endif + struct exclusion _excl_table[MAX_EXCLUSIONS]; /* Block header index; see gc_headers.h */ bottom_index * _all_nils; bottom_index * _top_index [TOP_SZ]; @@ -986,6 +1138,7 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # 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_mark_procs GC_arrays._mark_procs # define GC_heapsize GC_arrays._heapsize # define GC_max_heapsize GC_arrays._max_heapsize # define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc @@ -994,11 +1147,20 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # ifdef MSWIN32 # define GC_heap_bases GC_arrays._heap_bases # 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 # define GC_all_nils GC_arrays._all_nils # define GC_top_index GC_arrays._top_index # if defined(PROC_VDB) || defined(MPROTECT_VDB) # define GC_grungy_pages GC_arrays._grungy_pages # endif +# ifdef MPROTECT_VDB +# define GC_dirty_pages GC_arrays._dirty_pages +# endif +# ifdef PROC_VDB +# define GC_written_pages GC_arrays._written_pages +# endif # ifdef GATHERSTATS # define GC_composite_in_use GC_arrays._composite_in_use # define GC_atomic_in_use GC_arrays._atomic_in_use @@ -1010,11 +1172,9 @@ 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)) -GC_API word GC_fo_entries; - +/* Object kinds: */ # define MAXOBJKINDS 16 -/* Object kinds: */ extern struct obj_kind { ptr_t *ok_freelist; /* Array of free listheaders for this kind of object */ /* Point either to GC_arrays or to storage allocated */ @@ -1024,12 +1184,18 @@ extern struct obj_kind { /* swept. */ word ok_descriptor; /* Descriptor template for objects in this */ /* block. */ - bool ok_relocate_descr; + GC_bool ok_relocate_descr; /* Add object size in bytes to descriptor */ /* template to obtain descriptor. Otherwise */ /* template is used as is. */ - bool ok_init; /* Clear objects before putting them on the free list. */ + 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 end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ + endGC_obj_kinds : endGC_arrays) + /* Predefined kinds: */ # define PTRFREE 0 # define NORMAL 1 @@ -1045,6 +1211,8 @@ extern struct obj_kind { extern int GC_n_kinds; +GC_API word GC_fo_entries; + extern word GC_n_heap_sects; /* Number of separately added heap */ /* sections. */ @@ -1074,24 +1242,27 @@ extern struct hblk * GC_hblkfreelist; /* header structure associated with */ /* block. */ -extern bool GC_is_initialized; /* GC_init() has been run. */ +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ -extern bool GC_objects_are_marked; /* There are marked objects in */ +extern GC_bool GC_objects_are_marked; /* There are marked objects in */ /* the heap. */ -extern int GC_incremental; /* Using incremental/generational collection. */ +#ifndef SMALL_CONFIG + extern GC_bool GC_incremental; + /* Using incremental/generational collection. */ +#else +# define GC_incremental TRUE + /* Hopefully allow optimizer to remove some code. */ +#endif -extern bool GC_dirty_maintained;/* Dirty bits are being maintained, */ +extern GC_bool GC_dirty_maintained; + /* Dirty bits are being maintained, */ /* either for incremental collection, */ /* or to limit the root set. */ -# ifndef PCR - extern ptr_t GC_stackbottom; /* Cool end of user stack */ -# endif - extern word GC_root_size; /* Total size of registered root sections */ -extern bool GC_debugging_started; /* GC_debug_malloc has been called. */ +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; @@ -1110,7 +1281,7 @@ extern ptr_t GC_greatest_plausible_heap_addr; /* object are used. */ -/* Mark bit perations */ +/* Mark bit operations */ /* * Retrieve, set, clear the mark bit corresponding @@ -1130,6 +1301,13 @@ extern ptr_t GC_greatest_plausible_heap_addr; /* 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. */ @@ -1144,8 +1322,8 @@ void GC_invalidate_mark_state(); /* Tell the marker that marked */ void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ /* Return after about one pages worth of */ /* work. */ -bool GC_mark_stack_empty(); -bool GC_mark_some(); /* Perform about one pages worth of marking */ +GC_bool GC_mark_stack_empty(); +GC_bool GC_mark_some(); /* 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. */ @@ -1159,7 +1337,7 @@ void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ /* subintervals of [b,t) onto */ /* mark stack. */ #ifndef SMALL_CONFIG - void GC_push_conditional(/* ptr_t b, ptr_t t, bool all*/); + void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); #else # define GC_push_conditional(b, t, all) GC_push_all(b, t) #endif @@ -1167,7 +1345,7 @@ void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ /* on the third arg. */ void GC_push_all_stack(/*b,t*/); /* As above, but consider */ /* interior pointers as valid */ -void GC_push_roots(/* bool all */); /* Push all or dirty roots. */ +void GC_push_roots(/* GC_bool all */); /* Push all or dirty roots. */ extern void (*GC_push_other_roots)(); /* Push system or application specific roots */ /* onto the mark stack. In some environments */ @@ -1183,10 +1361,7 @@ extern void (*GC_start_call_back)(/* void */); 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. */ -void GC_push_one(/*p*/); /* If p points to an object, mark it */ - /* and push contents on the mark stack */ -/* Ivan Demakov: Watcom C error'ed without this */ -# if defined(MSWIN32) && defined(__WATCOMC__) +# if defined(MSWIN32) void __cdecl GC_push_one(); # else void GC_push_one(/*p*/); /* If p points to an object, mark it */ @@ -1207,12 +1382,12 @@ 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. */ -bool GC_stopped_mark(); /* Stop world and mark from all roots */ +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(); -bool GC_is_static_root(/* ptr_t p */); +GC_bool GC_is_static_root(/* ptr_t p */); /* Is the address p in one of the registered static */ /* root sections? */ void GC_register_dynamic_libraries(); @@ -1225,15 +1400,27 @@ void GC_register_data_segments(); /* Black listing: */ void GC_bl_init(); # ifndef ALL_INTERIOR_POINTERS - void GC_add_to_black_list_normal(/* bits */); + void GC_add_to_black_list_normal(/* bits, maybe source */); /* Register bits as a possible future false */ /* reference from the heap or static data */ -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits) GC_add_to_black_list_normal(bits) +# 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 # else -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits) GC_add_to_black_list_stack(bits) +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits) +# endif # endif -void GC_add_to_black_list_stack(/* bits */); +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 */ @@ -1262,7 +1449,7 @@ void GC_invalidate_map(/* hdr */); /* with the block. This identifies */ /* the block as invalid to the mark */ /* routines. */ -bool GC_add_map_entry(/*sz*/); +GC_bool GC_add_map_entry(/*sz*/); /* Add a heap block map for objects of */ /* size sz to obj_map. */ /* Return FALSE on failure. */ @@ -1286,7 +1473,7 @@ void GC_freehblk(); /* Deallocate a heap block and mark it */ /* Misc GC: */ void GC_init_inner(); -bool GC_expand_hp_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 */ @@ -1302,12 +1489,12 @@ void GC_reclaim_or_delete_all(); /* Arrange for all reclaim lists to be */ /* empty. Judiciously choose between */ /* sweeping and discarding each page. */ -bool GC_reclaim_all(/* GC_stop_func f*/); +GC_bool GC_reclaim_all(/* GC_stop_func f*/); /* Reclaim all blocks. Abort (in a */ /* consistent state) if f returns TRUE. */ -bool GC_block_empty(/* hhdr */); /* Block completely unmarked? */ -bool GC_never_stop_func(); /* Returns FALSE. */ -bool GC_try_to_collect_inner(/* GC_stop_func f */); +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 */); /* Collect; caller must have acquired */ /* lock and disabled signals. */ /* Collection is aborted if f returns */ @@ -1317,13 +1504,13 @@ bool GC_try_to_collect_inner(/* GC_stop_func f */); (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. */ -bool GC_collect_or_expand(/* needed_blocks */); +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. */ -void GC_init(); /* Initialize collector. */ +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. */ @@ -1361,10 +1548,10 @@ ptr_t GC_allocobj(/* sz_inn_words, kind */); /* head. */ void GC_init_headers(); -bool GC_install_header(/*h*/); +GC_bool GC_install_header(/*h*/); /* Install a header for block h. */ /* Return FALSE on failure. */ -bool GC_install_counts(/*h, sz*/); +GC_bool GC_install_counts(/*h, sz*/); /* Set up forwarding counts for block */ /* h of size sz. */ /* Return FALSE on failure. */ @@ -1379,8 +1566,6 @@ void GC_finalize(); /* Perform all indicated finalization actions */ /* Unreachable finalizable objects are enqueued */ /* for processing by GC_invoke_finalizers. */ /* Invoked with lock. */ -void GC_invoke_finalizers(); /* Run eligible finalizers. */ - /* Invoked without lock. */ void GC_add_to_heap(/*p, bytes*/); /* Add a HBLKSIZE aligned chunk to the heap. */ @@ -1393,13 +1578,17 @@ 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. */ /* Virtual dirty bit implementation: */ /* Each implementation exports the following: */ void GC_read_dirty(); /* Retrieve dirty bits. */ -bool GC_page_was_dirty(/* struct hblk * h */); +GC_bool GC_page_was_dirty(/* struct hblk * h */); /* Read retrieved dirty bits. */ -bool GC_page_was_ever_dirty(/* struct hblk * h */); +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 */ @@ -1409,13 +1598,13 @@ void GC_write_hint(/* struct hblk * h */); void GC_dirty_init(); /* Slow/general mark bit manipulation: */ -bool GC_is_marked(); +GC_API GC_bool GC_is_marked(); void GC_clear_mark_bit(); void GC_set_mark_bit(); /* Stubborn objects: */ void GC_read_changed(); /* Analogous to GC_read_dirty */ -bool GC_page_was_changed(/* h */); /* Analogous to GC_page_was_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(); @@ -1427,7 +1616,12 @@ void GC_print_static_roots(); void GC_dump(); /* Make arguments appear live to compiler */ -GC_API void GC_noop(); +# ifdef __WATCOMC__ + void GC_noop(void*, ...); +# else + GC_API void GC_noop(); +# endif + void GC_noop1(/* word arg */); /* Logging and diagnostic output: */ diff --git a/gc_watcom.asm b/gc_watcom.asm deleted file mode 100644 index 5131ab96..00000000 --- a/gc_watcom.asm +++ /dev/null @@ -1,51 +0,0 @@ - - name gc_watcom - -.386p - - extrn _edata : byte ; end of DATA (start of BSS) - extrn _end : byte ; end of BSS (start of STACK) - extrn __nullarea : word - - extrn "C",_STACKLOW : dword - extrn "C",_STACKTOP : dword - - -DGROUP group _DATA - -_DATA segment dword public 'DATA' -_DATA ends - -_TEXT segment para public use32 'CODE' - assume cs:_TEXT, ds:DGROUP, ss:DGROUP - - public Get_DATASTART - align 4 -Get_DATASTART proc near - - mov eax,offset DGROUP:__nullarea - ret - -Get_DATASTART endp - - public Get_DATAEND - align 4 -Get_DATAEND proc near - - mov eax,offset DGROUP:_end - ret - -Get_DATAEND endp - - public Get_STACKBOTTOM - align 4 -Get_STACKBOTTOM proc near - - mov eax,_STACKTOP - ret - -Get_STACKBOTTOM endp - -_TEXT ends - - end @@ -22,6 +22,11 @@ /* Machine specific parts contributed by various people. See README file. */ +/* First a unified test for Linux: */ +# if defined(linux) || defined(__linux__) +# define LINUX +# endif + /* Determine the machine type: */ # if defined(sun) && defined(mc68000) # define M68K @@ -90,7 +95,7 @@ # endif # define mach_type_known # endif -# if defined(sparc) && defined(unix) && !defined(sun) +# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) # define SPARC # define DRSNX # define mach_type_known @@ -102,7 +107,11 @@ # if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) /* The above test may need refinement */ # define I386 -# define SCO +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif # define mach_type_known # endif # if defined(_AUX_SOURCE) @@ -110,25 +119,31 @@ # 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(hppa) || defined(__hppa__) # define HP_PA # define mach_type_known # endif -# if defined(linux) && defined(i386) +# if defined(LINUX) && defined(i386) # define I386 -# define LINUX # define mach_type_known # endif -# if defined(linux) && defined(powerpc) +# if defined(LINUX) && defined(powerpc) # define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(linux) && defined(sparc) +# define SPARC # define LINUX # define mach_type_known # endif # if defined(__alpha) || defined(__alpha__) # define ALPHA -# if defined(linux) || defined(__linux__) -# define LINUX -# else +# if !defined(LINUX) # define OSF1 /* a.k.a Digital Unix */ # endif # define mach_type_known @@ -188,14 +203,17 @@ /* DGUX defined */ # define mach_type_known # endif -# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) +# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) # define I386 # define MSWIN32 /* or Win32s */ # define mach_type_known # endif # if defined(__DJGPP__) # define I386 -# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif # define mach_type_known # endif # if defined(__CYGWIN32__) @@ -248,7 +266,7 @@ /* I386 ==> Intel 386 */ /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ /* FREEBSD, THREE86BSD, MSWIN32, */ - /* BSDI, SUNOS5, NEXT variants) */ + /* BSDI,SUNOS5, NEXT, other variants) */ /* NS32K ==> Encore Multimax */ /* MIPS ==> R2000 or R3000 */ /* (RISCOS, ULTRIX variants) */ @@ -365,6 +383,29 @@ extern char etext; # define DATASTART ((ptr_t)(&etext)) # endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t)0xf0000000) +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + 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. */ + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif # ifdef SUNOS4 # define OS_TYPE "SUNOS4" extern char etext; @@ -492,7 +533,15 @@ # define HEAP_START DATAEND # endif # define PROC_VDB -# define HEURISTIC1 +/* HEURISTIC1 reportedly no longer works under 2.7. Thus we */ +/* switched to HEURISTIC2, eventhough it creates some debugging */ +/* issues. */ +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least one */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING # endif # ifdef SUNOS4 # define OS_TYPE "SUNOS4" @@ -511,6 +560,7 @@ # define DATASTART ((ptr_t)(*(int *)(TEXTSTART+0x4)+TEXTSTART)) # define MPROTECT_VDB # define HEURISTIC1 +# define DYNAMIC_LOADING # endif # ifdef DRSNX # define CPP_WORDSZ 32 @@ -520,8 +570,21 @@ # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) # define MPROTECT_VDB # define STACKBOTTOM ((ptr_t) 0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + Linux Sparc non elf ? +# endif + extern int _end; +# define DATAEND (&_end) +# define SVR4 +# define STACKBOTTOM ((ptr_t) 0xf0000000) # endif -# define DYNAMIC_LOADING # endif # ifdef I386 @@ -566,21 +629,40 @@ +((word)&etext & 0xfff)) # define STACKBOTTOM ((ptr_t) 0x7ffffffc) # endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext; +# define DATASTART ((ptr_t)(&etext)) +# define STACKBOTTOM ((ptr_t) 0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif # ifdef LINUX # define OS_TYPE "LINUX" # define STACKBOTTOM ((ptr_t)0xc0000000) -# define MPROTECT_VDB -# ifdef __ELF__ -# define DYNAMIC_LOADING -# endif + /* Appears to be 0xe0000000 for at least one 2.1.91 kernel. */ + /* Probably needs to be more flexible, but I don't yet */ + /* fully understand how flexible. */ +# if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif # ifdef __ELF__ # define DYNAMIC_LOADING # ifdef UNDEFINED /* includes ro data */ extern int _etext; # define DATASTART ((ptr_t)((((word) (&_etext)) + 0xfff) & ~0xfff)) # endif - extern char **__environ; -# define DATASTART ((ptr_t)(&__environ)) +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 + extern int __data_start; +# define DATASTART ((ptr_t)(&__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 */ @@ -589,6 +671,7 @@ /* 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 @@ -598,10 +681,22 @@ # endif # ifdef CYGWIN32 # define OS_TYPE "CYGWIN32" - extern int _bss_start__; -# define DATASTART ((ptr_t)&_bss_start__) - extern int _data_end__; -# define DATAEND ((ptr_t)&_data_end__) + extern int _data_start__; + extern int _data_end__; + extern int _bss_start__; + extern int _bss_end__; + /* For binutils 2.9.1, we have */ + /* DATASTART = _data_start__ */ + /* DATAEND = _bss_end__ */ + /* whereas for some earlier versions it was */ + /* DATASTART = _bss_start__ */ + /* DATAEND = _data_end__ */ + /* To get it right for both, we take the */ + /* minumum/maximum of the two. */ +# define MAX(x,y) ((x) > (y) ? (x) : (y)) +# define MIN(x,y) ((x) < (y) ? (x) : (y)) +# define DATASTART ((ptr_t) MIN(&_data_start__, &_bss_start__)) +# define DATAEND ((ptr_t) MAX(&_data_end__, &_bss_end__)) # undef STACK_GRAN # define STACK_GRAN 0x10000 # define HEURISTIC1 @@ -659,18 +754,18 @@ # endif # ifdef DOS4GW # define OS_TYPE "DOS4GW" - /* Get_DATASTART, Get_DATAEND, Get_STACKBOTTOM - * Defined in gc-watcom.asm - */ - extern char* Get_DATASTART (void); - extern char* Get_DATAEND (void); - extern char* Get_STACKBOTTOM (void); -# pragma aux Get_DATASTART "*" value [eax]; -# pragma aux Get_DATAEND "*" value [eax]; -# pragma aux Get_STACKBOTTOM "*" value [eax]; -# define DATASTART ((ptr_t) Get_DATASTART()) -# define STACKBOTTOM ((ptr_t) Get_STACKBOTTOM()) -# define DATAEND ((ptr_t) Get_DATAEND()) + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes + or does not precedes with undescore names of C-variables. + Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t) _STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t) &__nullarea) +# define DATAEND ((ptr_t) &_end) # endif # endif @@ -696,7 +791,7 @@ extern int _fdata; # define DATASTART ((ptr_t)(&_fdata)) # ifdef USE_MMAP -# define HEAP_START (ptr_t)0x40000000 +# define HEAP_START (ptr_t)0x30000000 # else # define HEAP_START DATASTART # endif @@ -718,9 +813,7 @@ # endif # ifdef IRIX5 # define OS_TYPE "IRIX5" -# ifndef IRIX_THREADS -# define MPROTECT_VDB -# endif +# define MPROTECT_VDB # ifdef _MIPS_SZPTR # define CPP_WORDSZ _MIPS_SZPTR # define ALIGNMENT (_MIPS_SZPTR/8) @@ -740,7 +833,7 @@ # define ALIGNMENT 4 # define DATASTART ((ptr_t)0x20000000) extern int errno; -# define STACKBOTTOM ((ptr_t)((ulong)&errno + 2*sizeof(int))) +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) # define DYNAMIC_LOADING /* For really old versions of AIX, this may have to be removed. */ # endif @@ -767,6 +860,7 @@ # define DYNAMIC_LOADING # include <unistd.h> # define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) + /* They misspelled the Posix macro? */ # endif # ifdef ALPHA @@ -775,6 +869,8 @@ # ifdef OSF1 # define OS_TYPE "OSF1" # define DATASTART ((ptr_t) 0x140000000) + extern _end; +# define DATAEND ((ptr_t) &_end) # define HEURISTIC2 /* Normally HEURISTIC2 is too conervative, since */ /* the text segment immediately follows the stack. */ @@ -798,9 +894,9 @@ # endif extern int _end; # define DATAEND (&_end) - /* As of 1.3.90, I couldn't find a way to retrieve the correct */ - /* fault address from a signal handler. */ - /* Hence MPROTECT_VDB is broken. */ +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ # endif # endif @@ -852,14 +948,14 @@ # endif # if defined(SVR4) && !defined(GETPAGESIZE) -# include <unistd.h> - int - GC_getpagesize() - { - return sysconf(_SC_PAGESIZE); - } +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) # endif + # ifndef GETPAGESIZE +# if defined(SUNOS5) || defined(IRIX5) +# include <unistd.h> +# endif # define GETPAGESIZE() getpagesize() # endif @@ -906,17 +1002,29 @@ # define DEFAULT_VDB # endif +# 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(PCR) || defined(SRC_M3) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS) +# if defined(PCR) || defined(SRC_M3) || \ + defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) # define THREADS # endif -# if defined(SPARC) +# if defined(SPARC) && !defined(LINUX) # define SAVE_CALL_CHAIN # define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ /* include assembly code to do it well. */ @@ -53,7 +53,6 @@ ptr_t GC_scratch_alloc(bytes) register word bytes; { register ptr_t result = scratch_free_ptr; - register word bytes_needed = bytes; # ifdef ALIGN_DOUBLE # define GRANULARITY (2 * sizeof(word)) @@ -71,7 +70,12 @@ register word bytes; if (bytes_to_get <= bytes) { /* Undo the damage, and get memory directly */ - ptr_t result = (ptr_t)GET_MEM(bytes); + bytes_to_get = bytes; +# ifdef USE_MMAP + bytes_to_get += GC_page_size - 1; + bytes_to_get &= ~(GC_page_size - 1); +# endif + result = (ptr_t)GET_MEM(bytes_to_get); scratch_free_ptr -= bytes; GC_scratch_last_end_ptr = result + bytes; return(result); @@ -82,7 +86,12 @@ register word bytes; GC_printf0("Out of memory - trying to allocate less\n"); # endif scratch_free_ptr -= bytes; - return((ptr_t)GET_MEM(bytes)); + bytes_to_get = bytes; +# ifdef USE_MMAP + bytes_to_get += GC_page_size - 1; + bytes_to_get &= ~(GC_page_size - 1); +# endif + return((ptr_t)GET_MEM(bytes_to_get)); } scratch_free_ptr = result; GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get; @@ -127,7 +136,7 @@ void GC_init_headers() /* Make sure that there is a bottom level index block for address addr */ /* Return FALSE on failure. */ -static bool get_index(addr) +static GC_bool get_index(addr) register word addr; { register word hi = @@ -168,7 +177,7 @@ register word addr; /* Install a header for block h. */ /* The header is uninitialized. */ /* Returns FALSE on failure. */ -bool GC_install_header(h) +GC_bool GC_install_header(h) register struct hblk * h; { hdr * result; @@ -180,7 +189,7 @@ register struct hblk * h; } /* Set up forwarding counts for block h of size sz */ -bool GC_install_counts(h, sz) +GC_bool GC_install_counts(h, sz) register struct hblk * h; register word sz; /* bytes */ { @@ -1,6 +1,6 @@ -/* Conditionally execute a command based on machine and OS from config.h */ -/* Boehm, November 21, 1994 1:40 pm PST */ -# include "config.h" +/* Conditionally execute a command based on machine and OS from gcconfig.h */ + +# include "gcconfig.h" # include <stdio.h> int main(argc, argv, envp) diff --git a/if_not_there.c b/if_not_there.c index 9616309d..a93795f1 100644 --- a/if_not_there.c +++ b/if_not_there.c @@ -1,6 +1,6 @@ /* Conditionally execute a command based if the file argv[1] doesn't exist */ /* Except for execvp, we stick to ANSI C. */ -# include "config.h" +# include "gcconfig.h" # include <stdio.h> int main(argc, argv, envp) diff --git a/include/gc.h b/include/gc.h index 1e00c2f5..3b9f9ce2 100644 --- a/include/gc.h +++ b/include/gc.h @@ -36,11 +36,19 @@ #endif #if defined(_MSC_VER) && defined(_DLL) -#ifdef GC_BUILD -#define GC_API __declspec(dllexport) -#else -#define GC_API __declspec(dllimport) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif #endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif #endif #ifndef GC_API @@ -126,7 +134,19 @@ GC_API GC_word GC_max_retries; /* reporting out of memory after heap */ /* expansion fails. Initially 0. */ - + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + /* Public procedures */ /* * general purpose allocation routines, with roughly malloc calling conv. @@ -193,8 +213,8 @@ GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); /* If the argument is stubborn, the result will have changes enabled. */ /* It is an error to have changes enabled for the original object. */ /* Follows ANSI comventions for NULL old_object. */ -GC_API GC_PTR GC_realloc GC_PROTO((GC_PTR old_object, - size_t new_size_in_bytes)); +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); /* Explicitly increase the heap size. */ /* Returns 0 on failure, 1 on success. */ @@ -293,37 +313,48 @@ GC_API int GC_collect_a_little GC_PROTO((void)); GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#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 +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + /* Debugging (annotated) allocation. GC_gcollect will check */ /* objects allocated in this way for overwrites, etc. */ GC_API GC_PTR GC_debug_malloc - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_atomic - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_uncollectable - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_stubborn - GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int)); + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); GC_API GC_PTR GC_debug_realloc GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, - char * descr_string, int descr_int)); + GC_EXTRA_PARAMS)); GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); # ifdef GC_DEBUG -# define GC_MALLOC(sz) GC_debug_malloc(sz, __FILE__, __LINE__) -# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, __FILE__, __LINE__) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) # define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ - __FILE__, __LINE__) -# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, __FILE__, \ - __LINE__) + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) # define GC_FREE(p) GC_debug_free(p) # define GC_REGISTER_FINALIZER(p, f, d, of, od) \ 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_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, __FILE__, \ - __LINE__) +# 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) # define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ @@ -473,6 +504,14 @@ 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)); +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 */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* FINALIZE_ON_DEMAND is defined, it must be called */ + /* explicitly. */ + /* GC_set_warn_proc can be used to redirect or filter warning messages. */ /* p may not be a NULL pointer. */ typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); @@ -585,6 +624,10 @@ 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. */ @@ -624,7 +667,7 @@ GC_API void (*GC_is_visible_print_proc) # endif /* SOLARIS_THREADS */ -#ifdef IRIX_THREADS +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) /* We treat these similarly. */ # include <pthread.h> # include <signal.h> @@ -639,9 +682,12 @@ GC_API void (*GC_is_visible_print_proc) # define pthread_sigmask GC_pthread_sigmask # define pthread_join GC_pthread_join -#endif /* IRIX_THREADS */ +#endif /* IRIX_THREADS || LINUX_THREADS */ -#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_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. */ @@ -650,7 +696,7 @@ GC_PTR GC_malloc_many(size_t lb); /* in returned list. */ extern void GC_thr_init(); /* Needed for Solaris/X86 */ -#endif /* SOLARIS_THREADS */ +#endif /* THREADS && !SRC_M3 */ /* * If you are planning on putting @@ -672,12 +718,11 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif #endif -#ifdef __WATCOMC__ - /* Ivan Demakov: Programs compiled by Watcom C with -5r option - * crash without this declaration - * HB: Could this go into gc_priv.h? - */ - void GC_noop(void*, ...); +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); #endif #ifdef __cplusplus diff --git a/include/gc_alloc.h b/include/gc_alloc.h index 85cb7cdb..1d912db2 100644 --- a/include/gc_alloc.h +++ b/include/gc_alloc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -32,6 +32,8 @@ #define GC_ALLOC_H #define __ALLOC_H // Prevent inclusion of the default version. Ugly. +#define __SGI_STL_ALLOC_H +#define __SGI_STL_INTERNAL_ALLOC_H #ifndef __ALLOC # define __ALLOC alloc @@ -304,8 +306,8 @@ class gc_alloc_template { static void * allocate(size_t n) { return GC_malloc(n); } static void * ptr_free_allocate(size_t n) { return GC_malloc_atomic(n); } - static void deallocate(void *p, size_t n) { } - static void ptr_free_deallocate(void *p, size_t n) { } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } }; typedef gc_alloc_template < 0 > gc_alloc; @@ -316,8 +318,8 @@ class alloc_template { static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } static void * ptr_free_allocate(size_t n) { return GC_malloc_atomic_uncollectable(n); } - static void deallocate(void *p, size_t n) { GC_free(p); } - static void ptr_free_deallocate(void *p, size_t n) { GC_free(p); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } }; typedef alloc_template < 0 > alloc; @@ -367,6 +369,12 @@ __GC_SPECIALIZE(unsigned, single_client_alloc) __GC_SPECIALIZE(float, single_client_alloc) __GC_SPECIALIZE(double, single_client_alloc) +#ifdef __STL_USE_STD_ALLOCATORS + +???copy stuff from stl_alloc.h or remove it to a different file ??? + +#endif /* __STL_USE_STD_ALLOCATORS */ + #endif /* _SGI_SOURCE */ #endif /* GC_ALLOC_H */ diff --git a/include/gc_cpp.h b/include/gc_cpp.h index e2f456fb..a591d368 100644 --- a/include/gc_cpp.h +++ b/include/gc_cpp.h @@ -133,7 +133,8 @@ uses explicit invocation. #endif #if ! defined( OPERATOR_NEW_ARRAY ) \ - && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6)) + && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6) \ + || __WATCOMC__ >= 1050) # define OPERATOR_NEW_ARRAY #endif diff --git a/include/javaxfc.h b/include/javaxfc.h new file mode 100644 index 00000000..880020c5 --- /dev/null +++ b/include/javaxfc.h @@ -0,0 +1,41 @@ +# ifndef GC_H +# include "gc.h" +# endif + +/* + * Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * This is problematic for several reasons: + * 1) It means that finalizers, and all methods calle by them, + * must be prepared to deal with objects that have been finalized in + * spite of the fact that they are still referenced by statically + * allocated pointer variables. + * 1) It may mean that we get stuck in an infinite loop running + * finalizers which create new finalizable objects, though that's + * probably unlikely. + * Thus this is not recommended for general use. + */ +void GC_finalize_all(); + +/* + * A version of GC_register_finalizer that allows the object to be + * finalized before the objects it references. This is again error + * prone, in that it makes it easy to accidentally reference finalized + * objects. Again, recommended only for JVM implementors. + */ +void GC_register_finalizer_no_order(GC_PTR obj, + GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR * ocd); + +void GC_debug_register_finalizer_no_order(GC_PTR obj, + GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR * ocd); + +#ifdef GC_DEBUG +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) +#else +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) +#endif diff --git a/include/new_gc_alloc.h b/include/new_gc_alloc.h new file mode 100644 index 00000000..57713883 --- /dev/null +++ b/include/new_gc_alloc.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. 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 a revision of gc_alloc.h for SGI STL versions > 3.0 +// Unlike earlier versions, it supplements the standard "alloc.h" +// instead of replacing it. +// +// This is sloppy about variable names used in header files. +// 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. +// +// Some of this could be faster in the explicit deallocation case. +// In particular, we spend too much time clearing objects on the +// free lists. That could be avoided. +// +// This uses template classes with static members, and hence does not work +// with g++ 2.7.2 and earlier. +// +// Unlike its predecessor, this one simply defines +// gc_alloc +// single_client_gc_alloc +// traceable_alloc +// single_client_traceable_alloc +// +// It does not redefine alloc. Nor does it change the default allocator, +// though the user may wish to do so. (The argument against changing +// the default allocator is that it may introduce subtle link compatibility +// problems. The argument for changing it is that the usual default +// allocator is usually a very bad choice for a garbage collected environment.) +// + +#ifndef GC_ALLOC_H + +#include "gc.h" +#include <alloc.h> + +#define GC_ALLOC_H + +#include <stddef.h> +#include <string.h> + +// The following need to match collector data structures. +// We can't include gc_priv.h, since that pulls in way too much stuff. +// This should eventually be factored out into another include file. + +extern "C" { + extern void ** const GC_objfreelist_ptr; + extern void ** const GC_aobjfreelist_ptr; + extern void ** const GC_uobjfreelist_ptr; + extern void ** const GC_auobjfreelist_ptr; + + extern void GC_incr_words_allocd(size_t words); + extern void GC_incr_mem_freed(size_t words); + + extern char * GC_generic_malloc_words_small(size_t word, int kind); +} + +// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and +// AUNCOLLECTABLE in gc_priv.h. + +enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2, + GC_AUNCOLLECTABLE = 3 }; + +enum { GC_max_fast_bytes = 255 }; + +enum { GC_bytes_per_word = sizeof(char *) }; + +enum { GC_byte_alignment = 8 }; + +enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word }; + +inline void * &GC_obj_link(void * p) +{ return *(void **)p; } + +// Compute a number of words >= n+1 bytes. +// The +1 allows for pointers one past the end. +inline size_t GC_round_up(size_t n) +{ + return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment; +} + +// The same but don't allow for extra byte. +inline size_t GC_round_up_uncollectable(size_t n) +{ + return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment; +} + +template <int dummy> +class GC_aux_template { +public: + // File local count of allocated words. Occasionally this is + // added into the global count. A separate count is necessary since the + // real one must be updated with a procedure call. + static size_t GC_words_recently_allocd; + + // Same for uncollectable mmory. Not yet reflected in either + // GC_words_recently_allocd or GC_non_gc_bytes. + static size_t GC_uncollectable_words_recently_allocd; + + // Similar counter for explicitly deallocated memory. + static size_t GC_mem_recently_freed; + + // Again for uncollectable memory. + static size_t GC_uncollectable_mem_recently_freed; + + static void * GC_out_of_line_malloc(size_t nwords, int kind); +}; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_mem_recently_freed = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_mem_recently_freed = 0; + +template <int dummy> +void * GC_aux_template<dummy>::GC_out_of_line_malloc(size_t nwords, int kind) +{ + GC_words_recently_allocd += GC_uncollectable_words_recently_allocd; + GC_non_gc_bytes += + GC_bytes_per_word * GC_uncollectable_words_recently_allocd; + GC_uncollectable_words_recently_allocd = 0; + + GC_mem_recently_freed += GC_uncollectable_mem_recently_freed; + GC_non_gc_bytes -= + GC_bytes_per_word * GC_uncollectable_mem_recently_freed; + GC_uncollectable_mem_recently_freed = 0; + + GC_incr_words_allocd(GC_words_recently_allocd); + GC_words_recently_allocd = 0; + + GC_incr_mem_freed(GC_mem_recently_freed); + GC_mem_recently_freed = 0; + + return GC_generic_malloc_words_small(nwords, kind); +} + +typedef GC_aux_template<0> GC_aux; + +// A fast, single-threaded, garbage-collected allocator +// We assume the first word will be immediately overwritten. +// In this version, deallocation is not a noop, and explicit +// deallocation is likely to help performance. +template <int dummy> +class single_client_gc_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc(n); + flh = GC_objfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + flh = GC_aobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_objfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + memset((char *)p + GC_bytes_per_word, 0, + GC_bytes_per_word * (nwords - 1)); + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_aobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_gc_alloc_template<0> single_client_gc_alloc; + +// Once more, for uncollectable objects. +template <int dummy> +class single_client_traceable_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + flh = GC_uobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + flh = GC_auobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_uobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_auobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_traceable_alloc_template<0> single_client_traceable_alloc; + +template < int dummy > +class gc_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic(n); } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } +}; + +typedef gc_alloc_template < 0 > gc_alloc; + +template < int dummy > +class traceable_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic_uncollectable(n); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } +}; + +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. + +# define __GC_SPECIALIZE(T,alloc) \ +class simple_alloc<T, alloc> { \ +public: \ + static T *allocate(size_t n) \ + { return 0 == n? 0 : \ + (T*) alloc::ptr_free_allocate(n * sizeof (T)); } \ + static T *allocate(void) \ + { return (T*) alloc::ptr_free_allocate(sizeof (T)); } \ + static void deallocate(T *p, size_t n) \ + { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof (T)); } \ + static void deallocate(T *p) \ + { alloc::ptr_free_deallocate(p, sizeof (T)); } \ +}; + +__GC_SPECIALIZE(char, gc_alloc) +__GC_SPECIALIZE(int, gc_alloc) +__GC_SPECIALIZE(unsigned, gc_alloc) +__GC_SPECIALIZE(float, gc_alloc) +__GC_SPECIALIZE(double, gc_alloc) + +__GC_SPECIALIZE(char, traceable_alloc) +__GC_SPECIALIZE(int, traceable_alloc) +__GC_SPECIALIZE(unsigned, traceable_alloc) +__GC_SPECIALIZE(float, traceable_alloc) +__GC_SPECIALIZE(double, traceable_alloc) + +__GC_SPECIALIZE(char, single_client_gc_alloc) +__GC_SPECIALIZE(int, single_client_gc_alloc) +__GC_SPECIALIZE(unsigned, single_client_gc_alloc) +__GC_SPECIALIZE(float, single_client_gc_alloc) +__GC_SPECIALIZE(double, single_client_gc_alloc) + +__GC_SPECIALIZE(char, single_client_traceable_alloc) +__GC_SPECIALIZE(int, single_client_traceable_alloc) +__GC_SPECIALIZE(unsigned, single_client_traceable_alloc) +__GC_SPECIALIZE(float, single_client_traceable_alloc) +__GC_SPECIALIZE(double, single_client_traceable_alloc) + +#ifdef __STL_USE_STD_ALLOCATORS + +__STL_BEGIN_NAMESPACE + +template <class _T> +struct _Alloc_traits<_T, gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, gc_alloc > _Alloc_type; + typedef __allocator<_T, gc_alloc > allocator_type; +}; + +inline bool operator==(const gc_alloc&, + const gc_alloc&) +{ + return true; +} + +inline bool operator!=(const gc_alloc&, + const gc_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, single_client_gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, single_client_gc_alloc > _Alloc_type; + typedef __allocator<_T, single_client_gc_alloc > allocator_type; +}; + +inline bool operator==(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, traceable_alloc > _Alloc_type; + typedef __allocator<_T, traceable_alloc > allocator_type; +}; + +inline bool operator==(const traceable_alloc&, + const traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const traceable_alloc&, + const traceable_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, single_client_traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, single_client_traceable_alloc > _Alloc_type; + typedef __allocator<_T, single_client_traceable_alloc > allocator_type; +}; + +inline bool operator==(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return false; +} + +__STL_END_NAMESPACE + +#endif /* __STL_USE_STD_ALLOCATORS */ + +#endif /* _SGI_SOURCE */ + +#endif /* GC_ALLOC_H */ diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 49a916d0..46184522 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -42,29 +42,14 @@ typedef GC_word word; typedef GC_signed_word signed_word; # ifndef CONFIG_H -# include "config.h" +# include "gcconfig.h" # endif # ifndef HEADERS_H # include "gc_hdrs.h" # endif -# if !defined(bool) && !defined(__cplusplus) - typedef int bool; - /* This is problematic with C++ implementations that do not define bool. */ - /* By now they should. */ -# else -# if defined(_SGI_SOURCE) && !defined(_BOOL) - typedef int bool; -# endif -# if defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x410 - typedef int bool; -# endif -# if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1020 - /* Visual C++ 4.2 does not have bool type. */ - typedef int bool; -# endif -# endif +typedef int GC_bool; # define TRUE 1 # define FALSE 0 @@ -148,16 +133,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ #define PRINTBLOCKS /* Print object sizes associated with heap blocks, */ /* whether the objects are atomic or composite, and */ /* whether or not the block was found to be empty */ - /* duing the reclaim phase. Typically generates */ + /* during the reclaim phase. Typically generates */ /* about one screenful per garbage collection. */ #undef PRINTBLOCKS -#define PRINTBLACKLIST /* Print black listed blocks, i.e. values that */ - /* cause the allocator to avoid allocating certain */ - /* blocks in order to avoid introducing "false */ - /* hits". */ -#undef PRINTBLACKLIST - #ifdef SILENT # ifdef PRINTSTATS # undef PRINTSTATS @@ -174,6 +153,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() +#endif + #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 */ @@ -185,6 +170,15 @@ 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. */ +#endif + /* ALIGN_DOUBLE requires MERGE_SIZES at present. */ # if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) # define MERGE_SIZES @@ -197,6 +191,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # 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 */ # else # define MINHINCR 64 @@ -224,6 +219,8 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* */ /*********************************/ +#ifdef SAVE_CALL_CHAIN + /* * Number of frames and arguments to save in objects allocated by * debugging allocator. @@ -232,12 +229,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* alignment reasons. */ # define NARGS 2 /* Mumber of arguments to save for each call. */ - -#ifdef SAVE_CALL_CHAIN - struct callinfo { - word ci_pc; - word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ - }; +# define NEED_CALLINFO /* Fill in the pc and argument information for up to NFRAMES of my */ /* callers. Ignore my frame and my callers frame. */ @@ -245,6 +237,27 @@ void GC_save_callers (/* struct callinfo info[NFRAMES] */); void GC_print_callers (/* struct callinfo info[NFRAMES] */); +#else + +# ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +# endif + +#endif + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if defined(ALIGN_DOUBLE) && (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; #endif @@ -321,6 +334,11 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); /* HBLKSIZE aligned allocation. 0 is taken to mean failure */ /* space is assumed to be cleared. */ +/* In the case os USE_MMAP, the argument must also be a */ +/* physical page size. */ +/* GET_MEM is currently not assumed to retrieve 0 filled space, */ +/* though we should perhaps take advantage of the case in which */ +/* does. */ # ifdef PCR char * real_malloc(); # define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ @@ -416,12 +434,59 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define LOCK() mutex_lock(&GC_allocate_ml); # define UNLOCK() mutex_unlock(&GC_allocate_ml); # endif -# ifdef IRIX_THREADS +# 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)) -# define __test_and_set(l,v) test_and_set(l,v) +# 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) */ @@ -439,20 +504,27 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # define LOCK() pthread_mutex_lock(&GC_allocate_ml) # define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) # else -# define LOCK() { if (__test_and_set(&GC_allocate_lock, 1)) GC_lock(); } -# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) +# 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 -# define UNLOCK() GC_allocate_lock = 0 + /* 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 bool GC_collecting; + extern GC_bool GC_collecting; # define ENTER_GC() \ { \ GC_collecting = 1; \ } # define EXIT_GC() GC_collecting = 0; -# endif +# endif /* IRIX_THREADS || IRIX_JDK_THREADS */ # ifdef WIN32_THREADS # include <windows.h> GC_API CRITICAL_SECTION GC_allocate_ml; @@ -505,7 +577,8 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); # else # if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ - || defined(NO_SIGNALS) || defined(IRIX_THREADS) + || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ + || defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS) /* Also useful for debugging. */ /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ # define DISABLE_SIGNALS() @@ -532,7 +605,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */); PCR_allSigsBlocked, \ PCR_waitForever); # else -# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS) +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) void GC_stop_world(); void GC_start_world(); # define STOP_WORLD() GC_stop_world() @@ -801,7 +876,69 @@ struct hblk { /* Object free list link */ # define obj_link(p) (*(ptr_t *)(p)) -/* lists of all heap blocks and free lists */ +/* 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) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 4096 +# else +# ifdef PCR +# define MAX_ROOT_SETS 1024 +# else +# ifdef MSWIN32 +# define MAX_ROOT_SETS 512 + /* Under NT, we add only written pages, which can result */ + /* in many small root sets. */ +# else +# define MAX_ROOT_SETS 64 +# endif +# endif +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start; + ptr_t r_end; +# ifndef MSWIN32 + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#ifndef MSWIN32 + /* 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 */ +#endif + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ /* These are grouped together in a struct */ /* so that they can be easily skipped by the */ /* GC_mark routine. */ @@ -841,7 +978,10 @@ struct _GC_arrays { word _mem_freed; /* Number of explicitly deallocated words of memory */ /* since last collection. */ - + 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]; /* free list for objects */ ptr_t _aobjfreelist[MAXOBJSZ+1]; @@ -923,17 +1063,24 @@ struct _GC_arrays { /* GC_modws_valid_offsets[i%sizeof(word)] */ # endif # ifdef STUBBORN_ALLOC - page_hash_table _changed_pages; + page_hash_table _changed_pages; /* Stubborn object pages that were changes since last call to */ /* GC_read_changed. */ - page_hash_table _prev_changed_pages; + page_hash_table _prev_changed_pages; /* Stubborn object pages that were changes before last call to */ /* GC_read_changed. */ # endif # if defined(PROC_VDB) || defined(MPROTECT_VDB) - page_hash_table _grungy_pages; /* Pages that were dirty at last */ + page_hash_table _grungy_pages; /* Pages that were dirty at last */ /* GC_read_dirty. */ # endif +# ifdef MPROTECT_VDB + VOLATILE page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# ifdef PROC_VDB + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif # ifdef LARGE_CONFIG # if CPP_WORDSZ > 32 # define MAX_HEAP_SECTS 4096 /* overflows at roughly 64 GB */ @@ -950,6 +1097,11 @@ struct _GC_arrays { ptr_t _heap_bases[MAX_HEAP_SECTS]; /* Start address of memory regions obtained from kernel. */ # endif + struct roots _static_roots[MAX_ROOT_SETS]; +# ifndef MSWIN32 + struct roots * _root_index[RT_SIZE]; +# endif + struct exclusion _excl_table[MAX_EXCLUSIONS]; /* Block header index; see gc_headers.h */ bottom_index * _all_nils; bottom_index * _top_index [TOP_SZ]; @@ -986,6 +1138,7 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # 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_mark_procs GC_arrays._mark_procs # define GC_heapsize GC_arrays._heapsize # define GC_max_heapsize GC_arrays._max_heapsize # define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc @@ -994,11 +1147,20 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # ifdef MSWIN32 # define GC_heap_bases GC_arrays._heap_bases # 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 # define GC_all_nils GC_arrays._all_nils # define GC_top_index GC_arrays._top_index # if defined(PROC_VDB) || defined(MPROTECT_VDB) # define GC_grungy_pages GC_arrays._grungy_pages # endif +# ifdef MPROTECT_VDB +# define GC_dirty_pages GC_arrays._dirty_pages +# endif +# ifdef PROC_VDB +# define GC_written_pages GC_arrays._written_pages +# endif # ifdef GATHERSTATS # define GC_composite_in_use GC_arrays._composite_in_use # define GC_atomic_in_use GC_arrays._atomic_in_use @@ -1010,11 +1172,9 @@ 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)) -GC_API word GC_fo_entries; - +/* Object kinds: */ # define MAXOBJKINDS 16 -/* Object kinds: */ extern struct obj_kind { ptr_t *ok_freelist; /* Array of free listheaders for this kind of object */ /* Point either to GC_arrays or to storage allocated */ @@ -1024,12 +1184,18 @@ extern struct obj_kind { /* swept. */ word ok_descriptor; /* Descriptor template for objects in this */ /* block. */ - bool ok_relocate_descr; + GC_bool ok_relocate_descr; /* Add object size in bytes to descriptor */ /* template to obtain descriptor. Otherwise */ /* template is used as is. */ - bool ok_init; /* Clear objects before putting them on the free list. */ + 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 end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ + endGC_obj_kinds : endGC_arrays) + /* Predefined kinds: */ # define PTRFREE 0 # define NORMAL 1 @@ -1045,6 +1211,8 @@ extern struct obj_kind { extern int GC_n_kinds; +GC_API word GC_fo_entries; + extern word GC_n_heap_sects; /* Number of separately added heap */ /* sections. */ @@ -1074,24 +1242,27 @@ extern struct hblk * GC_hblkfreelist; /* header structure associated with */ /* block. */ -extern bool GC_is_initialized; /* GC_init() has been run. */ +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ -extern bool GC_objects_are_marked; /* There are marked objects in */ +extern GC_bool GC_objects_are_marked; /* There are marked objects in */ /* the heap. */ -extern int GC_incremental; /* Using incremental/generational collection. */ +#ifndef SMALL_CONFIG + extern GC_bool GC_incremental; + /* Using incremental/generational collection. */ +#else +# define GC_incremental TRUE + /* Hopefully allow optimizer to remove some code. */ +#endif -extern bool GC_dirty_maintained;/* Dirty bits are being maintained, */ +extern GC_bool GC_dirty_maintained; + /* Dirty bits are being maintained, */ /* either for incremental collection, */ /* or to limit the root set. */ -# ifndef PCR - extern ptr_t GC_stackbottom; /* Cool end of user stack */ -# endif - extern word GC_root_size; /* Total size of registered root sections */ -extern bool GC_debugging_started; /* GC_debug_malloc has been called. */ +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; @@ -1110,7 +1281,7 @@ extern ptr_t GC_greatest_plausible_heap_addr; /* object are used. */ -/* Mark bit perations */ +/* Mark bit operations */ /* * Retrieve, set, clear the mark bit corresponding @@ -1130,6 +1301,13 @@ extern ptr_t GC_greatest_plausible_heap_addr; /* 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. */ @@ -1144,8 +1322,8 @@ void GC_invalidate_mark_state(); /* Tell the marker that marked */ void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ /* Return after about one pages worth of */ /* work. */ -bool GC_mark_stack_empty(); -bool GC_mark_some(); /* Perform about one pages worth of marking */ +GC_bool GC_mark_stack_empty(); +GC_bool GC_mark_some(); /* 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. */ @@ -1159,7 +1337,7 @@ void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ /* subintervals of [b,t) onto */ /* mark stack. */ #ifndef SMALL_CONFIG - void GC_push_conditional(/* ptr_t b, ptr_t t, bool all*/); + void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); #else # define GC_push_conditional(b, t, all) GC_push_all(b, t) #endif @@ -1167,7 +1345,7 @@ void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ /* on the third arg. */ void GC_push_all_stack(/*b,t*/); /* As above, but consider */ /* interior pointers as valid */ -void GC_push_roots(/* bool all */); /* Push all or dirty roots. */ +void GC_push_roots(/* GC_bool all */); /* Push all or dirty roots. */ extern void (*GC_push_other_roots)(); /* Push system or application specific roots */ /* onto the mark stack. In some environments */ @@ -1183,10 +1361,7 @@ extern void (*GC_start_call_back)(/* void */); 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. */ -void GC_push_one(/*p*/); /* If p points to an object, mark it */ - /* and push contents on the mark stack */ -/* Ivan Demakov: Watcom C error'ed without this */ -# if defined(MSWIN32) && defined(__WATCOMC__) +# if defined(MSWIN32) void __cdecl GC_push_one(); # else void GC_push_one(/*p*/); /* If p points to an object, mark it */ @@ -1207,12 +1382,12 @@ 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. */ -bool GC_stopped_mark(); /* Stop world and mark from all roots */ +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(); -bool GC_is_static_root(/* ptr_t p */); +GC_bool GC_is_static_root(/* ptr_t p */); /* Is the address p in one of the registered static */ /* root sections? */ void GC_register_dynamic_libraries(); @@ -1225,15 +1400,27 @@ void GC_register_data_segments(); /* Black listing: */ void GC_bl_init(); # ifndef ALL_INTERIOR_POINTERS - void GC_add_to_black_list_normal(/* bits */); + void GC_add_to_black_list_normal(/* bits, maybe source */); /* Register bits as a possible future false */ /* reference from the heap or static data */ -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits) GC_add_to_black_list_normal(bits) +# 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 # else -# define GC_ADD_TO_BLACK_LIST_NORMAL(bits) GC_add_to_black_list_stack(bits) +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits) +# endif # endif -void GC_add_to_black_list_stack(/* bits */); +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 */ @@ -1262,7 +1449,7 @@ void GC_invalidate_map(/* hdr */); /* with the block. This identifies */ /* the block as invalid to the mark */ /* routines. */ -bool GC_add_map_entry(/*sz*/); +GC_bool GC_add_map_entry(/*sz*/); /* Add a heap block map for objects of */ /* size sz to obj_map. */ /* Return FALSE on failure. */ @@ -1286,7 +1473,7 @@ void GC_freehblk(); /* Deallocate a heap block and mark it */ /* Misc GC: */ void GC_init_inner(); -bool GC_expand_hp_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 */ @@ -1302,12 +1489,12 @@ void GC_reclaim_or_delete_all(); /* Arrange for all reclaim lists to be */ /* empty. Judiciously choose between */ /* sweeping and discarding each page. */ -bool GC_reclaim_all(/* GC_stop_func f*/); +GC_bool GC_reclaim_all(/* GC_stop_func f*/); /* Reclaim all blocks. Abort (in a */ /* consistent state) if f returns TRUE. */ -bool GC_block_empty(/* hhdr */); /* Block completely unmarked? */ -bool GC_never_stop_func(); /* Returns FALSE. */ -bool GC_try_to_collect_inner(/* GC_stop_func f */); +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 */); /* Collect; caller must have acquired */ /* lock and disabled signals. */ /* Collection is aborted if f returns */ @@ -1317,13 +1504,13 @@ bool GC_try_to_collect_inner(/* GC_stop_func f */); (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. */ -bool GC_collect_or_expand(/* needed_blocks */); +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. */ -void GC_init(); /* Initialize collector. */ +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. */ @@ -1361,10 +1548,10 @@ ptr_t GC_allocobj(/* sz_inn_words, kind */); /* head. */ void GC_init_headers(); -bool GC_install_header(/*h*/); +GC_bool GC_install_header(/*h*/); /* Install a header for block h. */ /* Return FALSE on failure. */ -bool GC_install_counts(/*h, sz*/); +GC_bool GC_install_counts(/*h, sz*/); /* Set up forwarding counts for block */ /* h of size sz. */ /* Return FALSE on failure. */ @@ -1379,8 +1566,6 @@ void GC_finalize(); /* Perform all indicated finalization actions */ /* Unreachable finalizable objects are enqueued */ /* for processing by GC_invoke_finalizers. */ /* Invoked with lock. */ -void GC_invoke_finalizers(); /* Run eligible finalizers. */ - /* Invoked without lock. */ void GC_add_to_heap(/*p, bytes*/); /* Add a HBLKSIZE aligned chunk to the heap. */ @@ -1393,13 +1578,17 @@ 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. */ /* Virtual dirty bit implementation: */ /* Each implementation exports the following: */ void GC_read_dirty(); /* Retrieve dirty bits. */ -bool GC_page_was_dirty(/* struct hblk * h */); +GC_bool GC_page_was_dirty(/* struct hblk * h */); /* Read retrieved dirty bits. */ -bool GC_page_was_ever_dirty(/* struct hblk * h */); +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 */ @@ -1409,13 +1598,13 @@ void GC_write_hint(/* struct hblk * h */); void GC_dirty_init(); /* Slow/general mark bit manipulation: */ -bool GC_is_marked(); +GC_API GC_bool GC_is_marked(); void GC_clear_mark_bit(); void GC_set_mark_bit(); /* Stubborn objects: */ void GC_read_changed(); /* Analogous to GC_read_dirty */ -bool GC_page_was_changed(/* h */); /* Analogous to GC_page_was_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(); @@ -1427,7 +1616,12 @@ void GC_print_static_roots(); void GC_dump(); /* Make arguments appear live to compiler */ -GC_API void GC_noop(); +# ifdef __WATCOMC__ + void GC_noop(void*, ...); +# else + GC_API void GC_noop(); +# endif + void GC_noop1(/* word arg */); /* Logging and diagnostic output: */ diff --git a/include/private/config.h b/include/private/gcconfig.h index 62492c3e..7fecb732 100644 --- a/include/private/config.h +++ b/include/private/gcconfig.h @@ -1,6 +1,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. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -11,7 +12,6 @@ * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ -/* Boehm, October 3, 1995 6:39 pm PDT */ #ifndef CONFIG_H @@ -22,6 +22,11 @@ /* Machine specific parts contributed by various people. See README file. */ +/* First a unified test for Linux: */ +# if defined(linux) || defined(__linux__) +# define LINUX +# endif + /* Determine the machine type: */ # if defined(sun) && defined(mc68000) # define M68K @@ -49,11 +54,11 @@ # endif # if defined(mips) || defined(__mips) # define MIPS -# if defined(ultrix) || defined(__ultrix) +# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) # define ULTRIX # else # if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) -# define IRIX5 +# define IRIX5 /* or IRIX 6.X */ # else # define RISCOS /* or IRIX 4.X */ # endif @@ -90,7 +95,7 @@ # endif # define mach_type_known # endif -# if defined(sparc) && defined(unix) && !defined(sun) +# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) # define SPARC # define DRSNX # define mach_type_known @@ -102,7 +107,11 @@ # if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) /* The above test may need refinement */ # define I386 -# define SCO +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif # define mach_type_known # endif # if defined(_AUX_SOURCE) @@ -110,17 +119,33 @@ # 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(hppa) || defined(__hppa__) # define HP_PA # define mach_type_known # endif -# if defined(linux) && defined(i386) +# if defined(LINUX) && defined(i386) # define I386 +# define mach_type_known +# endif +# if defined(LINUX) && defined(powerpc) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(linux) && defined(sparc) +# define SPARC # define LINUX # define mach_type_known # endif -# if defined(__alpha) +# if defined(__alpha) || defined(__alpha__) # define ALPHA +# if !defined(LINUX) +# define OSF1 /* a.k.a Digital Unix */ +# endif # define mach_type_known # endif # if defined(_AMIGA) @@ -178,14 +203,22 @@ /* DGUX defined */ # define mach_type_known # endif -# if defined(_MSDOS) && (_M_IX86 == 300) || (_M_IX86 == 400) +# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) # define I386 # define MSWIN32 /* or Win32s */ # define mach_type_known # endif -# if defined(GO32) +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) # define I386 -# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# define CYGWIN32 # define mach_type_known # endif # if defined(__BORLANDC__) @@ -193,6 +226,27 @@ # define MSWIN32 # define mach_type_known # endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +/* Ivan Demakov */ +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif /* Feel free to add more clauses here */ @@ -212,7 +266,7 @@ /* I386 ==> Intel 386 */ /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ /* FREEBSD, THREE86BSD, MSWIN32, */ - /* BSDI, SUNOS5, NEXT variants) */ + /* BSDI,SUNOS5, NEXT, other variants) */ /* NS32K ==> Encore Multimax */ /* MIPS ==> R2000 or R3000 */ /* (RISCOS, ULTRIX variants) */ @@ -225,9 +279,12 @@ /* SPARC ==> SPARC under SunOS */ /* (SUNOS4, SUNOS5, */ /* DRSNX variants) */ - /* ALPHA ==> DEC Alpha OSF/1 */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ /* M88K ==> Motorola 88XX0 */ /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ /* @@ -249,7 +306,12 @@ * * DATASTART is the beginning of the data segment. * On UNIX systems, the collector will scan the area between DATASTART - * and &end for root pointers. + * and DATAEND for root pointers. + * + * DATAEND, if not &end. + * + * ALIGN_DOUBLE of GC_malloc should return blocks aligned to twice + * the pointer size. * * STACKBOTTOM is the cool end of the stack, which is usually the * highest address in the stack. @@ -321,6 +383,29 @@ extern char etext; # define DATASTART ((ptr_t)(&etext)) # endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t)0xf0000000) +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + 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. */ + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif # ifdef SUNOS4 # define OS_TYPE "SUNOS4" extern char etext; @@ -334,6 +419,8 @@ # define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) # define STACKBOTTOM ((ptr_t) 0xffeffffc) /* empirically determined. seems to work. */ +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) # endif # ifdef SYSV # define OS_TYPE "SYSV" @@ -353,12 +440,15 @@ /* that the stack direction is incorrect. Two */ /* bytes down from 0x0 should be safe enough. */ /* --Parag */ +# include <sys/mmu.h> +# define GETPAGESIZE() PAGESIZE /* Is this still right? */ # endif # ifdef AMIGA # define OS_TYPE "AMIGA" /* STACKBOTTOM and DATASTART handled specially */ /* in os_dep.c */ # define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 # endif # ifdef MACOS # ifndef __LOWMEM__ @@ -368,6 +458,7 @@ /* see os_dep.c for details of global data segments. */ # define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) # define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 # endif # ifdef NEXT # define OS_TYPE "NEXT" @@ -389,6 +480,13 @@ # define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) # define DATAEND /* not needed */ # endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t)0x80000000) +# define DATASTART GC_data_start + extern int _end; +# define DATAEND (&_end) +# endif # endif # ifdef VAX @@ -417,6 +515,7 @@ # ifdef SPARC # define MACH_TYPE "SPARC" # define ALIGNMENT 4 /* Required by hardware */ +# define ALIGN_DOUBLE extern int etext; # ifdef SUNOS5 # define OS_TYPE "SUNOS5" @@ -425,8 +524,24 @@ extern char * GC_SysVGetDataStart(); # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) # define DATAEND (&_end) +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif # define PROC_VDB -# define HEURISTIC1 +/* HEURISTIC1 reportedly no longer works under 2.7. Thus we */ +/* switched to HEURISTIC2, eventhough it creates some debugging */ +/* issues. */ +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least one */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING # endif # ifdef SUNOS4 # define OS_TYPE "SUNOS4" @@ -445,6 +560,7 @@ # define DATASTART ((ptr_t)(*(int *)(TEXTSTART+0x4)+TEXTSTART)) # define MPROTECT_VDB # define HEURISTIC1 +# define DYNAMIC_LOADING # endif # ifdef DRSNX # define CPP_WORDSZ 32 @@ -454,8 +570,21 @@ # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) # define MPROTECT_VDB # define STACKBOTTOM ((ptr_t) 0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + Linux Sparc non elf ? +# endif + extern int _end; +# define DATAEND (&_end) +# define SVR4 +# define STACKBOTTOM ((ptr_t) 0xf0000000) # endif -# define DYNAMIC_LOADING # endif # ifdef I386 @@ -463,6 +592,11 @@ # define ALIGNMENT 4 /* Appears to hold for all "32 bit" compilers */ /* except Borland. The -a4 option fixes */ /* Borland. */ + /* Ivan Demakov: For Watcom the option is -zp4. */ +# ifndef SMALL_CONFIG +# define ALIGN_DOUBLE /* Not strictly necessary, but may give speed */ + /* improvement on Pentiums. */ +# endif # ifdef SEQUENT # define OS_TYPE "SEQUENT" extern int etext; @@ -475,7 +609,17 @@ extern char * GC_SysVGetDataStart(); # define DATASTART GC_SysVGetDataStart(0x1000, &etext) # define STACKBOTTOM ((ptr_t)(&_start)) -# define PROC_VDB +/** At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/*# define PROC_VDB*/ +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif # endif # ifdef SCO # define OS_TYPE "SCO" @@ -485,12 +629,77 @@ +((word)&etext & 0xfff)) # define STACKBOTTOM ((ptr_t) 0x7ffffffc) # endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext; +# define DATASTART ((ptr_t)(&etext)) +# define STACKBOTTOM ((ptr_t) 0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif # ifdef LINUX # define OS_TYPE "LINUX" - extern int etext; -# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) # define STACKBOTTOM ((ptr_t)0xc0000000) -# define MPROTECT_VDB + /* Appears to be 0xe0000000 for at least one 2.1.91 kernel. */ + /* Probably needs to be more flexible, but I don't yet */ + /* fully understand how flexible. */ +# if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# ifdef __ELF__ +# define DYNAMIC_LOADING +# ifdef UNDEFINED /* includes ro data */ + extern int _etext; +# define DATASTART ((ptr_t)((((word) (&_etext)) + 0xfff) & ~0xfff)) +# endif +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 + extern int __data_start; +# define DATASTART ((ptr_t)(&__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 CYGWIN32 +# define OS_TYPE "CYGWIN32" + extern int _data_start__; + extern int _data_end__; + extern int _bss_start__; + extern int _bss_end__; + /* For binutils 2.9.1, we have */ + /* DATASTART = _data_start__ */ + /* DATAEND = _bss_end__ */ + /* whereas for some earlier versions it was */ + /* DATASTART = _bss_start__ */ + /* DATAEND = _data_end__ */ + /* To get it right for both, we take the */ + /* minumum/maximum of the two. */ +# define MAX(x,y) ((x) > (y) ? (x) : (y)) +# define MIN(x,y) ((x) < (y) ? (x) : (y)) +# define DATASTART ((ptr_t) MIN(&_data_start__, &_bss_start__)) +# define DATAEND ((ptr_t) MAX(&_data_end__, &_bss_end__)) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 # endif # ifdef OS2 # define OS_TYPE "OS2" @@ -503,14 +712,20 @@ # define OS_TYPE "MSWIN32" /* STACKBOTTOM and DATASTART are handled specially in */ /* os_dep.c. */ -# define MPROTECT_VDB +# ifndef __WATCOMC__ +# define MPROTECT_VDB +# endif # define DATAEND /* not needed */ # endif # ifdef DJGPP # define OS_TYPE "DJGPP" +# include "stubinfo.h" extern int etext; -# define DATASTART ((ptr_t)(&etext)) -# define STACKBOTTOM ((ptr_t)0x00080000) + extern int _stklen; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x1ff) & ~0x1ff)) +# define STACKBOTTOM ((ptr_t)((word) _stubinfo + _stubinfo->size \ + + _stklen)) + /* This may not be right. */ # endif # ifdef FREEBSD # define OS_TYPE "FREEBSD" @@ -537,6 +752,21 @@ # define STACKBOTTOM ((ptr_t)0xc0000000) # define DATAEND /* not needed */ # endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes + or does not precedes with undescore names of C-variables. + Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t) _STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t) &__nullarea) +# define DATAEND ((ptr_t) &_end) +# endif # endif # ifdef NS32K @@ -553,25 +783,47 @@ # ifdef MIPS # define MACH_TYPE "MIPS" -# define ALIGNMENT 4 /* Required by hardware */ -# define DATASTART 0x10000000 +# ifndef IRIX5 +# define DATASTART (ptr_t)0x10000000 /* Could probably be slightly higher since */ - /* startup code allocates lots of junk */ + /* 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 OS_TYPE "RISCOS" +# define ALIGNMENT 4 /* Required by hardware */ # endif # ifdef IRIX5 # define OS_TYPE "IRIX5" -# define MPROTECT_VDB - /* The above is dubious. Mprotect and signals do work, */ - /* and dirty bits are implemented under IRIX5. But, */ - /* at least under IRIX5.2, mprotect seems to be so */ - /* slow relative to the hardware that incremental */ - /* collection is likely to be rarely useful. */ +# define MPROTECT_VDB +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# if CPP_WORDSZ != 64 +# define ALIGN_DOUBLE +# endif +# else +# define ALIGNMENT 4 +# define ALIGN_DOUBLE +# endif # define DYNAMIC_LOADING # endif # endif @@ -580,46 +832,104 @@ # define MACH_TYPE "RS6000" # define ALIGNMENT 4 # define DATASTART ((ptr_t)0x20000000) -# define STACKBOTTOM ((ptr_t)0x2ff80000) + extern int errno; +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ # endif # ifdef HP_PA # define MACH_TYPE "HP_PA" # define ALIGNMENT 4 +# define ALIGN_DOUBLE extern int __data_start; # define DATASTART ((ptr_t)(&__data_start)) -# define HEURISTIC2 +# 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 */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* 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 # ifdef ALPHA # define MACH_TYPE "ALPHA" # define ALIGNMENT 8 -# define DATASTART ((ptr_t) 0x140000000) -# define HEURISTIC2 +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t) 0x140000000) + extern _end; +# define DATAEND ((ptr_t) &_end) +# define HEURISTIC2 /* Normally HEURISTIC2 is too conervative, since */ /* the text segment immediately follows the stack. */ /* Hence we give an upper pound. */ - extern __start; -# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) -# define CPP_WORDSZ 64 -# define MPROTECT_VDB -# define DYNAMIC_LOADING + extern __start; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) +# define CPP_WORDSZ 64 +# define MPROTECT_VDB +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t) 0x120000000) +# ifdef __ELF__ + extern int __data_start; +# define DATASTART &__data_start +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x140000000) +# endif + extern int _end; +# define DATAEND (&_end) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif # endif # ifdef M88K # define MACH_TYPE "M88K" # define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int etext; # ifdef CX_UX +# define OS_TYPE "CX_UX" # define DATASTART ((((word)&etext + 0x3fffff) & ~0x3fffff) + 0x10000) # endif # ifdef DGUX +# define OS_TYPE "DGUX" extern char * GC_SysVGetDataStart(); # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) # endif # define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ # endif +# ifdef S370 +# define MACH_TYPE "S370" +# define OS_TYPE "UTS4" +# define ALIGNMENT 4 /* Required by hardware */ + 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 + # ifndef STACK_GROWS_UP # define STACK_GROWS_DOWN # endif @@ -637,7 +947,19 @@ # define DATAEND (&end) # endif -# if defined(SUNOS5) || defined(DRSNX) +# if defined(SVR4) && !defined(GETPAGESIZE) +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# endif + +# ifndef GETPAGESIZE +# if defined(SUNOS5) || defined(IRIX5) +# include <unistd.h> +# endif +# define GETPAGESIZE() getpagesize() +# endif + +# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) /* OS has SVR4 generic features. Probably others also qualify. */ # define SVR4 # endif @@ -680,8 +1002,32 @@ # define DEFAULT_VDB # endif -# if defined(SPARC) +# 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(PCR) || defined(SRC_M3) || \ + defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) +# define THREADS +# endif + +# if defined(SPARC) && !defined(LINUX) # define SAVE_CALL_CHAIN +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ # endif # endif diff --git a/irix_threads.c b/irix_threads.c index 5e191d11..5efca211 100644 --- a/irix_threads.c +++ b/irix_threads.c @@ -15,12 +15,17 @@ * Support code for Irix (>=6.2) Pthreads. This relies on properties * not guaranteed by the Pthread standard. It may or may not be portable * to other implementations. + * + * Note that there is a lot of code duplication between linux_threads.c + * and irix_threads.c; any changes made here may need to be reflected + * there too. */ # if defined(IRIX_THREADS) # include "gc_priv.h" # include <pthread.h> +# include <semaphore.h> # include <time.h> # include <errno.h> # include <unistd.h> @@ -65,6 +70,10 @@ typedef struct GC_Thread_Rep { /* guaranteed to be dead, but we may */ /* not yet have registered the join.) */ pthread_t id; + word stop; +# define NOT_STOPPED 0 +# define PLEASE_STOP 1 +# define STOPPED 2 word flags; # define FINISHED 1 /* Thread has exited. */ # define DETACHED 2 /* Thread is intended to be detached. */ @@ -90,7 +99,6 @@ GC_thread GC_lookup_thread(pthread_t id); # define SIG_SUSPEND (SIGRTMIN + 6) pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER; -volatile unsigned GC_n_stopped = 0; /* Number of threads stopped so far */ pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER; pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER; @@ -104,14 +112,19 @@ void GC_suspend_handler(int sig) int i; if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); - pthread_mutex_lock(&GC_suspend_lock); me = GC_lookup_thread(pthread_self()); /* The lookup here is safe, since I'm doing this on behalf */ /* of a thread which holds the allocation lock in order */ /* to stop the world. Thus concurrent modification of the */ /* data structure is impossible. */ + if (PLEASE_STOP != me -> stop) { + /* Misdirected signal. */ + pthread_mutex_unlock(&GC_suspend_lock); + return; + } + pthread_mutex_lock(&GC_suspend_lock); me -> stack_ptr = (ptr_t)(&dummy); - GC_n_stopped++; + me -> stop = STOPPED; pthread_cond_signal(&GC_suspend_ack_cv); pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock); pthread_mutex_unlock(&GC_suspend_lock); @@ -119,7 +132,7 @@ void GC_suspend_handler(int sig) } -bool GC_thr_initialized = FALSE; +GC_bool GC_thr_initialized = FALSE; size_t GC_min_stack_sz; @@ -190,7 +203,7 @@ GC_thread GC_new_thread(pthread_t id) int hv = ((word)id) % THREAD_TABLE_SZ; GC_thread result; static struct GC_Thread_Rep first_thread; - static bool first_thread_used = FALSE; + static GC_bool first_thread_used = FALSE; if (!first_thread_used) { result = &first_thread; @@ -204,7 +217,8 @@ GC_thread GC_new_thread(pthread_t id) result -> id = id; result -> next = GC_threads[hv]; GC_threads[hv] = result; - /* result -> flags = 0; */ + /* result -> flags = 0; */ + /* result -> stop = 0; */ return(result); } @@ -271,35 +285,52 @@ void GC_stop_world() pthread_t my_thread = pthread_self(); register int i; register GC_thread p; - register int n_live_threads = 0; register int result; + struct timespec timeout; - GC_n_stopped = 0; for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { if (p -> id != my_thread) { - if (p -> flags & FINISHED) continue; - n_live_threads++; + if (p -> flags & FINISHED) { + p -> stop = STOPPED; + continue; + } + p -> stop = PLEASE_STOP; result = pthread_kill(p -> id, SIG_SUSPEND); /* GC_printf1("Sent signal to 0x%x\n", p -> id); */ switch(result) { case ESRCH: /* Not really there anymore. Possible? */ - n_live_threads--; + p -> stop = STOPPED; break; case 0: break; default: - ABORT("thr_kill failed"); + ABORT("pthread_kill failed"); } } } } pthread_mutex_lock(&GC_suspend_lock); - while(GC_n_stopped < n_live_threads) { - /* GC_printf3("\nwaiting:%d %d %d\n", GC_gc_no, - GC_n_stopped, n_live_threads); */ - pthread_cond_wait(&GC_suspend_ack_cv, &GC_suspend_lock); + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + while (p -> id != my_thread && p -> stop != STOPPED) { + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_nsec += 50000000; /* 50 msecs */ + if (timeout.tv_nsec >= 1000000000) { + timeout.tv_nsec -= 1000000000; + ++timeout.tv_sec; + } + result = pthread_cond_timedwait(&GC_suspend_ack_cv, + &GC_suspend_lock, + &timeout); + if (result == ETIMEDOUT) { + /* Signal was lost or misdirected. Try again. */ + /* Duplicate signals should be benign. */ + result = pthread_kill(p -> id, SIG_SUSPEND); + } + } + } } pthread_mutex_unlock(&GC_suspend_lock); /* GC_printf1("World stopped 0x%x\n", pthread_self()); */ @@ -308,7 +339,15 @@ void GC_stop_world() /* Caller holds allocation lock. */ void GC_start_world() { + GC_thread p; + unsigned i; + /* GC_printf0("World starting\n"); */ + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + p -> stop = NOT_STOPPED; + } + } pthread_mutex_lock(&GC_suspend_lock); /* All other threads are at pthread_cond_wait in signal handler. */ /* Otherwise we couldn't have acquired the lock. */ @@ -336,8 +375,6 @@ int GC_is_thread_stack(ptr_t addr) } # endif -extern ptr_t GC_approx_sp(); - /* We hold allocation lock. We assume the world is stopped. */ void GC_push_all_stacks() { @@ -373,12 +410,21 @@ void GC_push_all_stacks() void GC_thr_init() { GC_thread t; + struct sigaction act; + if (GC_thr_initialized) return; GC_thr_initialized = TRUE; GC_min_stack_sz = HBLKSIZE; GC_page_sz = sysconf(_SC_PAGESIZE); - if (sigset(SIG_SUSPEND, GC_suspend_handler) != SIG_DFL) + (void) sigaction(SIG_SUSPEND, 0, &act); + if (act.sa_handler != SIG_DFL) ABORT("Previously installed SIG_SUSPEND handler"); + /* Install handler. */ + act.sa_handler = GC_suspend_handler; + act.sa_flags = SA_RESTART; + (void) sigemptyset(&act.sa_mask); + if (0 != sigaction(SIG_SUSPEND, &act, 0)) + ABORT("Failed to install SIG_SUSPEND handler"); /* Add the initial thread, so we can stop it. */ t = GC_new_thread(pthread_self()); t -> stack_size = 0; @@ -401,9 +447,14 @@ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) struct start_info { void *(*start_routine)(void *); void *arg; + word flags; + ptr_t stack; + size_t stack_size; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ }; -void GC_thread_exit_proc(void *dummy) +void GC_thread_exit_proc(void *arg) { GC_thread me; @@ -428,6 +479,9 @@ int GC_pthread_join(pthread_t thread, void **retval) /* cant have been recycled by pthreads. */ UNLOCK(); result = pthread_join(thread, retval); + /* Some versions of the Irix pthreads library can erroneously */ + /* return EINTR when the call succeeds. */ + if (EINTR == result) result = 0; LOCK(); /* Here the pthread thread id may have been recycled. */ GC_delete_gc_thread(thread, thread_gc_id); @@ -440,12 +494,34 @@ void * GC_start_routine(void * arg) struct start_info * si = arg; void * result; GC_thread me; - + pthread_t my_pthread; + void *(*start)(void *); + void *start_arg; + + my_pthread = pthread_self(); + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ LOCK(); - me = GC_lookup_thread(pthread_self()); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + /* Holding the allocation lock there would make REDIRECT_MALLOC */ + /* impossible. It probably still doesn't work, but we're a little */ + /* closer ... */ + /* This unfortunately means that we have to be careful the parent */ + /* doesn't try to do a pthread_join before we're registered. */ + me = GC_new_thread(my_pthread); + me -> flags = si -> flags; + me -> stack = si -> stack; + me -> stack_size = si -> stack_size; + me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word); UNLOCK(); + start = si -> start_routine; + start_arg = si -> arg; + sem_post(&(si -> registered)); pthread_cleanup_push(GC_thread_exit_proc, 0); - result = (*(si -> start_routine))(si -> arg); + result = (*start)(start_arg); me -> status = result; me -> flags |= FINISHED; pthread_cleanup_pop(1); @@ -462,15 +538,17 @@ GC_pthread_create(pthread_t *new_thread, { int result; GC_thread t; - pthread_t my_new_thread; void * stack; size_t stacksize; pthread_attr_t new_attr; int detachstate; word my_flags = 0; struct start_info * si = GC_malloc(sizeof(struct start_info)); + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ if (0 == si) return(ENOMEM); + sem_init(&(si -> registered), 0, 0); si -> start_routine = start_routine; si -> arg = arg; LOCK(); @@ -496,25 +574,25 @@ GC_pthread_create(pthread_t *new_thread, my_flags |= CLIENT_OWNS_STACK; } if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; - result = pthread_create(&my_new_thread, &new_attr, GC_start_routine, si); - /* No GC can start until the thread is registered, since we hold */ - /* the allocation lock. */ - if (0 == result) { - t = GC_new_thread(my_new_thread); - t -> flags = my_flags; - t -> stack = stack; - t -> stack_size = stacksize; - t -> stack_ptr = (ptr_t)stack + stacksize - sizeof(word); - if (0 != new_thread) *new_thread = my_new_thread; - } else if (!(my_flags & CLIENT_OWNS_STACK)) { + si -> flags = my_flags; + si -> stack = stack; + si -> stack_size = stacksize; + result = pthread_create(new_thread, &new_attr, GC_start_routine, si); + if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) { GC_stack_free(stack, stacksize); } UNLOCK(); + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto si until the child is done */ + /* with it. Thus it doesn't matter whether it is otherwise */ + /* visible to the collector. */ + if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed"); + sem_destroy(&(si -> registered)); /* pthread_attr_destroy(&new_attr); */ return(result); } -bool GC_collecting = 0; /* A hint that we're in the collector and */ +GC_bool GC_collecting = 0; /* A hint that we're in the collector and */ /* holding the allocation lock for an */ /* extended period. */ @@ -524,6 +602,8 @@ bool GC_collecting = 0; /* A hint that we're in the collector and */ unsigned long GC_allocate_lock = 0; +#define SLEEP_THRESHOLD 3 + void GC_lock() { # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ @@ -532,13 +612,14 @@ void GC_lock() unsigned my_spin_max; static unsigned last_spins = 0; unsigned my_last_spins; - unsigned junk; + volatile unsigned junk; # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk int i; - if (!__test_and_set(&GC_allocate_lock, 1)) { + if (!GC_test_and_set(&GC_allocate_lock, 1)) { return; } + junk = 0; my_spin_max = spin_max; my_last_spins = last_spins; for (i = 0; i < my_spin_max; i++) { @@ -547,7 +628,7 @@ void GC_lock() PAUSE; continue; } - if (!__test_and_set(&GC_allocate_lock, 1)) { + if (!GC_test_and_set(&GC_allocate_lock, 1)) { /* * got it! * Spinning worked. Thus we're probably not being scheduled @@ -562,11 +643,22 @@ void GC_lock() /* We are probably being scheduled against the other process. Sleep. */ spin_max = low_spin_max; yield: - for (;;) { - if (!__test_and_set(&GC_allocate_lock, 1)) { + for (i = 0;; ++i) { + if (!GC_test_and_set(&GC_allocate_lock, 1)) { return; } - sched_yield(); + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 26) i = 26; + /* Don't wait for more than about 60msecs, even */ + /* under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } } } diff --git a/linux_threads.c b/linux_threads.c new file mode 100644 index 00000000..4bcdd3a1 --- /dev/null +++ b/linux_threads.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Support code for LinuxThreads, the clone()-based kernel + * thread package for Linux which is included in libc6. + * + * This code relies on implementation details of LinuxThreads, + * (i.e. properties not guaranteed by the Pthread standard): + * + * - the function GC_linux_thread_top_of_stack(void) + * relies on the way LinuxThreads lays out thread stacks + * in the address space. + * + * Note that there is a lot of code duplication between linux_threads.c + * and irix_threads.c; any changes made here may need to be reflected + * there too. + */ + +/* #define DEBUG_THREADS 1 */ + +/* ANSI C requires that a compilation unit contains something */ +# include "gc_priv.h" + +# if defined(LINUX_THREADS) + +# include <pthread.h> +# include <time.h> +# include <errno.h> +# include <unistd.h> +# include <sys/mman.h> +# include <sys/time.h> +# include <semaphore.h> + +#undef pthread_create +#undef pthread_sigmask +#undef pthread_join + +void GC_thr_init(); + +#if 0 +void GC_print_sig_mask() +{ + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask"); + GC_printf0("Blocked: "); + for (i = 1; i <= MAXSIG; i++) { + if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } + } + GC_printf0("\n"); +} +#endif + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. We never actually create detached threads. We allocate all */ +/* new thread stacks ourselves. These allow us to maintain this */ +/* data structure. */ +/* Protected by GC_thr_lock. */ +/* Some of this should be declared volatile, but that's incosnsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; + word flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define MAIN_THREAD 4 /* True for the original thread only. */ + + ptr_t stack_end; + ptr_t stack_ptr; /* Valid only when stopped. */ + int signal; + void * status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ +} * GC_thread; + +GC_thread GC_lookup_thread(pthread_t id); + +/* + * The only way to suspend threads given the pthread interface is to send + * signals. We can't use SIGSTOP directly, because we need to get the + * thread to save its stack pointer in the GC thread table before + * suspending. So we have to reserve a signal of our own for this. + * This means we have to intercept client calls to change the signal mask. + * The linuxthreads package already uses SIGUSR1 and SIGUSR2, + * so we need to reuse something else. I chose SIGPWR. + * (Perhaps SIGUNUSED would be a better choice.) + */ +#define SIG_SUSPEND SIGPWR + +#define SIG_RESTART SIGXCPU + +sem_t GC_suspend_ack_sem; + +/* +GC_linux_thread_top_of_stack() relies on implementation details of +LinuxThreads, namely that thread stacks are allocated on 2M boundaries +and grow to no more than 2M. +To make sure that we're using LinuxThreads and not some other thread +package, we generate a dummy reference to `__pthread_initial_thread_bos', +which is a symbol defined in LinuxThreads, but (hopefully) not in other +thread packages. +*/ +extern char * __pthread_initial_thread_bos; +char **dummy_var_to_force_linux_threads = &__pthread_initial_thread_bos; + +#define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024) + +static inline ptr_t GC_linux_thread_top_of_stack(void) +{ + char *sp = GC_approx_sp(); + ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1); +#if DEBUG_THREADS + GC_printf1("SP = %lx\n", (unsigned long)sp); + GC_printf1("TOS = %lx\n", (unsigned long)tos); +#endif + return tos; +} + +void GC_suspend_handler(int sig) +{ + int dummy; + pthread_t my_thread = pthread_self(); + GC_thread me; + sigset_t all_sigs; + sigset_t old_sigs; + int i; + sigset_t mask; + + if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); + +#if DEBUG_THREADS + GC_printf1("Suspending 0x%x\n", my_thread); +#endif + + me = GC_lookup_thread(my_thread); + /* The lookup here is safe, since I'm doing this on behalf */ + /* of a thread which holds the allocation lock in order */ + /* to stop the world. Thus concurrent modification of the */ + /* data structure is impossible. */ + me -> stack_ptr = (ptr_t)(&dummy); + me -> stack_end = GC_linux_thread_top_of_stack(); + + /* Tell the thread that wants to stop the world that this */ + /* thread has been stopped. Note that sem_post() is */ + /* the only async-signal-safe primitive in LinuxThreads. */ + sem_post(&GC_suspend_ack_sem); + + /* Wait until that thread tells us to restart by sending */ + /* this thread a SIG_RESTART signal. */ + /* SIG_RESTART should be masked at this point. Thus there */ + /* is no race. */ + if (sigfillset(&mask) != 0) ABORT("sigfillset() failed"); + if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed"); + do { + me->signal = 0; + sigsuspend(&mask); /* Wait for signal */ + } while (me->signal != SIG_RESTART); + +#if DEBUG_THREADS + GC_printf1("Continuing 0x%x\n", my_thread); +#endif +} + +void GC_restart_handler(int sig) +{ + GC_thread me; + + if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler"); + + /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */ + /* The lookup here is safe, since I'm doing this on behalf */ + /* of a thread which holds the allocation lock in order */ + /* to stop the world. Thus concurrent modification of the */ + /* data structure is impossible. */ + me = GC_lookup_thread(pthread_self()); + me->signal = SIG_RESTART; + + /* + ** Note: even if we didn't do anything useful here, + ** it would still be necessary to have a signal handler, + ** rather than ignoring the signals, otherwise + ** the signals will not be delivered at all, and + ** will thus not interrupt the sigsuspend() above. + */ + +#if DEBUG_THREADS + GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self()); +#endif +} + +GC_bool GC_thr_initialized = FALSE; + +# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ +volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +GC_thread GC_new_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + GC_thread result; + static struct GC_Thread_Rep first_thread; + static GC_bool first_thread_used = FALSE; + + if (!first_thread_used) { + result = &first_thread; + first_thread_used = TRUE; + /* Dont acquire allocation lock, since we may already hold it. */ + } else { + result = (struct GC_Thread_Rep *) + GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL); + } + if (result == 0) return(0); + result -> id = id; + result -> next = GC_threads[hv]; + GC_threads[hv] = result; + /* result -> flags = 0; */ + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* Caller holds allocation lock. */ +void GC_delete_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (!pthread_equal(p -> id, id)) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same pthread id. */ +/* This is OK, but we need a way to delete a specific one. */ +void GC_delete_gc_thread(pthread_t id, GC_thread gc_id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (p != gc_id) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* Return a GC_thread corresponding to a given thread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock or otherwise inhibits */ +/* updates. */ +/* If there is more than one thread with the given id we */ +/* return the most recent one. */ +GC_thread GC_lookup_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + + while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next; + return(p); +} + +/* Caller holds allocation lock. */ +void GC_stop_world() +{ + pthread_t my_thread = pthread_self(); + register int i; + register GC_thread p; + register int n_live_threads = 0; + register int result; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> id != my_thread) { + if (p -> flags & FINISHED) continue; + n_live_threads++; + #if DEBUG_THREADS + GC_printf1("Sending suspend signal to 0x%x\n", p -> id); + #endif + result = pthread_kill(p -> id, SIG_SUSPEND); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + } + } + } + for (i = 0; i < n_live_threads; i++) { + sem_wait(&GC_suspend_ack_sem); + } + #if DEBUG_THREADS + GC_printf1("World stopped 0x%x\n", pthread_self()); + #endif +} + +/* Caller holds allocation lock. */ +void GC_start_world() +{ + pthread_t my_thread = pthread_self(); + register int i; + register GC_thread p; + register int n_live_threads = 0; + register int result; + +# if DEBUG_THREADS + GC_printf0("World starting\n"); +# endif + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> id != my_thread) { + if (p -> flags & FINISHED) continue; + n_live_threads++; + #if DEBUG_THREADS + GC_printf1("Sending restart signal to 0x%x\n", p -> id); + #endif + result = pthread_kill(p -> id, SIG_RESTART); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + } + } + } + #if DEBUG_THREADS + GC_printf0("World started\n"); + #endif +} + +/* We hold allocation lock. We assume the world is stopped. */ +void GC_push_all_stacks() +{ + register int i; + register GC_thread p; + register ptr_t sp = GC_approx_sp(); + register ptr_t lo, hi; + pthread_t me = pthread_self(); + + if (!GC_thr_initialized) GC_thr_init(); + #if DEBUG_THREADS + GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me); + #endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> flags & FINISHED) continue; + if (pthread_equal(p -> id, me)) { + lo = GC_approx_sp(); + } else { + lo = p -> stack_ptr; + } + if ((p -> flags & MAIN_THREAD) == 0) { + if (pthread_equal(p -> id, me)) { + hi = GC_linux_thread_top_of_stack(); + } else { + hi = p -> stack_end; + } + } else { + /* The original stack. */ + hi = GC_stackbottom; + } + #if DEBUG_THREADS + GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n", + (unsigned long) p -> id, + (unsigned long) lo, (unsigned long) hi); + #endif + GC_push_all_stack(lo, hi); + } + } +} + + +/* We hold the allocation lock. */ +void GC_thr_init() +{ + GC_thread t; + struct sigaction act; + + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + + if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0) + ABORT("sem_init failed"); + + act.sa_flags = SA_RESTART; + if (sigfillset(&act.sa_mask) != 0) { + ABORT("sigfillset() failed"); + } + /* SIG_RESTART is unmasked by the handler when necessary. */ + act.sa_handler = GC_suspend_handler; + if (sigaction(SIG_SUSPEND, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + + act.sa_handler = GC_restart_handler; + if (sigaction(SIG_RESTART, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + + /* Add the initial thread, so we can stop it. */ + t = GC_new_thread(pthread_self()); + t -> stack_ptr = 0; + t -> flags = DETACHED | MAIN_THREAD; +} + +int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + sigset_t fudged_set; + + if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { + fudged_set = *set; + sigdelset(&fudged_set, SIG_SUSPEND); + set = &fudged_set; + } + return(pthread_sigmask(how, set, oset)); +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; + word flags; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ +}; + + +void GC_thread_exit_proc(void *arg) +{ + GC_thread me; + struct start_info * si = arg; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + if (me -> flags & DETACHED) { + GC_delete_thread(pthread_self()); + } else { + me -> flags |= FINISHED; + } + UNLOCK(); +} + +int GC_pthread_join(pthread_t thread, void **retval) +{ + int result; + GC_thread thread_gc_id; + + LOCK(); + thread_gc_id = GC_lookup_thread(thread); + /* This is guaranteed to be the intended one, since the thread id */ + /* cant have been recycled by pthreads. */ + UNLOCK(); + result = pthread_join(thread, retval); + LOCK(); + /* Here the pthread thread id may have been recycled. */ + GC_delete_gc_thread(thread, thread_gc_id); + UNLOCK(); + return result; +} + +void * GC_start_routine(void * arg) +{ + struct start_info * si = arg; + void * result; + GC_thread me; + pthread_t my_pthread; + void *(*start)(void *); + void *start_arg; + + my_pthread = pthread_self(); + LOCK(); + me = GC_new_thread(my_pthread); + me -> flags = si -> flags; + me -> stack_ptr = 0; + me -> stack_end = 0; + UNLOCK(); + start = si -> start_routine; + start_arg = si -> arg; + sem_post(&(si -> registered)); + pthread_cleanup_push(GC_thread_exit_proc, si); +# ifdef DEBUG_THREADS + GC_printf1("Starting thread 0x%lx\n", pthread_self()); + GC_printf1("pid = %ld\n", (long) getpid()); + GC_printf1("sp = 0x%lx\n", (long) &arg); + GC_printf1("start_routine = 0x%lx\n", start); +# endif + result = (*start)(start_arg); +#if DEBUG_THREADS + GC_printf1("Finishing thread 0x%x\n", pthread_self()); +#endif + me -> status = result; + me -> flags |= FINISHED; + pthread_cleanup_pop(1); + /* Cleanup acquires lock, ensuring that we can't exit */ + /* while a collection that thinks we're alive is trying to stop */ + /* us. */ + return(result); +} + +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + int result; + GC_thread t; + pthread_t my_new_thread; + void * stack; + size_t stacksize; + pthread_attr_t new_attr; + int detachstate; + word my_flags = 0; + struct start_info * si = GC_malloc(sizeof(struct start_info)); + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + + if (0 == si) return(ENOMEM); + sem_init(&(si -> registered), 0, 0); + si -> start_routine = start_routine; + si -> arg = arg; + LOCK(); + if (!GC_thr_initialized) GC_thr_init(); + if (NULL == attr) { + stack = 0; + (void) pthread_attr_init(&new_attr); + } else { + new_attr = *attr; + } + pthread_attr_getdetachstate(&new_attr, &detachstate); + if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; + si -> flags = my_flags; + UNLOCK(); + result = pthread_create(new_thread, &new_attr, GC_start_routine, si); + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto si until the child is done */ + /* with it. Thus it doesn't matter whether it is otherwise */ + /* visible to the collector. */ + if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed"); + sem_destroy(&(si -> registered)); + /* pthread_attr_destroy(&new_attr); */ + /* pthread_attr_destroy(&new_attr); */ + return(result); +} + +GC_bool GC_collecting = 0; + /* A hint that we're in the collector and */ + /* holding the allocation lock for an */ + /* extended period. */ + +/* Reasonably fast spin locks. Basically the same implementation */ +/* as STL alloc.h. This isn't really the right way to do this. */ +/* but until the POSIX scheduling mess gets straightened out ... */ + +volatile unsigned int GC_allocate_lock = 0; + + +void GC_lock() +{ +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max 1000 /* spin cycles for multiprocessor */ + static unsigned spin_max = low_spin_max; + unsigned my_spin_max; + static unsigned last_spins = 0; + unsigned my_last_spins; + volatile unsigned junk; +# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk + int i; + + if (!GC_test_and_set(&GC_allocate_lock)) { + return; + } + junk = 0; + my_spin_max = spin_max; + my_last_spins = last_spins; + for (i = 0; i < my_spin_max; i++) { + if (GC_collecting) goto yield; + if (i < my_last_spins/2 || GC_allocate_lock) { + PAUSE; + continue; + } + if (!GC_test_and_set(&GC_allocate_lock)) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + last_spins = i; + spin_max = high_spin_max; + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + spin_max = low_spin_max; +yield: + for (i = 0;; ++i) { + if (!GC_test_and_set(&GC_allocate_lock)) { + return; + } +# define SLEEP_THRESHOLD 12 + /* nanosleep(<= 2ms) just spins under Linux. We */ + /* want to be careful to avoid that behavior. */ + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 26) i = 26; + /* Don't wait for more than about 60msecs, even */ + /* under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } + } +} + +# endif /* LINUX_THREADS */ + @@ -169,7 +169,9 @@ void GC_push_regs() # endif /* MACOS */ # if defined(I386) &&!defined(OS2) &&!defined(SVR4) &&!defined(MSWIN32) \ - && !defined(SCO) && !(defined(LINUX) && defined(__ELF__)) \ + && !defined(SCO) && !defined(SCO_ELF) \ + && !(defined(LINUX) && defined(__ELF__)) \ + && !(defined(__FreeBSD__) && defined(__ELF__)) \ && !defined(DOS4GW) /* I386 code, generic code does not appear to work */ /* It does appear to work under OS2, and asms dont */ @@ -183,8 +185,11 @@ void GC_push_regs() asm("pushl %ebx"); asm("call _GC_push_one"); asm("addl $4,%esp"); # endif -# if defined(I386) && defined(LINUX) && defined(__ELF__) +# if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \ + || ( defined(I386) && defined(__FreeBSD__) && defined(__ELF__) ) + /* This is modified for Linux with ELF (Note: _ELF_ only) */ + /* This section handles FreeBSD with ELF. */ asm("pushl %eax"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %ecx"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %edx"); asm("call GC_push_one"); asm("addl $4,%esp"); @@ -219,7 +224,7 @@ void GC_push_regs() __asm add esp,4 # endif -# if defined(I386) && (defined(SVR4) || defined(SCO)) +# if defined(I386) && (defined(SVR4) || defined(SCO) || defined(SCO_ELF)) /* I386 code, SVR4 variant, generic code does not appear to work */ asm("pushl %eax"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %ebx"); asm("call GC_push_one"); asm("addl $4,%esp"); @@ -21,7 +21,7 @@ void GC_extend_size_map(); /* in misc.c. */ /* Allocate reclaim list for kind: */ /* Return TRUE on success */ -bool GC_alloc_reclaim_list(kind) +GC_bool GC_alloc_reclaim_list(kind) register struct obj_kind * kind; { struct hblk ** result = (struct hblk **) @@ -115,7 +115,7 @@ register int k; ptr_t result; DCL_LOCK_STATE; - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); result = GC_generic_malloc_inner(lb, k); @@ -220,6 +220,9 @@ DCL_LOCK_STATE; /* * Thread initialisation can call malloc before * we're ready for it. + * It's not clear that this is enough to help matters. + * The thread implementation may well call malloc at other + * inopportune times. */ if (!GC_is_initialized) return sbrk(lb); # endif /* I386 && SOLARIS_THREADS */ @@ -375,6 +378,12 @@ int obj_kind; /* Required by ANSI. It's not my fault ... */ h = HBLKPTR(p); hhdr = HDR(h); +# if defined(REDIRECT_MALLOC) && \ + (defined(SOLARIS_THREADS) || defined(LINUX_THREADS)) + /* We have to redirect malloc calls during initialization. */ + /* Don't try to deallocate that memory. */ + if (0 == hhdr) return; +# endif knd = hhdr -> hb_obj_kind; sz = hhdr -> hb_sz; ok = &GC_obj_kinds[knd]; @@ -25,7 +25,7 @@ extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */ void GC_extend_size_map(); /* in misc.c. */ -bool GC_alloc_reclaim_list(); /* in malloc.c */ +GC_bool GC_alloc_reclaim_list(); /* in malloc.c */ /* Some externally visible but unadvertised variables to allow access to */ /* free lists from inlined allocators without including gc_priv.h */ @@ -76,7 +76,7 @@ register int k; register ptr_t result; DCL_LOCK_STATE; - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); result = GC_generic_malloc_inner_ignore_off_page(lb,k); @@ -139,7 +139,7 @@ register ptr_t *opp; register struct obj_kind * kind = GC_obj_kinds + k; DCL_LOCK_STATE; - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); opp = &(kind -> ok_freelist[lw]); @@ -192,7 +192,7 @@ DCL_LOCK_STATE; return(op); } lw = ALIGNED_WORDS(lb); - GC_invoke_finalizers(); + GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); opp = &(GC_obj_kinds[k].ok_freelist[lw]); @@ -21,7 +21,11 @@ /* We put this here to minimize the risk of inlining. */ /*VARARGS*/ -void GC_noop() {} +#ifdef __WATCOMC__ + void GC_noop(void *p, ...) {} +#else + void GC_noop() {} +#endif /* Single argument version, robust against whole program analysis. */ void GC_noop1(x) @@ -32,7 +36,8 @@ word x; sink = x; } -mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0}; +/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ + word GC_n_mark_procs = 0; /* Initialize GC_obj_kinds properly and standard free lists properly. */ @@ -103,12 +108,12 @@ static struct hblk * scan_ptr; mark_state_t GC_mark_state = MS_NONE; -bool GC_mark_stack_too_small = FALSE; +GC_bool GC_mark_stack_too_small = FALSE; -bool GC_objects_are_marked = FALSE; /* Are there collectable marked */ +GC_bool GC_objects_are_marked = FALSE; /* Are there collectable marked */ /* objects in the heap? */ -bool GC_collection_in_progress() +GC_bool GC_collection_in_progress() { return(GC_mark_state != MS_NONE); } @@ -169,7 +174,7 @@ ptr_t p; clear_mark_bit_from_hdr(hhdr, word_no); } -bool GC_is_marked(p) +GC_bool GC_is_marked(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); @@ -233,7 +238,7 @@ static void alloc_mark_stack(); /* Perform a small amount of marking. */ /* We try to touch roughly a page of memory. */ /* Return TRUE if we just finished a mark phase. */ -bool GC_mark_some() +GC_bool GC_mark_some() { switch(GC_mark_state) { case MS_NONE: @@ -320,7 +325,7 @@ bool GC_mark_some() } -bool GC_mark_stack_empty() +GC_bool GC_mark_stack_empty() { return(GC_mark_stack_top < GC_mark_stack); } @@ -339,7 +344,13 @@ bool GC_mark_stack_empty() /* Returns NIL without black listing if current points to a block */ /* with IGNORE_OFF_PAGE set. */ /*ARGSUSED*/ -word GC_find_start(current, hhdr) +# ifdef PRINT_BLACK_LIST + word GC_find_start(current, hhdr, source) + word source; +# else + word GC_find_start(current, hhdr) +# define source 0 +# endif register word current; register hdr * hhdr; { @@ -357,18 +368,19 @@ register hdr * hhdr; if ((word *)orig - (word *)current >= (ptrdiff_t)(hhdr->hb_sz)) { /* Pointer past the end of the block */ - GC_ADD_TO_BLACK_LIST_NORMAL(orig); + GC_ADD_TO_BLACK_LIST_NORMAL(orig, source); return(0); } return(current); } else { - GC_ADD_TO_BLACK_LIST_NORMAL(current); + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); return(0); } # else - GC_ADD_TO_BLACK_LIST_NORMAL(current); + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); return(0); # endif +# undef source } void GC_invalidate_mark_state() @@ -444,15 +456,14 @@ void GC_mark_from_mark_stack() credit -= WORDS_TO_BYTES(WORDSZ/2); /* guess */ while (descr != 0) { if ((signed_word)descr < 0) { - current = *current_p++; - descr <<= 1; - if ((ptr_t)current < least_ha) continue; - if ((ptr_t)current >= greatest_ha) continue; - PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit); - } else { - descr <<= 1; - current_p++; + current = *current_p; + if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { + PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit, + current_p, exit1); + } } + descr <<= 1; + ++ current_p; } continue; case DS_PROC: @@ -477,10 +488,11 @@ void GC_mark_from_mark_stack() limit -= 1; while (current_p <= limit) { current = *current_p; + if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { + PUSH_CONTENTS(current, GC_mark_stack_top_reg, + mark_stack_limit, current_p, exit2); + } current_p = (word *)((char *)current_p + ALIGNMENT); - if ((ptr_t)current < least_ha) continue; - if ((ptr_t)current >= greatest_ha) continue; - PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit); } } GC_mark_stack_top = GC_mark_stack_top_reg; @@ -655,9 +667,15 @@ word p; # endif /* As above, but argument passed preliminary test. */ -void GC_push_one_checked(p, interior_ptrs) +# ifdef PRINT_BLACK_LIST + void GC_push_one_checked(p, interior_ptrs, source) + ptr_t source; +# else + void GC_push_one_checked(p, interior_ptrs) +# define source 0 +# endif register word p; -register bool interior_ptrs; +register GC_bool interior_ptrs; { register word r; register hdr * hhdr; @@ -695,9 +713,14 @@ register bool interior_ptrs; /* displ is the word index within the block. */ if (hhdr == 0) { if (interior_ptrs) { - GC_add_to_black_list_stack(p); +# ifdef PRINT_BLACK_LIST + GC_add_to_black_list_stack(p, source); +# else + GC_add_to_black_list_stack(p); +# endif } else { - GC_ADD_TO_BLACK_LIST_NORMAL(p); + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); +# undef source /* In case we had to define it. */ } } else { if (!mark_bit_from_hdr(hhdr, displ)) { @@ -733,7 +756,7 @@ void GC_add_trace_entry(char *kind, word arg1, word arg2) if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; } -void GC_print_trace(word gc_no, bool lock) +void GC_print_trace(word gc_no, GC_bool lock) { int i; struct trace_entry *p; @@ -989,7 +1012,7 @@ register hdr * hhdr; #ifndef SMALL_CONFIG /* Test whether any page in the given block is dirty */ -bool GC_block_was_dirty(h, hhdr) +GC_bool GC_block_was_dirty(h, hhdr) struct hblk *h; register hdr * hhdr; { @@ -15,46 +15,27 @@ # include <stdio.h> # include "gc_priv.h" -/* MAX_ROOT_SETS is the maximum number of ranges that can be */ -/* registered as static roots. */ -# ifdef LARGE_CONFIG -# define MAX_ROOT_SETS 4096 -# else -# ifdef PCR -# define MAX_ROOT_SETS 1024 -# else -# ifdef MSWIN32 -# define MAX_ROOT_SETS 512 - /* Under NT, we add only written pages, which can result */ - /* in many small root sets. */ -# else -# define MAX_ROOT_SETS 64 -# endif -# endif -# endif - -# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) -/* Maximum number of segments that can be excluded from root sets. */ - /* Data structure for list of root sets. */ /* We keep a hash table, so that we can filter out duplicate additions. */ /* Under Win32, we need to do a better job of filtering overlaps, so */ /* we resort to sequential search, and pay the price. */ +/* This is really declared in gc_priv.h: struct roots { ptr_t r_start; ptr_t r_end; -# ifndef MSWIN32 + # ifndef MSWIN32 struct roots * r_next; -# endif - bool r_tmp; - /* Delete before registering new dynamic libraries */ + # endif + GC_bool r_tmp; + -- Delete before registering new dynamic libraries }; -static struct roots static_roots[MAX_ROOT_SETS]; +struct roots GC_static_roots[MAX_ROOT_SETS]; +*/ static int n_root_sets = 0; - /* static_roots[0..n_root_sets) contains the valid root sets. */ + /* GC_static_roots[0..n_root_sets) contains the valid root sets. */ # if !defined(NO_DEBUGGING) /* For debugging: */ @@ -65,14 +46,14 @@ void GC_print_static_roots() for (i = 0; i < n_root_sets; i++) { GC_printf2("From 0x%lx to 0x%lx ", - (unsigned long) static_roots[i].r_start, - (unsigned long) static_roots[i].r_end); - if (static_roots[i].r_tmp) { + (unsigned long) GC_static_roots[i].r_start, + (unsigned long) GC_static_roots[i].r_end); + if (GC_static_roots[i].r_tmp) { GC_printf0(" (temporary)\n"); } else { GC_printf0("\n"); } - total += static_roots[i].r_end - static_roots[i].r_start; + total += GC_static_roots[i].r_end - GC_static_roots[i].r_start; } GC_printf1("Total size: %ld\n", (unsigned long) total); if (GC_root_size != total) { @@ -85,18 +66,18 @@ void GC_print_static_roots() /* Primarily for debugging support: */ /* Is the address p in one of the registered static */ /* root sections? */ -bool GC_is_static_root(p) +GC_bool GC_is_static_root(p) ptr_t p; { static int last_root_set = 0; register int i; - if (p >= static_roots[last_root_set].r_start - && p < static_roots[last_root_set].r_end) return(TRUE); + if (p >= GC_static_roots[last_root_set].r_start + && p < GC_static_roots[last_root_set].r_end) return(TRUE); for (i = 0; i < n_root_sets; i++) { - if (p >= static_roots[i].r_start - && p < static_roots[i].r_end) { + if (p >= GC_static_roots[i].r_start + && p < GC_static_roots[i].r_end) { last_root_set = i; return(TRUE); } @@ -105,12 +86,15 @@ ptr_t p; } #ifndef MSWIN32 +/* # define LOG_RT_SIZE 6 -# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +# define RT_SIZE (1 << LOG_RT_SIZE) -- Power of 2, may be != MAX_ROOT_SETS - static struct roots * root_index[RT_SIZE]; - /* Hash table header. Used only to check whether a range is */ - /* already present. */ + struct roots * GC_root_index[RT_SIZE]; + -- Hash table header. Used only to check whether a range is + -- already present. + -- really defined in gc_priv.h +*/ static int rt_hash(addr) char * addr; @@ -134,7 +118,7 @@ struct roots * GC_roots_present(b) char *b; { register int h = rt_hash(b); - register struct roots *p = root_index[h]; + register struct roots *p = GC_root_index[h]; while (p != 0) { if (p -> r_start == (ptr_t)b) return(p); @@ -149,8 +133,8 @@ struct roots *p; { register int h = rt_hash(p -> r_start); - p -> r_next = root_index[h]; - root_index[h] = p; + p -> r_next = GC_root_index[h]; + GC_root_index[h] = p; } # else /* MSWIN32 */ @@ -185,7 +169,7 @@ char * b; char * e; /* reregistering dynamic libraries. */ void GC_add_roots_inner(b, e, tmp) char * b; char * e; -bool tmp; +GC_bool tmp; { struct roots * old; @@ -200,7 +184,7 @@ bool tmp; register int i; for (i = 0; i < n_root_sets; i++) { - old = static_roots + i; + old = GC_static_roots + i; if ((ptr_t)b <= old -> r_end && (ptr_t)e >= old -> r_start) { if ((ptr_t)b < old -> r_start) { old -> r_start = (ptr_t)b; @@ -219,7 +203,7 @@ bool tmp; struct roots *other; for (i++; i < n_root_sets; i++) { - other = static_roots + i; + other = GC_static_roots + i; b = (char *)(other -> r_start); e = (char *)(other -> r_end); if ((ptr_t)b <= old -> r_end && (ptr_t)e >= old -> r_start) { @@ -234,8 +218,8 @@ bool tmp; old -> r_tmp &= other -> r_tmp; /* Delete this entry. */ GC_root_size -= (other -> r_end - other -> r_start); - other -> r_start = static_roots[n_root_sets-1].r_start; - other -> r_end = static_roots[n_root_sets-1].r_end; + other -> r_start = GC_static_roots[n_root_sets-1].r_start; + other -> r_end = GC_static_roots[n_root_sets-1].r_end; n_root_sets--; } } @@ -255,13 +239,13 @@ bool tmp; if (n_root_sets == MAX_ROOT_SETS) { ABORT("Too many root sets\n"); } - static_roots[n_root_sets].r_start = (ptr_t)b; - static_roots[n_root_sets].r_end = (ptr_t)e; - static_roots[n_root_sets].r_tmp = tmp; + GC_static_roots[n_root_sets].r_start = (ptr_t)b; + GC_static_roots[n_root_sets].r_end = (ptr_t)e; + GC_static_roots[n_root_sets].r_tmp = tmp; # ifndef MSWIN32 - static_roots[n_root_sets].r_next = 0; + GC_static_roots[n_root_sets].r_next = 0; # endif - add_roots_to_index(static_roots + n_root_sets); + add_roots_to_index(GC_static_roots + n_root_sets); GC_root_size += (ptr_t)e - (ptr_t)b; n_root_sets++; } @@ -278,7 +262,7 @@ void GC_clear_roots GC_PROTO((void)) { register int i; - for (i = 0; i < RT_SIZE; i++) root_index[i] = 0; + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; } # endif UNLOCK(); @@ -291,11 +275,12 @@ void GC_remove_tmp_roots() register int i; for (i = 0; i < n_root_sets; ) { - if (static_roots[i].r_tmp) { - GC_root_size -= (static_roots[i].r_end - static_roots[i].r_start); - static_roots[i].r_start = static_roots[n_root_sets-1].r_start; - static_roots[i].r_end = static_roots[n_root_sets-1].r_end; - static_roots[i].r_tmp = static_roots[n_root_sets-1].r_tmp; + if (GC_static_roots[i].r_tmp) { + GC_root_size -= + (GC_static_roots[i].r_end - GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; n_root_sets--; } else { i++; @@ -305,8 +290,9 @@ void GC_remove_tmp_roots() { register int i; - for (i = 0; i < RT_SIZE; i++) root_index[i] = 0; - for (i = 0; i < n_root_sets; i++) add_roots_to_index(static_roots + i); + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); } # endif @@ -321,16 +307,19 @@ ptr_t GC_approx_sp() /* * Data structure for excluded static roots. - */ + * Real declaration is in gc_priv.h. + struct exclusion { ptr_t e_start; ptr_t e_end; }; -struct exclusion excl_table[MAX_EXCLUSIONS]; - /* Array of exclusions, ascending */ - /* address order. */ -size_t excl_table_entries = 0; /* Number of entries in use. */ +struct exclusion GC_excl_table[MAX_EXCLUSIONS]; + -- Array of exclusions, ascending + -- address order. +*/ + +size_t GC_excl_table_entries = 0; /* Number of entries in use. */ /* Return the first exclusion range that includes an address >= start_addr */ /* Assumes the exclusion table contains at least one entry (namely the */ @@ -339,20 +328,20 @@ struct exclusion * GC_next_exclusion(start_addr) ptr_t start_addr; { size_t low = 0; - size_t high = excl_table_entries - 1; + size_t high = GC_excl_table_entries - 1; size_t mid; while (high > low) { mid = (low + high) >> 1; /* low <= mid < high */ - if ((word) excl_table[mid].e_end <= (word) start_addr) { + if ((word) GC_excl_table[mid].e_end <= (word) start_addr) { low = mid + 1; } else { high = mid; } } - if ((word) excl_table[low].e_end <= (word) start_addr) return 0; - return excl_table + low; + if ((word) GC_excl_table[low].e_end <= (word) start_addr) return 0; + return GC_excl_table + low; } void GC_exclude_static_roots(start, finish) @@ -362,7 +351,7 @@ GC_PTR finish; struct exclusion * next; size_t next_index, i; - if (0 == excl_table_entries) { + if (0 == GC_excl_table_entries) { next = 0; } else { next = GC_next_exclusion(start); @@ -377,17 +366,17 @@ GC_PTR finish; next -> e_start = (ptr_t)start; return; } - next_index = next - excl_table; - for (i = excl_table_entries - 1; i >= next_index; --i) { - excl_table[i+1] = excl_table[i]; + next_index = next - GC_excl_table; + for (i = GC_excl_table_entries; i > next_index; --i) { + GC_excl_table[i] = GC_excl_table[i-1]; } } else { - next_index = excl_table_entries; + next_index = GC_excl_table_entries; } - if (excl_table_entries == MAX_EXCLUSIONS) ABORT("Too many exclusions"); - excl_table[next_index].e_start = (ptr_t)start; - excl_table[next_index].e_end = (ptr_t)finish; - ++excl_table_entries; + if (GC_excl_table_entries == MAX_EXCLUSIONS) ABORT("Too many exclusions"); + GC_excl_table[next_index].e_start = (ptr_t)start; + GC_excl_table[next_index].e_end = (ptr_t)finish; + ++GC_excl_table_entries; } /* Invoke push_conditional on ranges that are not excluded. */ @@ -417,7 +406,7 @@ int all; */ void GC_push_roots(all) -bool all; +GC_bool all; { register int i; @@ -440,8 +429,8 @@ bool all; /* Mark everything in static data areas */ for (i = 0; i < n_root_sets; i++) { GC_push_conditional_with_exclusions( - static_roots[i].r_start, - static_roots[i].r_end, all); + GC_static_roots[i].r_start, + GC_static_roots[i].r_end, all); } /* diff --git a/mips_sgi_mach_dep.s b/mips_sgi_mach_dep.s index bf7f61a5..03c4b980 100644 --- a/mips_sgi_mach_dep.s +++ b/mips_sgi_mach_dep.s @@ -9,6 +9,7 @@ # define RAOFF FRAMESZ-SZREG # define GPOFF FRAMESZ-(2*SZREG) NESTED(GC_push_regs, FRAMESZ, ra) + .mask 0x80000000,-SZREG # inform debugger of saved ra loc move t0,gp SETUP_GPX(t8) PTR_SUBU sp,FRAMESZ @@ -42,7 +42,8 @@ # ifdef WIN32_THREADS GC_API CRITICAL_SECTION GC_allocate_ml; # else -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) # ifdef UNDEFINED pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; # endif @@ -59,7 +60,7 @@ GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; -bool GC_debugging_started = FALSE; +GC_bool GC_debugging_started = FALSE; /* defined here so we don't have to load debug_malloc.o */ void (*GC_check_heap)() = (void (*)())0; @@ -68,9 +69,9 @@ void (*GC_start_call_back)() = (void (*)())0; ptr_t GC_stackbottom = 0; -bool GC_dont_gc = 0; +GC_bool GC_dont_gc = 0; -bool GC_quiet = 0; +GC_bool GC_quiet = 0; /*ARGSUSED*/ GC_PTR GC_default_oom_fn GC_PROTO((size_t bytes_requested)) @@ -232,8 +233,6 @@ word limit; } #endif -extern ptr_t GC_approx_sp(); /* in mark_rts.c */ - /* Clear some of the inaccessible part of the stack. Returns its */ /* argument, so it can be used in a tail call position, hence clearing */ /* another frame. */ @@ -330,8 +329,7 @@ ptr_t arg; /* Make sure r points to the beginning of the object */ r &= ~(WORDS_TO_BYTES(1) - 1); { - register int offset = - (char *)r - (char *)(HBLKPTR(r)) - HDR_BYTES; + register int offset = (char *)r - (char *)(HBLKPTR(r)); register signed_word sz = candidate_hdr -> hb_sz; # ifdef ALL_INTERIOR_POINTERS @@ -392,11 +390,7 @@ size_t GC_get_bytes_since_gc GC_PROTO(()) return ((size_t) WORDS_TO_BYTES(GC_words_allocd)); } -bool GC_is_initialized = FALSE; - -#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) - extern void GC_thr_init(); -#endif +GC_bool GC_is_initialized = FALSE; void GC_init() { @@ -424,22 +418,32 @@ void GC_init_inner() if (GC_is_initialized) return; GC_setpagesize(); - GC_exclude_static_roots(beginGC_arrays, endGC_arrays); + GC_exclude_static_roots(beginGC_arrays, end_gc_area); +# ifdef PRINTSTATS + if ((ptr_t)endGC_arrays != (ptr_t)(&GC_obj_kinds)) { + GC_printf0("Reordering linker, didn't exclude obj_kinds\n"); + } +# endif # ifdef MSWIN32 GC_init_win32(); # endif # if defined(LINUX) && defined(POWERPC) GC_init_linuxppc(); # endif +# if defined(LINUX) && defined(SPARC) + GC_init_linuxsparc(); +# endif # ifdef SOLARIS_THREADS GC_thr_init(); /* We need dirty bits in order to find live stack sections. */ GC_dirty_init(); # endif -# ifdef IRIX_THREADS - GC_thr_init(); +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) + GC_thr_init(); # endif -# if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS) +# if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) if (GC_stackbottom == 0) { GC_stackbottom = GC_get_stack_base(); } @@ -554,16 +558,16 @@ void GC_init_inner() void GC_enable_incremental GC_PROTO(()) { +# if !defined(FIND_LEAK) && !defined(SMALL_CONFIG) DCL_LOCK_STATE; -# ifndef FIND_LEAK DISABLE_SIGNALS(); LOCK(); if (GC_incremental) goto out; GC_setpagesize(); # ifdef MSWIN32 { - extern bool GC_is_win32s(); + extern GC_bool GC_is_win32s(); /* VirtualProtect is not functional under win32s. */ if (GC_is_win32s()) goto out; @@ -757,6 +761,39 @@ char * msg; } #endif +#ifdef NEED_CALLINFO + +void GC_print_callers (info) +struct callinfo info[NFRAMES]; +{ + register int i; + +# if NFRAMES == 1 + GC_err_printf0("\tCaller at allocation:\n"); +# else + GC_err_printf0("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES; i++) { + if (info[i].ci_pc == 0) break; +# if NARGS > 0 + { + int j; + + GC_err_printf0("\t\targs: "); + for (j = 0; j < NARGS; j++) { + if (j != 0) GC_err_printf0(", "); + GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), + ~(info[i].ci_arg[j])); + } + GC_err_printf0("\n"); + } +# endif + GC_err_printf1("\t\t##PC##= 0x%X\n", info[i].ci_pc); + } +} + +#endif /* SAVE_CALL_CHAIN */ + # ifdef SRC_M3 void GC_enable() { @@ -164,7 +164,7 @@ int kind; *prev; word *last_object; /* points to last object in new hblk */ register struct hblk *h; /* the new heap block */ - register bool clear = GC_obj_kinds[kind].ok_init; + register GC_bool clear = GC_obj_kinds[kind].ok_init; # ifdef PRINTSTATS if ((sizeof (struct hblk)) > HBLKSIZE) { @@ -100,7 +100,7 @@ word offset; /* Add a heap block map for objects of size sz to obj_map. */ /* Return FALSE on failure. */ -bool GC_add_map_entry(sz) +GC_bool GC_add_map_entry(sz) word sz; { register unsigned obj_start; @@ -24,8 +24,19 @@ # define __KERNEL__ # include <asm/signal.h> # undef __KERNEL__ -# elif (LINUX_VERSION_CODE < 0x20100) -# include <asm/sigcontext.h> +# else + /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */ + /* struct sigcontext. libc6 (glibc2) uses "struct sigcontext" in */ + /* prototypes, so we have to include the top-level sigcontext.h to */ + /* make sure the former gets defined to be the latter if appropriate. */ +# include <features.h> +# if 2 <= __GLIBC__ +# include <sigcontext.h> +# else /* not 2 <= __GLIBC__ */ + /* libc5 doesn't have <sigcontext.h>: go directly with the kernel */ + /* one. Check LINUX_VERSION_CODE to see which we should reference. */ +# include <asm/sigcontext.h> +# endif /* 2 <= __GLIBC__ */ # endif # endif # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) @@ -34,6 +45,7 @@ # include <unistd.h> # endif # endif + # include <stdio.h> # include <signal.h> @@ -56,7 +68,7 @@ # define NEED_FIND_LIMIT # endif -# if defined(LINUX) && defined(POWERPC) +# if defined(LINUX) && (defined(POWERPC) || defined(SPARC)) # define NEED_FIND_LIMIT # endif @@ -130,6 +142,21 @@ { extern ptr_t GC_find_limit(); extern char **_environ; + /* This may need to be environ, without the underscore, for */ + /* some versions. */ + GC_data_start = GC_find_limit((ptr_t)&_environ, FALSE); + } +#endif + +#if defined(LINUX) && defined(SPARC) + ptr_t GC_data_start; + + void GC_init_linuxsparc() + { + extern ptr_t GC_find_limit(); + extern char **_environ; + /* This may need to be environ, without the underscore, for */ + /* some versions. */ GC_data_start = GC_find_limit((ptr_t)&_environ, FALSE); } #endif @@ -257,7 +284,7 @@ void GC_enable_signals(void) # define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old)) # endif -static bool mask_initialized = FALSE; +static GC_bool mask_initialized = FALSE; static SIGSET_T new_mask; @@ -331,7 +358,7 @@ word GC_page_size; } # else -# if defined(MPROTECT_VDB) || defined(PROC_VDB) +# if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) void GC_setpagesize() { GC_page_size = GETPAGESIZE(); @@ -455,19 +482,22 @@ ptr_t GC_get_stack_base() typedef void (*handler)(); # endif -# ifdef SUNOS5SIGS - static struct sigaction oldact; +# if defined(SUNOS5SIGS) || defined(IRIX5) + static struct sigaction old_segv_act; +# if defined(_sigargs) /* !Irix6.x */ + static struct sigaction old_bus_act; +# endif # else static handler old_segv_handler, old_bus_handler; # endif void GC_setup_temporary_fault_handler() { -# ifdef SUNOS5SIGS +# if defined(SUNOS5SIGS) || defined(IRIX5) struct sigaction act; act.sa_handler = GC_fault_handler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_NODEFER; + act.sa_flags = SA_RESTART | SA_NODEFER; /* The presence of SA_NODEFER represents yet another gross */ /* hack. Under Solaris 2.3, siglongjmp doesn't appear to */ /* interact correctly with -lthread. We hide the confusion */ @@ -475,7 +505,20 @@ ptr_t GC_get_stack_base() /* signal mask. */ (void) sigemptyset(&act.sa_mask); - (void) sigaction(SIGSEGV, &act, &oldact); +# ifdef IRIX_THREADS + /* Older versions have a bug related to retrieving and */ + /* and setting a handler at the same time. */ + (void) sigaction(SIGSEGV, 0, &old_segv_act); + (void) sigaction(SIGSEGV, &act, 0); +# else + (void) sigaction(SIGSEGV, &act, &old_segv_act); +# ifdef _sigargs /* Irix 5.x, not 6.x */ + /* Under 5.x, we may get SIGBUS. */ + /* Pthreads doesn't exist under 5.x, so we don't */ + /* have to worry in the threads case. */ + (void) sigaction(SIGBUS, &act, &old_bus_act); +# endif +# endif /* IRIX_THREADS */ # else old_segv_handler = signal(SIGSEGV, GC_fault_handler); # ifdef SIGBUS @@ -486,8 +529,11 @@ ptr_t GC_get_stack_base() void GC_reset_fault_handler() { -# ifdef SUNOS5SIGS - (void) sigaction(SIGSEGV, &oldact, 0); +# if defined(SUNOS5SIGS) || defined(IRIX5) + (void) sigaction(SIGSEGV, &old_segv_act, 0); +# ifdef _sigargs /* Irix 5.x, not 6.x */ + (void) sigaction(SIGBUS, &old_bus_act, 0); +# endif # else (void) signal(SIGSEGV, old_segv_handler); # ifdef SIGBUS @@ -500,7 +546,7 @@ ptr_t GC_get_stack_base() /* the smallest location q s.t. [q,p] is addressible (!up). */ ptr_t GC_find_limit(p, up) ptr_t p; - bool up; + GC_bool up; { static VOLATILE ptr_t result; /* Needs to be static, since otherwise it may not be */ @@ -571,6 +617,9 @@ ptr_t GC_get_stack_base() # endif # endif /* HEURISTIC2 */ +# ifdef STACK_GROWS_DOWN + if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t)); +# endif return(result); # endif /* STACKBOTTOM */ } @@ -687,9 +736,9 @@ void GC_register_data_segments() /* all real work is done by GC_register_dynamic_libraries. Under */ /* win32s, we cannot find the data segments associated with dll's. */ /* We rgister the main data segment here. */ - bool GC_win32s = FALSE; /* We're running under win32s. */ + GC_bool GC_win32s = FALSE; /* We're running under win32s. */ - bool GC_is_win32s() + GC_bool GC_is_win32s() { DWORD v = GetVersion(); @@ -729,7 +778,7 @@ void GC_register_data_segments() /* Is p the start of either the malloc heap, or of one of our */ /* heap sections? */ - bool GC_is_heap_base (ptr_t p) + GC_bool GC_is_heap_base (ptr_t p) { register unsigned i; @@ -907,9 +956,19 @@ void GC_register_data_segments() # if defined(__MWERKS__) # if !__POWERPC__ extern void* GC_MacGetDataStart(void); + /* MATTHEW: Function to handle Far Globals (CW Pro 3) */ +# if __option(far_data) + extern void* GC_MacGetDataEnd(void); +# endif /* globals begin above stack and end at a5. */ GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), (ptr_t)LMGetCurrentA5(), FALSE); + /* MATTHEW: Handle Far Globals */ +# if __option(far_data) + /* Far globals follow he QD globals: */ + GC_add_roots_inner((ptr_t)LMGetCurrentA5(), + (ptr_t)GC_MacGetDataEnd(), FALSE); +# endif # else extern char __data_start__[], __data_end__[]; GC_add_roots_inner((ptr_t)&__data_start__, @@ -951,14 +1010,14 @@ void GC_register_data_segments() ptr_t GC_unix_get_mem(bytes) word bytes; { - caddr_t cur_brk = sbrk(0); + caddr_t cur_brk = (caddr_t)sbrk(0); caddr_t result; SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); static caddr_t my_brk_val = 0; if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */ if (lsbs != 0) { - if(sbrk(GC_page_size - lsbs) == (caddr_t)(-1)) return(0); + if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0); } if (cur_brk == my_brk_val) { /* Use the extra block we allocated last time. */ @@ -976,12 +1035,20 @@ word bytes; #else /* Not RS6000 */ #if defined(USE_MMAP) -/* Tested only under IRIX5 */ +/* Tested only under IRIX5 and Solaris 2 */ + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif ptr_t GC_unix_get_mem(bytes) word bytes; { - static bool initialized = FALSE; + static GC_bool initialized = FALSE; static int fd; void *result; static ptr_t last_addr = HEAP_START; @@ -990,8 +1057,9 @@ word bytes; fd = open("/dev/zero", O_RDONLY); initialized = TRUE; } + if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg"); result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, - MAP_PRIVATE | MAP_FIXED, fd, 0/* offset */); + GC_MMAP_FLAGS, fd, 0/* offset */); if (result == MAP_FAILED) return(0); last_addr = (ptr_t)result + bytes + GC_page_size - 1; last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1)); @@ -1075,6 +1143,17 @@ word bytes; return(result); } +void GC_win32_free_heap () +{ + if (GC_win32s) { + while (GC_n_heap_bases > 0) { + GlobalFree (GC_heap_bases[--GC_n_heap_bases]); + GC_heap_bases[GC_n_heap_bases] = 0; + } + } +} + + # endif /* Routine for pushing any additional roots. In THREADS */ @@ -1192,7 +1271,9 @@ void GC_default_push_other_roots() # endif /* SRC_M3 */ -# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS) +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_PCR_THREADS) extern void GC_push_all_stacks(); @@ -1228,7 +1309,7 @@ void (*GC_push_other_roots)() = GC_default_push_other_roots; * or write only to the stack. */ -bool GC_dirty_maintained = FALSE; +GC_bool GC_dirty_maintained = FALSE; # ifdef DEFAULT_VDB @@ -1254,7 +1335,7 @@ void GC_read_dirty() /* of the pages overlapping h are dirty. This routine may err on the */ /* side of labelling pages as dirty (and this implementation does). */ /*ARGSUSED*/ -bool GC_page_was_dirty(h) +GC_bool GC_page_was_dirty(h) struct hblk *h; { return(TRUE); @@ -1269,7 +1350,7 @@ struct hblk *h; /* Could any valid GC heap pointer ever have been written to this page? */ /*ARGSUSED*/ -bool GC_page_was_ever_dirty(h) +GC_bool GC_page_was_ever_dirty(h) struct hblk *h; { return(TRUE); @@ -1349,9 +1430,6 @@ struct hblk *h; # endif -VOLATILE page_hash_table GC_dirty_pages; - /* Pages dirtied since last GC_read_dirty. */ - #if defined(SUNOS4) || defined(FREEBSD) typedef void (* SIG_PF)(); #endif @@ -1372,11 +1450,27 @@ VOLATILE page_hash_table GC_dirty_pages; #endif #if defined(LINUX) # include <linux/version.h> -# if (LINUX_VERSION_CODE >= 0x20100) - typedef void (* REAL_SIG_PF)(int, struct sigcontext); +# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) + typedef struct sigcontext s_c; # else - typedef void (* REAL_SIG_PF)(int, struct sigcontext_struct); + typedef struct sigcontext_struct s_c; # endif +# ifdef ALPHA + typedef void (* REAL_SIG_PF)(int, int, s_c *); + /* Retrieve fault address from sigcontext structure by decoding */ + /* instruction. */ + char * get_fault_addr(s_c *sc) { + unsigned instr; + word faultaddr; + + instr = *((unsigned *)(sc->sc_pc)); + faultaddr = sc->sc_regs[(instr >> 16) & 0x1f]; + faultaddr += (word) (((int)instr << 16) >> 16); + return (char *)faultaddr; + } +# else /* !ALPHA */ + typedef void (* REAL_SIG_PF)(int, s_c); +# endif /* !ALPHA */ # endif SIG_PF GC_old_bus_handler; @@ -1411,10 +1505,10 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # endif # endif # if defined(LINUX) -# if (LINUX_VERSION_CODE >= 0x20100) - void GC_write_fault_handler(int sig, struct sigcontext sc) +# ifdef ALPHA + void GC_write_fault_handler(int sig, int code, s_c * sc) # else - void GC_write_fault_handler(int sig, struct sigcontext_struct sc) + void GC_write_fault_handler(int sig, s_c sc) # endif # define SIG_OK (sig == SIGSEGV) # define CODE_OK TRUE @@ -1449,7 +1543,37 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # ifdef I386 char * addr = (char *) (sc.cr2); # else - char * addr = /* As of 1.3.90 there seemed to be no way to do this. */; +# if defined(M68K) + char * addr = NULL; + + struct sigcontext *scp = (struct sigcontext *)(&sc); + + int format = (scp->sc_formatvec >> 12) & 0xf; + unsigned long *framedata = (unsigned long *)(scp + 1); + unsigned long ea; + + if (format == 0xa || format == 0xb) { + /* 68020/030 */ + ea = framedata[2]; + } else if (format == 7) { + /* 68040 */ + ea = framedata[3]; + } else if (format == 4) { + /* 68060 */ + ea = framedata[0]; + if (framedata[1] & 0x08000000) { + /* correct addr on misaligned access */ + ea = (ea+4095)&(~4095); + } + } + addr = (char *)ea; +# else +# ifdef ALPHA + char * addr = get_fault_addr(sc); +# else + --> architecture not supported +# endif +# endif # endif # endif # if defined(MSWIN32) @@ -1461,7 +1585,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ if (SIG_OK && CODE_OK) { register struct hblk * h = (struct hblk *)((word)addr & ~(GC_page_size-1)); - bool in_allocd_block; + GC_bool in_allocd_block; # ifdef SUNOS5SIGS /* Address is only within the correct physical page. */ @@ -1485,6 +1609,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ } if (old_handler == SIG_DFL) { # ifndef MSWIN32 + GC_err_printf1("Segfault at 0x%lx\n", addr); ABORT("Unexpected bus error or segmentation fault"); # else return(EXCEPTION_CONTINUE_SEARCH); @@ -1499,7 +1624,11 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ return; # endif # if defined (LINUX) - (*(REAL_SIG_PF)old_handler) (sig, sc); +# ifdef ALPHA + (*(REAL_SIG_PF)old_handler) (sig, code, sc); +# else + (*(REAL_SIG_PF)old_handler) (sig, sc); +# endif return; # endif # if defined (IRIX5) || defined(OSF1) @@ -1517,7 +1646,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ set_pht_entry_from_index(GC_dirty_pages, index); } UNPROTECT(h, GC_page_size); -# if defined(IRIX5) || defined(OSF1) || defined(LINUX) +# if defined(OSF1) || defined(LINUX) /* These reset the signal handler each time by default. */ signal(SIGSEGV, (SIG_PF) GC_write_fault_handler); # endif @@ -1532,6 +1661,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ #ifdef MSWIN32 return EXCEPTION_CONTINUE_SEARCH; #else + GC_err_printf1("Segfault at 0x%lx\n", addr); ABORT("Unexpected bus error or segmentation fault"); #endif } @@ -1545,7 +1675,7 @@ struct hblk *h; { register struct hblk * h_trunc; register unsigned i; - register bool found_clean; + register GC_bool found_clean; if (!GC_dirty_maintained) return; h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); @@ -1565,10 +1695,15 @@ struct hblk *h; void GC_dirty_init() { -#if defined(SUNOS5SIGS) +#if defined(SUNOS5SIGS) || defined(IRIX5) struct sigaction act, oldact; - act.sa_sigaction = GC_write_fault_handler; - act.sa_flags = SA_RESTART | SA_SIGINFO; +# ifdef IRIX5 + act.sa_flags = SA_RESTART; + act.sa_handler = GC_write_fault_handler; +# else + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = GC_write_fault_handler; +# endif (void)sigemptyset(&act.sa_mask); #endif # ifdef PRINTSTATS @@ -1591,7 +1726,7 @@ void GC_dirty_init() # endif } # endif -# if defined(IRIX5) || defined(OSF1) || defined(SUNOS4) || defined(LINUX) +# if defined(OSF1) || defined(SUNOS4) || defined(LINUX) GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler); if (GC_old_segv_handler == SIG_IGN) { GC_err_printf0("Previously ignored segmentation violation!?"); @@ -1603,13 +1738,24 @@ void GC_dirty_init() # endif } # endif -# if defined(SUNOS5SIGS) - sigaction(SIGSEGV, &act, &oldact); - if (oldact.sa_flags & SA_SIGINFO) { +# if defined(SUNOS5SIGS) || defined(IRIX5) +# if defined(IRIX_THREADS) || defined(IRIX_PCR_THREADS) + sigaction(SIGSEGV, 0, &oldact); + sigaction(SIGSEGV, &act, 0); +# else + sigaction(SIGSEGV, &act, &oldact); +# endif +# if defined(_sigargs) + /* This is Irix 5.x, not 6.x. Irix 5.x does not have */ + /* sa_sigaction. */ + GC_old_segv_handler = oldact.sa_handler; +# else /* Irix 6.x or SUNOS5SIGS */ + if (oldact.sa_flags & SA_SIGINFO) { GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction); - } else { + } else { GC_old_segv_handler = oldact.sa_handler; - } + } +# endif if (GC_old_segv_handler == SIG_IGN) { GC_err_printf0("Previously ignored segmentation violation!?"); GC_old_segv_handler = SIG_DFL; @@ -1657,7 +1803,7 @@ void GC_read_dirty() GC_protect_heap(); } -bool GC_page_was_dirty(h) +GC_bool GC_page_was_dirty(h) struct hblk * h; { register word index = PHT_HASH(h); @@ -1733,13 +1879,9 @@ word len; GC_begin_syscall(); GC_unprotect_range(buf, (word)nbyte); # ifdef IRIX5 - /* Indirect system call exists, but is undocumented, and */ - /* always seems to return EINVAL. There seems to be no */ - /* general way to wrap system calls, since the system call */ - /* convention appears to require an immediate argument for */ - /* the system call number, and building the required code */ - /* in the data segment also seems dangerous. We can fake it */ - /* for read; anything else is up to the client. */ + /* Indirect system call may not always be easily available. */ + /* We could call _read, but that would interfere with the */ + /* libpthread interception of read. */ { struct iovec iov; @@ -1756,7 +1898,7 @@ word len; #endif /* !MSWIN32 */ /*ARGSUSED*/ -bool GC_page_was_ever_dirty(h) +GC_bool GC_page_was_ever_dirty(h) struct hblk *h; { return(TRUE); @@ -1798,8 +1940,6 @@ word n; word GC_proc_buf_size = INITIAL_BUF_SZ; char *GC_proc_buf; -page_hash_table GC_written_pages = { 0 }; /* Pages ever dirtied */ - #ifdef SOLARIS_THREADS /* We don't have exact sp values for threads. So we count on */ /* occasionally declaring stack pages to be fresh. Thus we */ @@ -1969,11 +2109,11 @@ int dummy; #undef READ -bool GC_page_was_dirty(h) +GC_bool GC_page_was_dirty(h) struct hblk *h; { register word index = PHT_HASH(h); - register bool result; + register GC_bool result; result = get_pht_entry_from_index(GC_grungy_pages, index); # ifdef SOLARIS_THREADS @@ -1987,11 +2127,11 @@ struct hblk *h; return(result); } -bool GC_page_was_ever_dirty(h) +GC_bool GC_page_was_ever_dirty(h) struct hblk *h; { register word index = PHT_HASH(h); - register bool result; + register GC_bool result; result = get_pht_entry_from_index(GC_written_pages, index); # ifdef SOLARIS_THREADS @@ -2067,7 +2207,7 @@ void GC_read_dirty() } } -bool GC_page_was_dirty(h) +GC_bool GC_page_was_dirty(h) struct hblk *h; { if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) { @@ -2090,7 +2230,7 @@ struct hblk *h; * Call stack save code for debugging. * Should probably be in mach_dep.c, but that requires reorganization. */ -#if defined(SPARC) +#if defined(SPARC) && !defined(LINUX) # if defined(SUNOS4) # include <machine/frame.h> # else @@ -2132,26 +2272,5 @@ struct callinfo info[NFRAMES]; #endif /* SAVE_CALL_CHAIN */ #endif /* SPARC */ -#ifdef SAVE_CALL_CHAIN - -void GC_print_callers (info) -struct callinfo info[NFRAMES]; -{ - register int i,j; - - GC_err_printf0("\tCall chain at allocation:\n"); - for (i = 0; i < NFRAMES; i++) { - if (info[i].ci_pc == 0) break; - GC_err_printf0("\t\targs: "); - for (j = 0; j < NARGS; j++) { - if (j != 0) GC_err_printf0(", "); - GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), - ~(info[i].ci_arg[j])); - } - GC_err_printf1("\n\t\t##PC##= 0x%X\n", info[i].ci_pc); - } -} - -#endif /* SAVE_CALL_CHAIN */ diff --git a/pcr_interface.c b/pcr_interface.c index 12e3181e..4c950933 100644 --- a/pcr_interface.c +++ b/pcr_interface.c @@ -61,7 +61,7 @@ void * GC_DebugReallocProc(void * old_object, size_t new_size_in_bytes) typedef struct { PCR_ERes (*ed_proc)(void *p, size_t size, PCR_Any data); - bool ed_pointerfree; + GC_bool ed_pointerfree; PCR_ERes ed_fail_code; PCR_Any ed_client_data; } enumerate_data; @@ -137,7 +137,7 @@ struct PCR_MM_ProcsRep GC_DebugRep = { GC_DummyShutdownProc /* mmp_shutdown */ }; -bool GC_use_debug = 0; +GC_bool GC_use_debug = 0; void GC_pcr_install() { @@ -156,7 +156,7 @@ PCR_GC_Run(void) if( !PCR_Base_TestPCRArg("-nogc") ) { GC_quiet = ( PCR_Base_TestPCRArg("-gctrace") ? 0 : 1 ); - GC_use_debug = (bool)PCR_Base_TestPCRArg("-debug_alloc"); + GC_use_debug = (GC_bool)PCR_Base_TestPCRArg("-debug_alloc"); GC_init(); if( !PCR_Base_TestPCRArg("-nogc_incremental") ) { /* @@ -196,7 +196,7 @@ void (*GC_is_visible_print_proc) GC_PROTO((GC_PTR p)) = GC_default_is_visible_print_proc; /* Could p be a stack address? */ -bool GC_on_stack(p) +GC_bool GC_on_stack(p) ptr_t p; { # ifdef THREADS @@ -248,7 +248,7 @@ ptr_t p; if (GC_on_stack(p)) return(p); hhdr = HDR((word)p); if (hhdr == 0) { - bool result; + GC_bool result; if (GC_is_static_root(p)) return(p); /* Else do it again correctly: */ @@ -20,7 +20,7 @@ signed_word GC_mem_found = 0; /* Number of words of memory reclaimed */ # ifdef FIND_LEAK -static report_leak(p, sz) +static void report_leak(p, sz) ptr_t p; word sz; { @@ -40,7 +40,7 @@ word sz; # define FOUND_FREE(hblk, word_no) \ if (abort_if_found) { \ - report_leak((long)hblk + WORDS_TO_BYTES(word_no), \ + report_leak((ptr_t)hblk + WORDS_TO_BYTES(word_no), \ HDR(hblk) -> hb_sz); \ } # else @@ -59,7 +59,7 @@ word sz; * memory. */ -bool GC_block_empty(hhdr) +GC_bool GC_block_empty(hhdr) register hdr * hhdr; { register word *p = (word *)(&(hhdr -> hb_marks[0])); @@ -85,7 +85,7 @@ register hdr * hhdr; ptr_t GC_reclaim_clear(hbp, hhdr, sz, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ register hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; register word sz; { @@ -134,7 +134,7 @@ register word sz; ptr_t GC_reclaim_clear2(hbp, hhdr, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; { register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); @@ -182,7 +182,7 @@ register ptr_t list; ptr_t GC_reclaim_clear4(hbp, hhdr, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; { register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); @@ -242,7 +242,7 @@ register ptr_t list; ptr_t GC_reclaim_uninit(hbp, hhdr, sz, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ register hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; register word sz; { @@ -283,7 +283,7 @@ register word sz; ptr_t GC_reclaim_uninit2(hbp, hhdr, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; { register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); @@ -330,7 +330,7 @@ register ptr_t list; ptr_t GC_reclaim_uninit4(hbp, hhdr, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; { register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); @@ -385,7 +385,7 @@ register ptr_t list; ptr_t GC_reclaim1(hbp, hhdr, list, abort_if_found) register struct hblk *hbp; /* ptr to current heap block */ hdr * hhdr; -bool abort_if_found; /* Abort if a reclaimable object is found */ +GC_bool abort_if_found; /* Abort if a reclaimable object is found */ register ptr_t list; { register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); @@ -518,7 +518,7 @@ word abort_if_found; /* Abort if a reclaimable object is found */ GC_freehblk(hbp); } } else { - bool empty = GC_block_empty(hhdr); + GC_bool empty = GC_block_empty(hhdr); if (abort_if_found) { GC_reclaim_small_nonempty_block(hbp, (int)abort_if_found); } else if (empty) { @@ -675,9 +675,9 @@ int kind; * recently reclaimed, and discard the rest. * Stop_func may be 0. */ -bool GC_reclaim_all(stop_func, ignore_old) +GC_bool GC_reclaim_all(stop_func, ignore_old) GC_stop_func stop_func; -bool ignore_old; +GC_bool ignore_old; { register word sz; register int kind; @@ -25,7 +25,7 @@ #include <stdio.h> #include <setjmp.h> #include <string.h> -#include "config.h" +#include "gcconfig.h" #ifdef OS2 /* GETPAGESIZE() is set to getpagesize() by default, but that */ diff --git a/solaris_pthreads.c b/solaris_pthreads.c index cc72b2dc..58424577 100644 --- a/solaris_pthreads.c +++ b/solaris_pthreads.c @@ -36,6 +36,8 @@ # define _CLASSIC_XOPEN_TYPES # include <unistd.h> # include <errno.h> +# include "solaris_threads.h" +# include <stdio.h> #undef pthread_join #undef pthread_create @@ -44,7 +46,7 @@ pthread_cond_t GC_prom_join_cv; /* Broadcast when any thread terminates */ pthread_cond_t GC_create_cv; /* Signalled when a new undetached */ /* thread starts. */ -extern bool GC_multithreaded; +extern GC_bool GC_multithreaded; /* We use the allocation lock to protect thread-related data structures. */ @@ -163,7 +165,7 @@ GC_pthread_create(pthread_t *new_thread, # else #ifndef LINT - int GC_no_sunOS_threads; + int GC_no_sunOS_pthreads; #endif # endif /* SOLARIS_THREADS */ diff --git a/solaris_threads.c b/solaris_threads.c index d83ffbb5..1f5ebcdc 100644 --- a/solaris_threads.c +++ b/solaris_threads.c @@ -227,7 +227,7 @@ static void stop_all_lwps() char buf[30]; prstatus_t status; register int i; - bool changed; + GC_bool changed; lwpid_t me = _lwp_self(); if (GC_main_proc_fd == -1) { @@ -342,7 +342,7 @@ static void restart_all_lwps() { int lwp_fd; register int i; - bool changed; + GC_bool changed; lwpid_t me = _lwp_self(); # define PARANOID @@ -392,7 +392,7 @@ static void restart_all_lwps() if (i >= max_lwps) ABORT("Too many lwps"); } -bool GC_multithreaded = 0; +GC_bool GC_multithreaded = 0; void GC_stop_world() { @@ -410,7 +410,7 @@ void GC_start_world() void GC_thr_init(void); -bool GC_thr_initialized = FALSE; +GC_bool GC_thr_initialized = FALSE; size_t GC_min_stack_sz; @@ -565,7 +565,7 @@ GC_thread GC_new_thread(thread_t id) int hv = ((word)id) % THREAD_TABLE_SZ; GC_thread result; static struct GC_Thread_Rep first_thread; - static bool first_thread_used = FALSE; + static GC_bool first_thread_used = FALSE; if (!first_thread_used) { result = &first_thread; @@ -645,8 +645,6 @@ void GC_my_stack_limits() } -extern ptr_t GC_approx_sp(); - /* We hold allocation lock. We assume the world is stopped. */ void GC_push_all_stacks() { @@ -823,7 +821,7 @@ int GC_thr_join(thread_t wait_for, thread_t *departed, void **status) LOCK(); if (wait_for == 0) { register int i; - register bool thread_exists; + register GC_bool thread_exists; for (;;) { thread_exists = FALSE; diff --git a/solaris_threads.h b/solaris_threads.h index d13dd554..b2cdb36e 100644 --- a/solaris_threads.h +++ b/solaris_threads.h @@ -24,7 +24,7 @@ } * GC_thread; extern GC_thread GC_new_thread(thread_t id); - extern bool GC_thr_initialized; + extern GC_bool GC_thr_initialized; extern volatile GC_thread GC_threads[]; extern size_t GC_min_stack_sz; extern size_t GC_page_sz; @@ -73,7 +73,7 @@ void GC_stubborn_init() /* Invariant while this is running: GC_changing_list_current */ /* points at a word containing 0. */ /* Returns FALSE on failure. */ -bool GC_compact_changing_list() +GC_bool GC_compact_changing_list() { register GC_PTR *p, *q; register word count = 0; @@ -139,7 +139,7 @@ GC_PTR p; # else register GC_PTR * my_current = GC_changing_list_current; # endif - register bool tried_quick; + register GC_bool tried_quick; DCL_LOCK_STATE; if (*my_current == p) { @@ -252,7 +252,7 @@ void GC_read_changed() } } -bool GC_page_was_changed(h) +GC_bool GC_page_was_changed(h) struct hblk * h; { register word index = PHT_HASH(h); @@ -24,8 +24,8 @@ # include <assert.h> /* Not normally used, but handy for debugging. */ # include "gc.h" # include "gc_typed.h" -# include "gc_priv.h" /* For output and some statistics */ -# include "config.h" +# include "gc_priv.h" /* For output, locking, and some statistics */ +# include "gcconfig.h" # ifdef MSWIN32 # include <windows.h> @@ -45,7 +45,7 @@ # include <synch.h> # endif -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) # include <pthread.h> # endif @@ -266,6 +266,72 @@ struct { #define a A.aa /* + * A tiny list reversal test to check thread creation. + */ +#ifdef THREADS + +# ifdef WIN32_THREADS + unsigned __stdcall tiny_reverse_test(void * arg) +# else + void * tiny_reverse_test(void * arg) +# endif +{ + check_ints(reverse(reverse(ints(1,10))), 1, 10); + return 0; +} + +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(SOLARIS_PTHREADS) + void fork_a_thread() + { + pthread_t t; + int code; + if ((code = pthread_create(&t, 0, tiny_reverse_test, 0)) != 0) { + (void)GC_printf1("Small thread creation failed %lu\n", + (unsigned long)code); + FAIL; + } + if ((code = pthread_join(t, 0)) != 0) { + (void)GC_printf1("Small thread join failed %lu\n", + (unsigned long)code); + FAIL; + } + } + +# elif defined(WIN32_THREADS) + void fork_a_thread() + { + unsigned thread_id; + HANDLE h; + h = (HANDLE)_beginthreadex(NULL, 0, tiny_reverse_test, + 0, 0, &thread_id); + if (h == (HANDLE)-1) { + (void)GC_printf1("Small thread creation failed %lu\n", + (unsigned long)GetLastError()); + FAIL; + } + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + (void)GC_printf1("Small thread wait failed %lu\n", + (unsigned long)GetLastError()); + FAIL; + } + } + +/* # elif defined(SOLARIS_THREADS) */ + +# else + +# define fork_a_thread() + +# endif + +#else + +# define fork_a_thread() + +#endif + +/* * Repeatedly reverse lists built out of very different sized cons cells. * Check that we didn't lose anything. */ @@ -327,6 +393,7 @@ void reverse_test() check_ints(b,1,50); check_ints(a,1,49); for (i = 0; i < 60; i++) { + if (i % 10 == 0) fork_a_thread(); /* This maintains the invariant that a always points to a list of */ /* 49 integers. Thus this is thread safe without locks, */ /* assuming atomic pointer assignments. */ @@ -386,7 +453,7 @@ VOLATILE int dropped_something = 0; static mutex_t incr_lock; mutex_lock(&incr_lock); # endif -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&incr_lock); # endif @@ -404,7 +471,7 @@ VOLATILE int dropped_something = 0; # ifdef SOLARIS_THREADS mutex_unlock(&incr_lock); # endif -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) pthread_mutex_unlock(&incr_lock); # endif # ifdef WIN32_THREADS @@ -465,7 +532,7 @@ int n; static mutex_t incr_lock; mutex_lock(&incr_lock); # endif -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&incr_lock); # endif @@ -481,7 +548,7 @@ int n; # ifdef SOLARIS_THREADS mutex_unlock(&incr_lock); # endif -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) pthread_mutex_unlock(&incr_lock); # endif # ifdef WIN32_THREADS @@ -538,11 +605,14 @@ int n; chktree(t -> rchild, n-1); } -# ifdef SOLARIS_THREADS +# if defined(SOLARIS_THREADS) && !defined(_SOLARIS_PTHREADS) thread_key_t fl_key; void * alloc8bytes() { +# ifdef SMALL_CONFIG + return(GC_malloc(8)); +# else void ** my_free_list_ptr; void * my_free_list; @@ -568,10 +638,48 @@ void * alloc8bytes() *my_free_list_ptr = GC_NEXT(my_free_list); GC_NEXT(my_free_list) = 0; return(my_free_list); +# endif } #else -# define alloc8bytes() GC_MALLOC_ATOMIC(8) + +# if defined(_SOLARIS_PTHREADS) || defined(IRIX_THREADS) \ + || defined(LINUX_THREADS) +pthread_key_t fl_key; + +void * alloc8bytes() +{ +# ifdef SMALL_CONFIG + return(GC_malloc(8)); +# else + void ** my_free_list_ptr; + void * my_free_list; + + my_free_list_ptr = (void **)pthread_getspecific(fl_key); + if (my_free_list_ptr == 0) { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { + (void)GC_printf0("pthread_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) { + my_free_list = GC_malloc_many(8); + if (my_free_list == 0) { + (void)GC_printf0("alloc8bytes out of memory\n"); + FAIL; + } + } + *my_free_list_ptr = GC_NEXT(my_free_list); + GC_NEXT(my_free_list) = 0; + return(my_free_list); +# endif +} + +# else +# define alloc8bytes() GC_MALLOC_ATOMIC(8) +# endif #endif void alloc_small(n) @@ -646,7 +754,7 @@ void typed_test() # endif GC_descr d3 = GC_make_descriptor(&bm_large, 32); GC_descr d4 = GC_make_descriptor(bm_huge, 320); - GC_word * x = GC_malloc_explicitly_typed(2000, d4); + GC_word * x = (GC_word *)GC_malloc_explicitly_typed(2000, d4); register int i; old = 0; @@ -811,7 +919,7 @@ void run_one_test() LOCK(); n_tests++; UNLOCK(); - /* GC_printf1("Finished %x\n", pthread_self()); */ + /* GC_printf1("Finished %x\n", pthread_self()); */ } void check_heap_stats() @@ -819,6 +927,7 @@ void check_heap_stats() unsigned long max_heap_sz; register int i; int still_live; + int late_finalize_count = 0; if (sizeof(char *) > 4) { max_heap_sz = 13000000; @@ -836,11 +945,18 @@ void check_heap_stats() while (GC_collect_a_little()) { } for (i = 0; i < 16; i++) { GC_gcollect(); + late_finalize_count += GC_invoke_finalizers(); } (void)GC_printf1("Completed %lu tests\n", (unsigned long)n_tests); (void)GC_printf2("Finalized %lu/%lu objects - ", (unsigned long)finalized_count, (unsigned long)finalizable_count); +# ifdef FINALIZE_ON_DEMAND + if (finalized_count != late_finalize_count) { + (void)GC_printf0("Demand finalization error\n"); + FAIL; + } +# endif if (finalized_count > finalizable_count || finalized_count < finalizable_count/2) { (void)GC_printf0("finalization is probably broken\n"); @@ -857,8 +973,7 @@ void check_heap_stats() i = finalizable_count - finalized_count - still_live; if (0 != i) { (void)GC_printf2 - ("%lu disappearing links remain and %lu more objects " - "were not finalized\n", + ("%lu disappearing links remain and %lu more objects were not finalized\n", (unsigned long) still_live, (unsigned long)i); if (i > 10) { GC_printf0("\tVery suspicious!\n"); @@ -914,7 +1029,8 @@ void SetMinimumStack(long minSize) } -#if !defined(PCR) && !defined(SOLARIS_THREADS) && !defined(WIN32_THREADS) && !defined(IRIX_THREADS) || defined(LINT) +#if !defined(PCR) && !defined(SOLARIS_THREADS) && !defined(WIN32_THREADS) \ + && !defined(IRIX_THREADS) && !defined(LINUX_THREADS) || defined(LINT) #ifdef MSWIN32 int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) #else @@ -968,6 +1084,9 @@ void SetMinimumStack(long minSize) GC_set_max_heap_size, GC_get_bytes_since_gc, GC_pre_incr, GC_post_incr); # endif +# ifdef MSWIN32 + GC_win32_free_heap(); +# endif return(0); } # endif @@ -1042,7 +1161,7 @@ test() } #endif -#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) +#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) || defined(LINUX_THREADS) void * thr_run_one_test(void * arg) { run_one_test(); @@ -1100,13 +1219,23 @@ main() # ifdef IRIX_THREADS /* Force a larger stack to be preallocated */ /* Since the initial cant always grow later. */ - *((char *)&code - 1024*1024) = 0; /* Require 1 Mb */ + *((volatile char *)&code - 1024*1024) = 0; /* Require 1 Mb */ # endif /* IRIX_THREADS */ pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 1000000); +# ifdef IRIX_THREADS + pthread_attr_setstacksize(&attr, 1000000); +# endif n_tests = 0; - GC_enable_incremental(); +# ifdef MPROTECT_VDB + GC_enable_incremental(); + (void) GC_printf0("Switched to incremental mode\n"); + (void) GC_printf0("Emulating dirty bits with mprotect/signals\n"); +# endif (void) GC_set_warn_proc(warn_proc); + if (pthread_key_create(&fl_key, 0) != 0) { + (void)GC_printf1("Key creation failed %lu\n", (unsigned long)code); + FAIL; + } if ((code = pthread_create(&th1, &attr, thr_run_one_test, 0)) != 0) { (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); FAIL; @@ -1131,4 +1260,4 @@ main() return(0); } #endif /* pthreads */ -#endif /* SOLARIS_THREADS || IRIX_THREADS */ +#endif /* SOLARIS_THREADS || IRIX_THREADS || LINUX_THREADS */ diff --git a/test_cpp.cc b/test_cpp.cc index 058022c4..3160b098 100644 --- a/test_cpp.cc +++ b/test_cpp.cc @@ -34,9 +34,9 @@ few minutes to complete. extern "C" { #include "gc_priv.h" } -# ifdef MSWIN32 -# include <windows.h> -# endif +#ifdef MSWIN32 +# include <windows.h> +#endif #define my_assert( e ) \ @@ -174,11 +174,20 @@ int APIENTRY WinMain( if (0 == argv[ argc ]) break;} #else -int main( int argc, char* argv[] ) { +# ifdef MACOS + int main() { +# else + int main( int argc, char* argv[] ) { +# endif #endif +# if defined(MACOS) // MacOS + char* argv_[] = {"test_cpp", "10"}; // doesn't + argv = argv_; // have a + argc = sizeof(argv_)/sizeof(argv_[0]); // commandline +# endif int i, iters, n; -# ifndef __GNUC__ +# if !defined(__GNUC__) && !defined(MACOS) int *x = (int *)alloc::allocate(sizeof(int)); *x = 29; @@ -219,7 +228,11 @@ int main( int argc, char* argv[] ) { if (0 == i % 10) { B::Deleting( 1 ); delete b; - B::Deleting( 0 );}} + B::Deleting( 0 );} +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + } /* Make sure the uncollectable As and Bs are still there. */ for (i = 0; i < 1000; i++) { @@ -230,7 +243,12 @@ int main( int argc, char* argv[] ) { b->Test( i ); B::Deleting( 1 ); delete b; - B::Deleting( 0 );} + B::Deleting( 0 ); +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + + } /* Make sure most of the finalizable Cs, Ds, and Fs have gone away. */ @@ -238,7 +256,7 @@ int main( int argc, char* argv[] ) { D::Test(); F::Test();} -# ifndef __GNUC__ +# if !defined(__GNUC__) && !defined(MACOS) my_assert (29 == x[3]); # endif GC_printf0( "The test appears to have succeeded.\n" ); diff --git a/threadlibs.c b/threadlibs.c index bd549172..4a0a6cfc 100644 --- a/threadlibs.c +++ b/threadlibs.c @@ -1,9 +1,9 @@ -# include "config.h" +# include "gcconfig.h" # include <stdio.h> int main() { -# ifdef IRIX_THREADS +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) printf("-lpthread\n"); # endif # ifdef SOLARIS_THREADS @@ -46,7 +46,7 @@ # define EXTRA_BYTES (sizeof(word)) # endif -bool GC_explicit_typing_initialized = FALSE; +GC_bool GC_explicit_typing_initialized = FALSE; int GC_explicit_kind; /* Object kind for objects with indirect */ /* (possibly extended) descriptors. */ @@ -59,7 +59,7 @@ int GC_array_kind; /* Object kind for objects with complex */ /* can be described by a BITMAP_BITS sized bitmap. */ typedef struct { word ed_bitmap; /* lsb corresponds to first word. */ - bool ed_continued; /* next entry is continuation. */ + GC_bool ed_continued; /* next entry is continuation. */ } ext_descr; /* Array descriptors. GC_array_mark_proc understands these. */ @@ -430,7 +430,8 @@ word env; if (bm & 1) { current = *current_p; if ((ptr_t)current >= least_ha && (ptr_t)current <= greatest_ha) { - PUSH_CONTENTS(current, mark_stack_ptr, mark_stack_limit); + PUSH_CONTENTS(current, mark_stack_ptr, + mark_stack_limit, current_p, exit1); } } } @@ -590,7 +591,7 @@ word env; if (last_set_bit < 0) return(0 /* no pointers */); # if ALIGNMENT == CPP_WORDSZ/8 { - register bool all_bits_set = TRUE; + register GC_bool all_bits_set = TRUE; for (i = 0; i < last_set_bit; i++) { if (!GC_get_bit(bm, i)) { all_bits_set = FALSE; @@ -1,5 +1,5 @@ #define GC_VERSION_MAJOR 4 -#define GC_VERSION_MINOR 12 +#define GC_VERSION_MINOR 13 #define GC_ALPHA_VERSION GC_NOT_ALPHA # define GC_NOT_ALPHA 0xff diff --git a/win32_threads.c b/win32_threads.c index eb485bdc..2b01c5c6 100755 --- a/win32_threads.c +++ b/win32_threads.c @@ -8,22 +8,32 @@ #define MAX_THREADS 64 struct thread_entry { + LONG in_use; DWORD id; HANDLE handle; - void *stack; /* The cold end of the stack. */ + void *stack; /* The cold end of the stack. */ + /* 0 ==> entry not valid. */ + /* !in_use ==> stack == 0 */ CONTEXT context; + GC_bool suspended; }; -struct thread_entry thread_table[MAX_THREADS]; +volatile GC_bool GC_please_stop = FALSE; + +volatile struct thread_entry thread_table[MAX_THREADS]; void GC_stop_world() { DWORD thread_id = GetCurrentThreadId(); int i; + + GC_please_stop = TRUE; for (i = 0; i < MAX_THREADS; i++) - if (thread_table[i].stack != 0 && thread_table[i].id != thread_id) { + if (thread_table[i].stack != 0 + && thread_table[i].id != thread_id) { if (SuspendThread(thread_table[i].handle) == (DWORD)-1) ABORT("SuspendThread failed"); + thread_table[i].suspended = TRUE; } } @@ -32,10 +42,13 @@ void GC_start_world() DWORD thread_id = GetCurrentThreadId(); int i; for (i = 0; i < MAX_THREADS; i++) - if (thread_table[i].stack != 0 && thread_table[i].id != thread_id) { + if (thread_table[i].stack != 0 && thread_table[i].suspended + && thread_table[i].id != thread_id) { if (ResumeThread(thread_table[i].handle) == (DWORD)-1) ABORT("ResumeThread failed"); + thread_table[i].suspended = FALSE; } + GC_please_stop = FALSE; } ptr_t GC_current_stackbottom() @@ -117,20 +130,34 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) switch (reason) { case DLL_PROCESS_ATTACH: InitializeCriticalSection(&GC_allocate_ml); + GC_init(); /* Force initialization before thread attach. */ /* fall through */ case DLL_THREAD_ATTACH: { int i; - LOCK(); + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preeemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* inititalization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + /* The following should be a noop according to the win32 */ /* documentation. There is empirical evidence that it */ /* isn't. - HB */ - if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); - for (i = 0; thread_table[i].stack != 0; i++) { +# ifndef SMALL_CONFIG + if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); +# endif + + for (i = 0; InterlockedExchange(&thread_table[i].in_use,1) != 0; i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ if (i == MAX_THREADS - 1) ABORT("too many threads"); } - thread_table[i].stack = GC_get_stack_base(); thread_table[i].id = GetCurrentThreadId(); if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), @@ -143,7 +170,11 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) GC_printf1("Last error code: %lx\n", last_error); ABORT("DuplicateHandle failed"); } - UNLOCK(); + thread_table[i].stack = GC_get_stack_base(); + /* If this thread is being created while we are trying to stop */ + /* the world, wait here. Hopefully this can't happen on any */ + /* systems that don't allow us to block here. */ + while (GC_please_stop) Sleep(20); } break; case DLL_PROCESS_DETACH: @@ -152,10 +183,14 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) int i; DWORD thread_id = GetCurrentThreadId(); LOCK(); - for (i = 0; thread_table[i].stack == 0 || thread_table[i].id != thread_id; i++) + for (i = 0; + thread_table[i].stack == 0 || thread_table[i].id != thread_id; + i++) { if (i == MAX_THREADS - 1) ABORT("thread not found on detach"); + } thread_table[i].stack = 0; + thread_table[i].in_use = FALSE; CloseHandle(thread_table[i].handle); BZERO(&thread_table[i].context, sizeof(CONTEXT)); UNLOCK(); |