# Makefile for Borland C++ 4.5 on NT\r
+# For Borland 5.0, replace bc45 by bc5.\r
+# If you have the Borland assembler, remove "-DUSE_GENERIC"\r
#\r
bc= c:\bc45\r
bcbin= $(bc)\bin\r
cflags= -R -v -vi -H -H=gc.csm -I$(bcinclude);cord -L$(bclib) \\r
-w-pro -w-aus -w-par -w-ccc -w-rch -a4 -D__STDC__=0\r
#defines= -DSILENT\r
-defines= -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS\r
+defines= -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS -DUSE_GENERIC\r
\r
.c.obj:\r
$(cc) @&&|\r
XXXmach_dep.obj XXXos_dep.obj XXXmark_rts.obj XXXheaders.obj XXXmark.obj \\r
XXXobj_map.obj XXXblacklst.obj XXXfinalize.obj XXXnew_hblk.obj \\r
XXXdbg_mlc.obj XXXmalloc.obj XXXstubborn.obj XXXdyn_load.obj \\r
- XXXtypd_mlc.obj XXXptr_chck.obj XXXgc_cpp.obj\r
+ XXXtypd_mlc.obj XXXptr_chck.obj XXXgc_cpp.obj XXXmallocx.obj\r
\r
OBJS= $(XXXOBJS:XXX=)\r
\r
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 dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.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 dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.o mallocx.o
CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o
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\runloading shared library.
by Patrick C. Beard.
*/
-/* Boehm, November 17, 1995 11:50 am PST */
+/* Boehm, February 15, 1996 2:55 pm PST */
#include <Resources.h>
#include <Memory.h>
#include <stdlib.h>
#include <string.h>
+#include "gc.h"
+#include "gc_priv.h"
+
// use 'CODE' resource 0 to get exact location of the beginning of global space.
typedef struct {
void GC_MacFreeTemporaryMemory(void);
-Ptr GC_MacTemporaryNewPtr(Size size, Boolean clearMemory)
+Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory)
{
static Boolean firstTime = true;
OSErr result;
theTemporaryMemory = tempMemBlock;
}
+# if !defined(SHARED_LIBRARY_BUILD)
// install an exit routine to clean up the memory used at the end.
if (firstTime) {
atexit(&GC_MacFreeTemporaryMemory);
firstTime = false;
}
+# endif
return tempPtr;
}
+extern word GC_fo_entries;
+
+static void perform_final_collection()
+{
+ unsigned i;
+ word last_fo_entries = 0;
+
+ /* adjust the stack bottom, because CFM calls us from another stack
+ location. */
+ GC_stackbottom = (ptr_t)&i;
+
+ /* try to collect and finalize everything in sight */
+ for (i = 0; i < 2 || GC_fo_entries < last_fo_entries; i++) {
+ last_fo_entries = GC_fo_entries;
+ GC_gcollect();
+ }
+}
+
+
void GC_MacFreeTemporaryMemory()
{
+# if defined(SHARED_LIBRARY_BUILD)
+ /* if possible, collect all memory, and invoke all finalizers. */
+ perform_final_collection();
+# endif
+
+ if (theTemporaryMemory != NULL) {
long totalMemoryUsed = 0;
TemporaryMemoryHandle tempMemBlock = theTemporaryMemory;
while (tempMemBlock != NULL) {
tempMemBlock = nextBlock;
}
theTemporaryMemory = NULL;
- fprintf(stdout, "[total memory used: %ld bytes.]\n", totalMemoryUsed);
+
+# if !defined(SHARED_LIBRARY_BUILD)
+ fprintf(stdout, "[total memory used: %ld bytes.]\n",
+ totalMemoryUsed);
+ fprintf(stdout, "[total collections: %ld.]\n", GC_gc_no);
+# endif
+ }
}
# 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=gcc
+CC=cc
+CXX=CC
AS=as
# 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.
-CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT -DSOLARIS_THREADS
+CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT -DATOMIC_UNCOLLECTABLE
# Setjmp_test may yield overly optimistic results when compiled
# without optimization.
# (Clients should also define SOLARIS_THREADS and then include
# gc.h before performing thr_ or dl* or GC_ operations.)
# This is broken on nonSPARC machines.
+# -DIRIX_THREADS enables support for Irix pthreads. See README.irix.
# -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,
# implementations, and it sometimes has a significant performance
# impact. However, it is dangerous for many not-quite-ANSI C
# programs that call things like printf in asynchronous signal handlers.
+# -DNO_EXECUTE_PERMISSION may cause some or all of the heap to not
+# have execute permission, i.e. it may be impossible to execute
+# code from the heap. Currently this only affects the incremental
+# collector on UNIX machines. It may greatly improve its performance,
+# since this may avoid some expensive cache synchronization.
# -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the
# new syntax "operator new[]" for allocating and deleting arrays.
# See gc_cpp.h for details. No effect on the C part of the collector.
# existing code, but it often does. Neither works on all platforms,
# 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.
+# -DIGNORE_FREE turns calls to free into a noop. Only useful with
+# -DREDIRECT_MALLOC.
+# -DNO_DEBUGGING removes GC_dump and the debugging routines it calls.
# Reduces code size slightly at the expense of debuggability.
+# -DJAVA_FINALIZATION makes it somewhat safer to finalize objects out of
+# order by specifying a nonstandard finalization mark procedure (see
+# finalize.c). Objects reachable from finalizable objects will be marked
+# in a sepearte postpass, and hence their memory won't be reclaimed.
+# Not recommended unless you are implementing a language that specifies
+# these semantics.
+# -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.
LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \
-DREDIRECT_MALLOC=GC_malloc_uncollectable \
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 typd_mlc.o ptr_chck.o mit_threads.o dec_threads.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 typd_mlc.o ptr_chck.o mallocx.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 typd_mlc.c ptr_chck.c mit_threads.c dec_threads.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 typd_mlc.c ptr_chck.c mallocx.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
CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o
-SRCS= $(CSRCS) mips_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \
+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 \
- if_mach.c if_not_there.c gc_cpp.cc gc_cpp.h weakpointer.h \
- gcc_support.c $(CORD_SRCS)
+ 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)
OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \
README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \
MacOS.c EMX_MAKEFILE makefile.depend README.debugging \
include/gc_cpp.h Mac_files/datastart.c Mac_files/dataend.c \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \
- add_gc_prefix.c
+ add_gc_prefix.c README.solaris2 README.sgi README.hp README.uts \
+ win32_threads.c NT_THREADS_MAKEFILE gc.mak README.dj Makefile.dj
CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \
$(srcdir)/cord/private/cord_pos.h
+UTILS= if_mach if_not_there threadlibs
+
# Libraries needed for curses applications. Only needed for de.
CURSES= -lcurses -ltermlib
mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h
-gc.a: $(OBJS) dyn_load.o
+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
./if_mach SPARC SUNOS5 $(AR) rus gc.a $(OBJS) dyn_load.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
+cords: $(CORD_OBJS) cord/cordtest $(UTILS)
rm -f on_sparc_sunos5
./if_mach SPARC SUNOS5 touch on_sparc_sunos5
./if_mach SPARC SUNOS5 $(AR) rus gc.a $(CORD_OBJS)
gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h $(srcdir)/gc.h Makefile
$(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc
-
-test_cpp: $(srcdir)/test_cpp.cc $(srcdir)/gc_cpp.h gc_cpp.o $(srcdir)/gc.h gc.a
+
+test_cpp: $(srcdir)/test_cpp.cc $(srcdir)/gc_cpp.h gc_cpp.o $(srcdir)/gc.h \
+base_lib $(UTILS)
rm -f test_cpp
- ./if_mach SPARC SUNOS5 $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a -lthread -ldl
- ./if_not_there test_cpp $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a
+ ./if_mach HP_PA "" $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a -ldld
+ ./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_not_there on_sparc_sunos5 $(AR) ru gc.a gc_cpp.o
./if_not_there on_sparc_sunos5 $(RANLIB) gc.a || cat /dev/null
./test_cpp 1
+ echo > c++
dyn_load_sunos53.o: dyn_load.c
$(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@
libirixgc.so: $(OBJS) dyn_load.o
ld -shared -o libirixgc.so $(OBJS) dyn_load.o -lc
-mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_mach_dep.s $(srcdir)/rs6000_mach_dep.s if_mach if_not_there
+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
- ./if_mach MIPS "" $(AS) -o mach_dep.o $(srcdir)/mips_mach_dep.s
+ ./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_mach_dep.s
+ ./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
+ ./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
./if_mach RS6000 "" $(AS) -o mach_dep.o $(srcdir)/rs6000_mach_dep.s
./if_mach ALPHA "" $(AS) -o mach_dep.o $(srcdir)/alpha_mach_dep.s
./if_mach SPARC SUNOS5 $(AS) -o mach_dep.o $(srcdir)/sparc_mach_dep.s
./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c
-mark_rts.o: $(srcdir)/mark_rts.c if_mach if_not_there
+mark_rts.o: $(srcdir)/mark_rts.c if_mach if_not_there $(UTILS)
rm -f mark_rts.o
- -./if_mach ALPHA "" $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c
+ -./if_mach ALPHA OSF1 $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c
./if_not_there mark_rts.o $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c
# Work-around for DEC optimizer tail recursion elimination bug.
# The ALPHA-specific line should be removed if gcc is used.
$(CC) $(CFLAGS) -c $(srcdir)/cord/cordprnt.c
mv cordprnt.o cord/cordprnt.o
-cord/cordtest: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a
+cord/cordtest: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a $(UTILS)
rm -f cord/cordtest
- ./if_mach SPARC SUNOS5 $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -lthread -ldl
./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -lucb
- ./if_not_there cord/cordtest $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a
+ ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -ldld
+ ./if_not_there cord/cordtest $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a `./threadlibs`
-cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a
+cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(UTILS)
rm -f cord/de
- ./if_mach SPARC SUNOS5 $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lthread -ldl
./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 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_not_there cord/de $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES)
+ ./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
$(CC) $(CFLAGS) -o if_mach $(srcdir)/if_mach.c
+threadlibs: $(srcdir)/threadlibs.c $(srcdir)/config.h Makefile
+ $(CC) $(CFLAGS) -o threadlibs $(srcdir)/threadlibs.c
+
if_not_there: $(srcdir)/if_not_there.c
$(CC) $(CFLAGS) -o if_not_there $(srcdir)/if_not_there.c
clean:
rm -f gc.a *.o gctest gctest_dyn_link test_cpp \
setjmp_test mon.out gmon.out a.out core if_not_there if_mach \
- $(CORD_OBJS) cord/cordtest cord/de
+ threadlibs $(CORD_OBJS) cord/cordtest cord/de
-rm -f *~
gctest: test.o gc.a if_mach if_not_there
rm -f gctest
- ./if_mach SPARC SUNOS5 $(CC) $(CFLAGS) -o gctest test.o gc.a -lthread -ldl
./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest test.o gc.a -lucb
- ./if_not_there gctest $(CC) $(CFLAGS) -o gctest test.o gc.a
+ ./if_mach HP_PA "" $(CC) $(CFLAGS) -o gctest test.o gc.a -ldld
+ ./if_not_there gctest $(CC) $(CFLAGS) -o gctest test.o gc.a `./threadlibs`
# If an optimized setjmp_test generates a segmentation fault,
# odds are your compiler is broken. Gctest may still work.
./gctest
add_gc_prefix: add_gc_prefix.c
- $(CC) -o add_gc_prefix add_gc_prefix.c
-
+ $(CC) -o add_gc_prefix $(srcdir)/add_gc_prefix.c
+
gc.tar: $(SRCS) $(OTHER_FILES) add_gc_prefix
- tar cvfh gc.tar `add_gc_prefix $(SRCS) $(OTHER_FILES)`
-
+ ./add_gc_prefix $(SRCS) $(OTHER_FILES) > /tmp/gc.tar-files
+ (cd $(srcdir)/.. ; tar cvfh - `cat /tmp/gc.tar-files`) > gc.tar
+
pc_gc.tar: $(SRCS) $(OTHER_FILES)
tar cvfX pc_gc.tar pc_excludes $(SRCS) $(OTHER_FILES)
lint: $(CSRCS) test.c
lint -DLINT $(CSRCS) test.c | egrep -v "possible pointer alignment problem|abort|exit|sbrk|mprotect|syscall"
-
+
# BTL: added to test shared library version of collector.
# Currently works only under SunOS5. Requires GC_INIT call from statically
# loaded client code.
--- /dev/null
+# Primary targets:
+# gc.a - builds basic library
+# libgc.a - builds library for use with g++ "-fgc-keyword" extension
+# c++ - adds C++ interface to library
+# cords - adds cords (heavyweight strings) to library
+# test - prints porting information, then builds basic version of gc.a,
+# 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=gcc
+CXX=gxx
+
+CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT
+
+# Setjmp_test may yield overly optimistic results when compiled
+# without optimization.
+# -DSILENT disables statistics printing, and improves performance.
+# -DFIND_LEAK causes the collector to assume that all inaccessible
+# objects should have been explicitly deallocated, and reports exceptions.
+# Finalization and the test program are not usable in this mode.
+# -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,
+# usually causing it to use less space in such situations.
+# Incremental collection no longer works in this case.
+# -DLARGE_CONFIG tunes the collector for unusually large heaps.
+# Necessary for heaps larger than about 500 MB on most machines.
+# Recommended for heaps larger than about 64 MB.
+# -DDONT_ADD_BYTE_AT_END is meaningful only with
+# -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS
+# causes all objects to be padded so that pointers just past the end of
+# an object can be recognized. This can be expensive. (The padding
+# is normally more than one byte due to alignment constraints.)
+# -DDONT_ADD_BYTE_AT_END disables the padding.
+# -DNO_SIGNALS does not disable signals during critical parts of
+# the GC process. This is no less correct than many malloc
+# implementations, and it sometimes has a significant performance
+# impact. However, it is dangerous for many not-quite-ANSI C
+# programs that call things like printf in asynchronous signal handlers.
+# -DNO_EXECUTE_PERMISSION may cause some or all of the heap to not
+# have execute permission, i.e. it may be impossible to execute
+# code from the heap. Currently this only affects the incremental
+# collector on UNIX machines. It may greatly improve its performance,
+# since this may avoid some expensive cache synchronization.
+# -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the
+# new syntax "operator new[]" for allocating and deleting arrays.
+# See gc_cpp.h for details. No effect on the C part of the collector.
+# This is defined implicitly in a few environments.
+# -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be defined
+# as aliases for X, GC_realloc, and GC_free, respectively.
+# Calloc is redefined in terms of the new malloc. X should
+# be either GC_malloc or GC_malloc_uncollectable.
+# The former is occasionally useful for working around leaks in code
+# you don't want to (or can't) look at. It may not work for
+# existing code, but it often does. Neither works on all platforms,
+# since some ports use malloc or calloc to obtain system memory.
+# (Probably works for UNIX, and win32.)
+# -DIGNORE_FREE turns calls to free into a noop. Only useful with
+# -DREDIRECT_MALLOC.
+# -DNO_DEBUGGING removes GC_dump and the debugging routines it calls.
+# Reduces code size slightly at the expense of debuggability.
+
+LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \
+ -DREDIRECT_MALLOC=GC_malloc_uncollectable \
+ -DDONT_ADD_BYTE_AT_END -DALL_INTERIOR_POINTERS
+# Flags for building libgc.a -- the last two are required.
+
+CXXFLAGS= $(CFLAGS) -DOPERATOR_NEW_ARRAY
+AR= ar
+RANLIB= ranlib
+
+
+# Redefining srcdir allows object code for the nonPCR version of the collector
+# to be generated in different directories. In this case, the destination
+directory
+# should contain a copy of the original include directory.
+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 typd_mlc.o ptr_chck.o mallocx.o
+
+CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o
+
+CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \
+ $(srcdir)/cord/private/cord_pos.h
+
+# Libraries needed for curses applications. Only needed for de.
+CURSES= -lcurses -ltermlib
+
+# The following is irrelevant on most systems. But a few
+# versions of make otherwise fork the shell specified in
+# the SHELL environment variable.
+SHELL= /bin/sh
+
+SPECIALCFLAGS =
+# Alternative flags to the C compiler for mach_dep.c.
+# Mach_dep.c often doesn't like optimization, and it's
+# not time-critical anyway.
+# Set SPECIALCFLAGS to -q nodirect_code on Encore.
+
+all: gc.a gctest
+
+$(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
+# 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
+
+mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h
+
+gc.a: $(OBJS) dyn_load.o
+ $(AR) ru gc.a $(OBJS) dyn_load.o
+ $(RANLIB) gc.a
+
+libgc.a:
+ make CFLAGS="$(LIBGC_CFLAGS)" clean gc.a gcc_support.o
+ move gc.a libgc.a
+ -del 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
+ -del on_sparc_sunos5
+ ./if_mach SPARC SUNOS5 touch on_sparc_sunos5
+ ./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
+
+gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h $(srcdir)/gc.h Makefile
+ $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc
+
+test_cpp: $(srcdir)/test_cpp.cc $(srcdir)/gc_cpp.h gc_cpp.o $(srcdir)/gc.h gc.a
+ -del test_cpp
+ $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a
+
+
+c++: gc_cpp.o $(srcdir)/gc_cpp.h test_cpp
+ -del on_sparc_sunos5
+ $(AR) ru gc.a gc_cpp.o
+ $(RANLIB) gc.a
+ ./test_cpp 1
+
+mach_dep.o: $(srcdir)/mach_dep.c
+# $(srcdir)/mips_mach_dep.s $(srcdir)/rs6000_mach_dep.s if_mach if_not_there
+ -del mach_dep.o
+ $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c
+
+mark_rts.o: $(srcdir)/mark_rts.c
+ -del mark_rts.o
+ $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c
+
+cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES)
+ $(CC) $(CFLAGS) -c $(srcdir)/cord/cordbscs.c
+ move cordbscs.o cord/cordbscs.o
+# not all compilers understand -o filename
+
+cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES)
+ $(CC) $(CFLAGS) -c $(srcdir)/cord/cordxtra.c
+ move cordxtra.o cord/cordxtra.o
+
+cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES)
+ $(CC) $(CFLAGS) -c $(srcdir)/cord/cordprnt.c
+ move cordprnt.o cord/cordprnt.o
+
+cord/cordtest: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a
+ -del cord/cordtest
+ $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c\
+ $(CORD_OBJS) gc.a
+
+cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a
+ -del cord/de
+ ./if_mach SPARC SUNOS5 $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c\
+cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lthread -ldl
+ ./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 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_not_there cord/de $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c\
+cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES)
+
+clean:
+ -del gc.a *.o gctest gctest_dyn_link test_cpp \
+ setjmp_test mon.out gmon.out a.out core if_not_there if_mach \
+ $(CORD_OBJS) cord/cordtest cord/de
+ -del *~
+
+gctest: test.o gc.a
+ -del gctest
+ $(CC) $(CFLAGS) -o gctest test.o gc.a
+
+# If an optimized setjmp_test generates a segmentation fault,
+# odds are your compiler is broken. Gctest may still work.
+# Try compiling setjmp_t.c unoptimized.
+setjmp_test: $(srcdir)/setjmp_t.c $(srcdir)/gc.h
+ $(CC) $(CFLAGS) -o setjmp_test $(srcdir)/setjmp_t.c
+
+test: KandRtest cord/cordtest
+ cord/cordtest
+
+# Those tests that work even with a K&R C compiler:
+KandRtest: setjmp_test gctest
+ ./setjmp_test
+ ./gctest
+
+
+
+++ /dev/null
-# If your make barfs, try gnumake instead.
-
-# Supported targets:
-# <default> - builds gc.a and gctest
-# gc.a - builds basic library
-# gctest - tests basic library
-# c++ - adds C++ interface to library
-
-# Default threads package -- uncomment ONE of the following
-
-THR=MIT
-#THR=DEC
-
-ifeq ($(THR),MIT)
-
-# Definitions for MIT-pthreads
-CC=pgcc
-IFCC=cc
-CXX=pg++
-CFLAGS=-g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \
- -DMIT_PTHREADS -DGATHERSTATS
-endif
-
-ifeq ($(THR),DEC)
-
-# Definitions for DECthreads
-CC=cc
-IFCC=cc
-CXX=gcc
-CFLAGS= -g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \
- -DDEC_PTHREADS -DGATHERSTATS
-THRLIB=-lpthreads -lmach -lc_r
-endif
-
-ifeq ($(THR),SOLARIS)
-
-# Definitions for Solaris threads
-CC=gcc
-IFCC=gcc
-CXX=gcc
-CFLAGS= -g -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT \
- -DSOLARIS_THREADS -DGATHERSTATS
-endif
-
-all: gc.a gctest
-
-gc.a: pre-built
- $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS)" $@
-
-test.o:
- $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS)" $@
-
-gctest: pre-built gc.a test.o
- $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS) $(THRLIB)" $@
-
-c++: gc.a test.o
- $(MAKE) -f Makefile CC=$(CC) CXX=$(CXX) CFLAGS="$(CFLAGS) $(THRLIB)" $@
-
-# May need to avoid pgcc for if_not_there and if_mach...
-
-pre-built: if_not_there if_mach
-
-if_not_there: if_not_there.c
- $(MAKE) -f Makefile CC=$(IFCC) if_not_there
-
-if_mach: if_mach.c config.h
- $(MAKE) -f Makefile CC=$(IFCC) if_mach
CPU= i386
!include <ntwin32.mak>
-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
+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
all: gctest.exe cord\de.exe test_cpp.exe
--- /dev/null
+gc.mak
\ No newline at end of file
# Significantly revised for GC version 4.4 by Mark Boulter (Jan 1994).
-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 typd_mlc.obj ptr_chck.obj
+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 typd_mlc.obj ptr_chck.obj mallocx.obj
CORDOBJS= cord\cordbscs.obj cord\cordxtra.obj cord\cordprnt.obj
$(CC) $(CFLAGS) /C /Focord\cordprnt cord\cordprnt.c
cord\cordtest.exe: cord\cordtest.c cord\cord.h cord\private\cord_pos.h cord\ec.h $(CORDOBJS) gc.lib
- $(CC) $(CFLAGS) /B"/STACK:65536" /Fecord\cordtest cord\cordtest.c gc.lib $(CORDOBJS)
\ No newline at end of file
+ $(CC) $(CFLAGS) /B"/STACK:65536" /Fecord\cordtest cord\cordtest.c gc.lib $(CORDOBJS)
# Fix to point to local pcr installation directory.
PCRDIR= ..
-COBJ= alloc.o reclaim.o allchblk.o misc.o os_dep.o mark_rts.o headers.o mark.o obj_map.o pcr_interface.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o typd_mlc.o ptr_chck.o
+COBJ= alloc.o reclaim.o allchblk.o misc.o os_dep.o mark_rts.o headers.o mark.o obj_map.o pcr_interface.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o typd_mlc.o ptr_chck.o mallocx.o
-CSRC= 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 typd_mlc.c ptr_chck.c
+CSRC= 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 typd_mlc.c ptr_chck.c mallocx.c
SHELL= /bin/sh
Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
-Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
+Copyright (c) 1991-1996 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.
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.10 of a conservative garbage collector for C and C++.
+This is version 4.11 of a conservative garbage collector for C and C++.
HISTORY -
(Blame for misinstallation of these modifications goes to the first author,
however.)
+Credits for some more recent modifications are given in the modification
+history at the end of this file.
+
This is intended to be a general purpose, garbage collecting storage
allocator. The algorithms used are described in:
Boehm, H., and D. Chase, "A Proposal for GC-safe C Compilation",
The Journal of C Language Translation 4, 2 (December 1992).
-(Also available from parcftp.xerox.com:pub/gc, among other places.)
+
+and
+
+Boehm H., "Simple GC-safe Compilation", Proceedings
+of the ACM SIGPLAN '96 Conference on Programming Language Design and
+Implementation.
+
+(Both are also available from
+http://reality.sgi.com/employees/boehm_mti/papers/, among other places.)
Unlike the collector described in the second reference, this collector
operates either with the mutator stopped during the entire collection
from other such accessible objects, or from the registers,
stack, data, or statically allocated bss segments. Pointers from
the stack or registers may point to anywhere inside an object.
-However, it is usually assumed that all pointers originating in the
-heap point to the beginning of an object. (This does
-not disallow interior pointers; it simply requires that there must be a
-pointer to the beginning of every accessible object, in addition to any
-interior pointers.) There are two facilities for altering this behavior.
-The macro ALL_INTERIOR_POINTERS may be defined in gc_private.h to
-cause any pointer into an object (or one past the end) to retain the
-object. A routine GC_register_displacement is provided to allow for
-more controlled interior pointer use in the heap. Defining
-ALL_INTERIOR_POINTERS is somewhat dangerous, in that it can result
-in unnecessary memory retention. However this is much less of a
-problem than with older collector versions. The routine
-GC_register_displacement is described in gc.h.
+The same is true for heap pointers if the collector is compiled with
+ ALL_INTERIOR_POINTERS defined, as is now the default.
+
+Compiling without ALL_INTERIOR_POINTERS may reduce accidental retention
+of garbage objects, by requiring pointers from the heap to to the beginning
+of an object. But this no longer appears to be a significant
+issue for most programs.
+
+There are a number of routines which modify the pointer recognition
+algorithm. GC_register_displacement allows certain interior pointers
+to be recognized even if ALL_INTERIOR_POINTERS is nor defined.
+GC_malloc_ignore_off_page allows some pointers into the middle of large objects
+to be disregarded, greatly reducing the probablility of accidental
+retention of large objects. For most purposes it seems best to compile
+with ALL_INTERIOR_POINTERS and to use GC_malloc_ignore_off_page if
+you get collector warnings from allocations of very large objects.
+See README.debugging for details.
Note that pointers inside memory allocated by the standard "malloc" are not
seen by the garbage collector. Thus objects pointed to only from such a
GC_malloc_uncollectable has semantics similar to standard malloc,
but allocates objects that are traced by the collector.)
- The collector does not generally know how to find pointers in data
+ 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 and IRIX 5.X is
-included (see dynamic_load.c).
+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.
Note that the garbage collector does not need to be informed of shared
read-only data. However if the shared library mechanism can introduce
from a signal handler while another malloc is in progress, provided
the original malloc is not restarted. (Empirically, many UNIX
applications already assume this.) To obtain this level of signal
-safety, remove the definition of -DNO_SIGNALS in Makefile.
+safety, remove the definition of -DNO_SIGNALS in Makefile. This incurs
+a minor performance penalty, and hence is no longer the default.
The allocator/collector can also be configured for thread-safe operation.
(Full signal safety can also be achieved, but only at the cost of two system
It is suggested that if you need to replace a piece of the collector
(e.g. GC_mark_rts.c) you simply list your version ahead of gc.a on the
+ work.)
ld command line, rather than replacing the one in gc.a. (This will
generate numerous warnings under some versions of AIX, but it still
works.)
"cord.h" and "ec.h".)
The collector currently is designed to run essentially unmodified on
-the following machines (most of the operating systems mentioned are
+at least the following machines (most or all of the system names mentioned are
trademarks of their respective holders):
+ SGI workstations under IRIX 4, 5, & 6
+ Intel 386 or 486 under most operating systems, but not MSDOS.
+ (Win32S is somewhat supported, so it is marginally possible to
+ build applications for Windows 3.1. DOS + djgpp should also
+ work.)
Sun 3
Sun 4 under SunOS 4.X or Solaris2.X (with or without threads)
Vax under 4.3BSD, Ultrix
- Intel 386 or 486 under most operating systems, but not MSDOS.
- (Win32S is somewhat supported, so it is possible to
- build applications for Windows 3.1. There exists a port
- to DOS + 32 bit extender for at least one 32 bit extender.
- However, I don't have source for this.)
- Sequent Symmetry (single threaded)
- Encore Multimax (single threaded)
- MIPS M/120 (and presumably M/2000) (RISC/os 4.0 with BSD libraries)
IBM PC/RT (Berkeley UNIX)
IBM RS/6000
HP9000/300
HP9000/700
DECstations under Ultrix
DEC Alpha running OSF/1
- SGI workstations under IRIX 4 & 5
Sony News
Apple Macintosh under A/UX or MacOS
- Commodore Amiga (see README.amiga)
+ Commodore Amiga
NeXT machines
+ Some more obscure and/or obsolete systems ...
In a few cases (Amiga, OS/2, Win32, MacOS) a separate makefile
-or equivalent is supplied.
+or equivalent is supplied. Many of these have separate README.system
+files.
Dynamic libraries are completely supported only under SunOS
(and even that support is not functional on the last Sun 3 release),
-IRIX 5, Win32 (not Win32S) and OSF/1 on DEC AXP machines.
+IRIX 5&6, HP-PA, Win32 (not Win32S) and OSF/1 on DEC AXP machines.
On other machines we recommend that you do one of the following:
1) Add dynamic library support (and send us the code).
you may have to adjust the alignment parameters defined in gc_priv.h.
A port to a machine that is not byte addressed, or does not use 32 bit
-or 64 bit addresses will require a major effort. A port to MSDOS is hard,
-unless you are willing to assume an 80386 or better, and that only flat
-32 bit pointers will ever need to be seen by the collector.
+or 64 bit addresses will require a major effort. A port to plain MSDOS
+or win16 is hard.
For machines not already mentioned, or for nonstandard compilers, the
following are likely to require change:
portability constraints). To do so define FIND_LEAK in Makefile
This will cause the collector to invoke the report_leak
routine defined near the top of reclaim.c whenever an inaccessible
-object is found that has not been explicitly freed.
+object is found that has not been explicitly freed. The collector will
+no longer reclaim inaccessible memory; in this form it is purely a
+debugging tool.
Productive use of this facility normally involves redefining report_leak
to do something more intelligent. This typically requires annotating
objects with additional information (e.g. creation time stack trace) that
doesn't cut it.
Some C optimizers may lose the last undisguised pointer to a memory
object as a consequence of clever optimizations. This has almost
-never been observed in practice. Send mail to boehm@parc.xerox.com
+never been observed in practice. Send mail to boehm@mti.sgi.com
for suggestions on how to fix your compiler.
This is not a real-time collector. In the standard configuration,
percentage of time required for collection should be constant across
per MB of accessible memory that needs to be scanned. Your mileage
may vary.) The incremental/generational collection facility helps,
but is portable only if "stubborn" allocation is used.
- Please address bug reports to boehm@parc.xerox.com. If you are
+ Please address bug reports to boehm@mti.sgi.com. If you are
contemplating a major addition, you might also send mail to ask whether
it's already been done (or whether we tried and discarded it).
- Fixed a typo around a call to GC_collect_or_expand in alloc.c. It broke
handling of out of memory. (Thanks to Patrick Beard for noticing.)
+Since 4.10:
+ - Rationalized (hopefully) GC_try_to_collect in an incremental collection
+ environment. It appeared to not handle a call while a collection was in
+ progress, and was otherwise too conservative.
+ - Merged GC_reclaim_or_delete_all into GC_reclaim_all to get rid of some
+ code.
+ - Added Patrick Beard's Mac fixes, with substantial completely untested
+ modifications.
+ - Fixed the MPROTECT_VDB code to deal with large pages and imprecise
+ fault addresses (as on an UltraSPARC running Solaris 2.5). Note that this
+ was not a problem in the default configuration, which uses PROC_VDB.
+ - The DEC Alpha assembly code needed to restore $gp between calls.
+ Thanks to Fergus Henderson for tracking this down and supplying a
+ patch.
+ - The write command for "de" was completely broken for large files.
+ I used the easiest portable fix, which involved changing the semantics
+ so that f.new is written instead of overwriting f. That's safer anyway.
+ - Added README.solaris2 with a discussion of the possible problems of
+ mixing the collector's sbrk allocation with malloc/realloc.
+ - Changed the data segment starting address for SGI machines. The
+ old code failed under IRIX6.
+ - Required double word alignment for MIPS.
+ - Various minor fixes to remove warnings.
+ - Attempted to fix some Solaris threads problems reported by Zhiying Chen.
+ In particular, the collector could try to fork a thread with the
+ world stopped as part of GC_thr_init. It also failed to deal with
+ the case in which the original thread terminated before the whole
+ process did.
+ - Added -DNO_EXECUTE_PERMISSION. This has a major performance impact
+ on the incremental collector under Irix, and perhaps under other
+ operating systems.
+ - Added some code to support allocating the heap with mmap. This may
+ be preferable under some circumstances.
+ - Integrated dynamic library support for HP.
+ (Thanks to Knut Tvedten <knuttv@ifi.uio.no>.)
+ - Integrated James Clark's win32 threads support, and made a number
+ of changes to it, many of which were suggested by Pontus Rydin.
+ This is still not 100% solid.
+ - Integrated Alistair Crooks' support for UTS4 running on an Amdahl
+ 370-class machine.
+ - Fixed a serious bug in explicitly typed allocation. Objects requiring
+ large descriptors where handled in a way that usually resulted in
+ a segmentation fault in the marker. (Thanks to Jeremy Fitzhardinge
+ for helping to track this down.)
+ - Added partial support for GNU win32 development. (Thanks to Fergus
+ Henderson.)
+ - Added optional support for Java-style finalization semantics. (Thanks
+ to Patrick Bridges.) This is recommended only for Java implementations.
+ - GC_malloc_uncollectable faulted instead of returning 0 when out of
+ memory. (Thanks to dan@math.uiuc.edu for noticing.)
+ - Calls to GC_base before the collector was initialized failed on a
+ DEC Alpha. (Thanks to Matthew Flatt.)
+ - Added base pointer checking to GC_REGISTER_FINALIZER in debugging
+ mode, at the suggestion of Jeremy Fitzhardinge.
+ - GC_debug_realloc failed for uncollectable objects. (Thanks to
+ Jeremy Fitzhardinge.)
+ - Explicitly typed allocation could crash if it ran out of memory.
+ (Thanks to Jeremy Fitzhardinge.)
+ - Added minimal support for a DEC Alpha running Linux.
+ - Fixed a problem with allocation of objects whose size overflowed
+ ptrdiff_t. (This now fails unconditionally, as it should.)
+ - Added the beginning of Irix pthread support.
+ - Integrated Xiaokun Zhu's fixes for djgpp 2.01.
+ - Added SGI-style STL allocator support (gc_alloc.h).
+ - Fixed a serious bug in README.solaris2. Multithreaded programs must include
+ gc.h with SOLARIS_THREADS defined.
+ - Changed GC_free so it actually deallocates uncollectable objects.
+ (Thanks to Peter Chubb for pointing out the problem.)
+ - Added Linux ELF support for dynamic libararies. (Thanks again to
+ Patrick Bridges.)
+ - Changed the Borland cc configuration so that the assembler is not
+ required.
+ - Fixed a bug in the C++ test that casued it to fail in 64-bit
+ environments.
--- /dev/null
+[Partially supplied by Xiaokun Zhu <xiaokun@aero.gla.ac.uk>]
+
+There is no caddr_t in djgpp 2.01 so the following declaration was added to
+os_dep.c. It may need to be removed for other versions.
+
+ typedef caddr_t long unsigned int;
+
+
+Use Makefile.dj to compile it :
+
+ C> make -f Makefile.dj test
+
+
+!!! setjmp_t.exe works fine.
+!!! gctest.exe works fine.
+!!! cordtest.exe fail.
+!!! test_cpp.exe works fine.
+
+In some cases it may be necessary to explicitly set GC_stackbottom.
--- /dev/null
+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
+accessible without considering statically allocated variables in dynamic
+libraries.
+++ /dev/null
-See the README file for main Copyright notices.
-
-DECthreads/MIT-pthreads extensions:
-Copyright (c) 1995, 1996 by Ian Piumarta and INRIA, with permission
-for use under exactly the same terms as the original GC; i.e...
-
-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.
-
-
-MODIFICATIONS
-
-The pthreads extensions required the following GC source files to be
-modified:
-
- dyn_load.c
- gc.h
- gc_priv.h
- misc.c
- os_dep.c
- test.c
-
-The modified sections are clearly visible, being those affected by the
-existence of the preprocessor symbols MIT_PTHREADS and DEC_PTHREADS.
-No parts of the original sources outside these sections were modified.
-The following files are new:
-
- README.pthreads
- dec_threads.c
- mit_threads.c
-
-The Makefile was also modified very slightly, to include the two new
-source files in the OBJS and CSRCS lists.
-
-
-GENERAL DESCRIPTION
-
-The pthreads extensions allow the GC to be used with DECpthreads
-(under Digital Unix, formerly DEC OSF/1) or the user-level
-MIT-pthreads implementation by Chris Provenzano (running on a large
-variety of platforms). DECthreads is bundled with DEC's Unix
-machines; MIT-pthreads is available from:
-
- ftp://sipb.mit.edu:/pub/pthreads
- or ftp://toxicwaste.mit.edu:/pub/archive/pthreads
-
-
-INSTALLATION AND PORTABILITY
-
-These extensions have been tested (and appear to work) on the
-following platforms:
-
- DECthreads:
- DEC Alpha OSF/1 3.2C/3.2D
-
- MIT-pthreads (tested with pthreads versions 1.60 beta4 and beta5.9):
- DEC Alpha OSF/1 3.2C/3.2D
- Sparc SunOS 4.1.3/4.1.3_U1/4.1.4
- i586 Red Hat Linux 3.0.3, Kernel 1.99.4
- HP 9000/725 HP-UX A.09.05
-
-Very minor additions may be needed to be make the extensions work with
-the other architectures supported by both the GC and MIT-pthreads.
-
-Note that some versions of gcc-2.7 are broken on DEC Alpha. Version
-2.7.0 seems to be reliable, and versions 2.7.3 and later might be.
-
- Unless you're already familiar with this GC:
-
- Read the installtation notes in the README file!
- Read the Makefile!
-
- To install for DECpthreads:
-
- Edit "Makefile.pthreads". Uncomment the definition "THR=DEC".
- Edit (if necessary) the definitions immediately following the
- comment "Definitions for DECthreads" to use the correct
- compiler and/or CFLAGS for your environment. See Makefile for
- an explanation of the GC-related flags.
-
- If you are using gcc rather than the DEC C compiler, edit the
- Makefile and remove the line indicated in the target
- "mark_rts.o".
-
- Type "make -f Makefile.pthreads" to build the basic library
- and gctest program, with pthreads support.
-
- Run gctest to check that things are working.
-
- If you require C++ support, type "make -f Makefile.pthreads
- c++ THR=DEC". Make sure the test_cpp program does not fail.
-
- To install for MIT-pthreads:
-
- Install (if necessary) the MIT-pthreads package (see above).
- Versions 1.60beta4 and earlier have a broken "pg++" script
- (later versions may be corrected): if necessary change the
- definition of "libs" from
- libs='-lpthread -lstdc++ -lm -lgcc'
- to
- libs='-lpthread -lstdc++ -lm -lgcc -lpthread'
-
- Edit "Makefile.pthreads". Uncomment the definition "THR=MIT".
- Edit (if necessary) the definitions immediately following the
- comment "Definitions for MIT-pthreads" to use the correct
- CFLAGS for your environment (the supplied compiler definitions
- should be correct in most situations). See Makefile for an
- explanation of the GC-related flags.
-
- If you are compiling on a DEC Alpha using gcc rather than the
- DEC C compiler, edit the Makefile and remove the line
- indicated in the target "mark_rts.o".
-
- Type "make -f Makefile.pthreads" to build the basic library
- and gctest program, with pthreads support.
-
- Run gctest to check that things are working.
-
- If you require C++ support, type "make -f Makefile.pthreads
- c++ THR=MIT". Make sure the test_cpp program does not fail.
-
-
-MODIFICATIONS TO THE PTHREADS INTERFACES
-
-The extensions rely on intercepting calls to a few pthreads routines.
-Any source file using the pthreads routines MUST include "gc.h", even
-if there is no GC-related code in the file.
-
-The following DECthreads routines are affected:
-
- int pthread_create(pthread_t *thread, pthread_attr_t attr,
- pthread_startroutine_t start_routine,
- pthread_addr_t arg);
-
- int pthread_detach(pthread_t *thread);
-
-The following MIT-pthreads routines are affected:
-
- int pthread_create(pthread_t *thread, pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg);
-
-
-BUGS
-
-Incremental mode does not work with DECpthreads. The reasons are not
-yet fully explored. GC_enable_incremental() is therefore a no-op when
-the GC is compiled with DECpthreads support.
-
-The DECthreads extensions assume a one-to-one correspondance between
-each pthread and a kernel (mach) thread. This appears to be true with
-round-robin scheduling, but may not be true for other scheduling
-policies. (This concerns stopping the world: there is no documented
-mechanism to stop the pthreads world, so we have to stop the
-underlying kernel threads instead [which is documented]. When asked
-about setting the pthreads kernel lock, all that the DEC techincal
-people would tell me was: "don't meddle with things that you don't
-understand".)
-
-The extensions have been tested with both versions of pthreads,
-although in relatively undemanding situations. Some subtle problems
-may still exist in programs which use thread synchronisation
-operations (pthread_join/pthread_detach) in bizarre ways, or which use
-scheduling policies other than the default round-robin. The more
-exotic pthread routines (e.g. executing a call on another thread's
-stack) have not been tested very much at all.
-
-The MITpthreads extensions rely on snooping about in small parts of
-the pthreads' private internal structures. This seems unavoidable.
-
-The extensions appear to work with unmodified versions of the GNU
-libgcc and libstdc++ (at least as far as iostreams are concerned), but
-large parts of these libraries remain untested in a pthreads/GC
-context.
-
-
-BUG REPORTS AND SUGGESTIONS FOR IMPROVEMENTS
-
-Should be sent to: Ian.Piumarta@inria.fr
-
-It would be most helpful if you could include a small program which
-reproduces the bug.
-
-
-ACKNOWLEDGEMENTS
-
-I am immensely grateful to David Halls (David.Halls@cl.cam.ac.uk) who
-spent many months "alpha testing" the extensions, suffering problems
-with the early implementations, and contributing suggestions for
-improvements. He also kindly tested the MIT-pthreads extensions under
-Linux and provided the code specific to dynamic loading and the HP-PA
-architecture.
-
-
-------------------------------- projet SOR -------------------------------
-Ian Piumarta, INRIA Rocquencourt, Internet: Ian.Piumarta@inria.fr
-BP105, 78153 Le Chesnay Cedex, FRANCE Voice: +33 1 39 63 52 87
------------------------ Systemes a Objets Repartis -----------------------
--- /dev/null
+Performance of the incremental collector can be greatly enhanced with
+-DNO_EXECUTE_PERMISSION.
+
+The collector should run with all of the -32, -n32 and -64 ABIs. Remember to
+define the AS macro in the Makefile to be "as -64", or "as -n32".
+
+If you use -DREDIRECT_MALLOC=GC_malloc with C++ code, your code should make
+at least one explicit call to malloc instead of new to ensure that the proper
+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:
+
+1) You compile the collector with -DIRIX_THREADS specified in the Makefile.
+
+2) You have the latest pthreads patches installed. As of 11/7/96, some of
+the required patches were not yet generally available, so you may have
+to wait. If gctest fails, this is the likely cause.
+(Though the collector makes only documented pthread calls,
+it relies on signal/threads interactions working just right in ways
+that are not required by the standard. It is unlikely that this code
+will run on other pthreads platforms. But please tell me if it does.)
+
+3) Every file that makes thread calls should define IRIX_THREADS 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) For the time being, you should not use dlopen.
--- /dev/null
+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
+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.
+
+The collector normally obtains memory through sbrk. There is some reason
+to expect that this is not safe if the client program also calls the system
+malloc, or especially realloc. The sbrk man page strongly suggests this is
+not safe: "Many library routines use malloc() internally, so use brk()
+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.
+Furthermore there typically isn't much of a performance advantage to be
+gained from such a restriction.
+
+To complicate matters, empirically this approach seems to work, at least
+most of the time. There have been reports of crashes in Sun's realloc
+function. But I haven't been able to reproduce this on an example small
+enough to preclude other bugs.
+
+As it stands, you have 4 choices:
+
+1. Use the collector as is, possibly avoiding the system realloc function.
+If you get a simple program that does this to crash, send me a copy, and
+I'll change the default to do something else. Please also complain to Sun,
+if you find such a case.
+
+2. Build the collector with -DREDIRECT_MALLOC=GC_malloc_uncollectable, thus
+redirecting system allocation calls through the collector. I believe
+this is OK under Solaris, but there is some risk of infinite recursion if
+you turn on collector log messages.
+
+3. Change the GET_MEM definition for SPARC and SUNOS5 in gc_priv.h to
+be the same as for the AMIGA or NEXT. The collector will then obtain
+memory through the system calloc function. This is probably the safest,
+but it incurs a fragmentation penalty. Since the system malloc is likely
+to reserve header space in each object, the different heap sections
+are virtually guaranteed to not be adjacent. Thus large objects have to fit
+entirely in one section.
+
+4. Change GET_MEM to allocate memory from a different part of the address
+space, e.g. with mmap. Based on previous experiences with older versions
+of SunOS and other operating systems, this risks other performance problems.
+I have not tried it with Solaris 2.
+
+SOLARIS THREADS:
+
+The collector must be compiled with -DSOLARIS_THREADS to be thread safe.
+It is also essential that gc.h be included in files that call thr_create,
+thr_join, thr_suspend, thr_continue, or dlopen. Gc.h macro defines
+these to also do GC bookkeeping, etc. Gc.h must be included with SOLARIS_THREADS
+defined, otherwise these replacements are not visible.
+
+In this mode, the collector contains various workarounds for older Solaris
+bugs. Mostly, these should not be noticeable unless you look at system
+call traces. However, it cannot protect a guard page at the end of
+a thread stack. If you know that you will only be running Solaris2.5
+or later, it should be possible to fix this by compiling the collector
+with -DSOLARIS23_MPROTECT_BUG_FIXED.
+
+Jeremy Fitzhardinge points out that there is a problem with the dlopen
+replacement, in that startup code in the library is run while the allocation
+lock is held. This appears to be difficult to fix, since the collector does
+look at data structures maintained by dlopen, and hence some locking is needed
+around the dlopen call.
+
+Hans-J. Boehm
+(The above contains my personal opinions, which are probably not shared
+by anyone else.)
--- /dev/null
+Alistair Crooks supplied the port. He used Lexa C version 2.1.3 with
+-Xa to compile.
-The collector currently does not handle multiple threads. There
-is good reason to believe this is fixable. (SRC M3 works with
-NT threads.)
-
The collector has only been compiled under Windows NT, with the
original Microsoft SDK, with Visual C++ 2.0, and with Borland 4.5.
For Microsoft development tools, rename NT_MAKEFILE as
MAKEFILE. (Make sure that the CPU environment variable is defined
-to be i386.) For Borland tools, use BCC_MAKEFILE. Note that
+to be i386.)
+
+For Borland tools, use BCC_MAKEFILE. Note that
Borland's compiler defaults to 1 byte alignment in structures (-a1),
whereas Visual C++ appears to default to 8 byte alignment (/Zp8).
The garbage collector in its default configuration EXPECTS AT
486 or Pentium.) Note that this changes structure layouts. (As a last
resort, config.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
+require the assembler. If you do have the assembler, I recommend
+removing the -DUSE_GENERIC.
Incremental collection support was recently added. This is
currently pretty simpleminded. Pages are protected. Protection
Incremental collection is not supported under win32s, and it may not
be possible to do so. However, win32 applications that attempt to use
incremental collection should continue to run, since the
-colllector detects if it's running under win32s and turns calls to
+collector detects if it's running under win32s and turns calls to
GC_enable_incremental() into noops.
+James Clark has contributed the necessary code to support win32 threads.
+This code is known to exhibit some problems with incremental collection
+enabled. Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE
+to build this version. Note that this requires some files whose names
+are more than 8 + 3 characters long. Thus you should unpack the tar file
+so that long file names are preserved. To build the garbage collector
+test with VC++ from the command line, use
+
+nmake /F ".\gc.mak" CFG="gctest - Win32 Release"
+
+This requires that the subdirectory gctest\Release exist.
+
+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.
+
+Hans
+
+
dyn_load.o : dyn_load.c $(INC)
dbg_mlc.o : dbg_mlc.c $(INC)
malloc.o : malloc.c $(INC)
+mallocx.o : malloc.c $(INC)
stubborn.o : stubborn.c $(INC)
checksums.o : checksums.c $(INC)
typd_mlc.o: typd_mlc.c $(INC)
continue;
}
}
- if ( kind != UNCOLLECTABLE &&
+ if ( !IS_UNCOLLECTABLE(kind) &&
(kind != PTRFREE || size_needed > MAX_BLACK_LIST_ALLOC)) {
struct hblk * lasthbp = hbp;
ptr_t search_end = (ptr_t)hbp + size_avail - size_needed;
* modified is included with the above copyright notice.
*
*/
-/* Boehm, February 7, 1996 4:37 pm PST */
+/* Boehm, February 16, 1996 2:26 pm PST */
# include "gc_priv.h"
word GC_free_space_divisor = 4;
+extern bool GC_collection_in_progress();
+
int GC_never_stop_func GC_PROTO((void)) { return(0); }
CLOCK_TYPE GC_start_time;
/* collections to amortize the collection cost. */
static word min_words_allocd()
{
- int dummy;
# ifdef THREADS
/* We punt, for now. */
register signed_word stack_size = 10000;
# else
+ int dummy;
register signed_word stack_size = (ptr_t)(&dummy) - GC_stackbottom;
# endif
register word total_root_size; /* includes double stack size, */
if (!GC_incremental) {
GC_gcollect_inner();
n_partial_gcs = 0;
+ return;
} else if (n_partial_gcs >= GC_full_freq) {
- GC_initiate_full();
+# ifdef PRINTSTATS
+ GC_printf2(
+ "***>Full mark for collection %lu after %ld allocd bytes\n",
+ (unsigned long) GC_gc_no+1,
+ (long)WORDS_TO_BYTES(GC_words_allocd));
+# endif
+ GC_promote_black_lists();
+ (void)GC_reclaim_all((GC_stop_func)0, TRUE);
+ GC_clear_marks();
n_partial_gcs = 0;
} else {
- /* We try to mark with the world stopped. */
- /* If we run out of time, this turns into */
- /* incremental marking. */
- GET_TIME(GC_start_time);
- if (GC_stopped_mark(GC_timeout_stop_func)) {
-# ifdef SAVE_CALL_CHAIN
- GC_save_callers(GC_last_stack);
-# endif
- GC_finish_collection();
- }
n_partial_gcs++;
}
+ /* We try to mark with the world stopped. */
+ /* If we run out of time, this turns into */
+ /* incremental marking. */
+ GET_TIME(GC_start_time);
+ if (GC_stopped_mark(GC_timeout_stop_func)) {
+# ifdef SAVE_CALL_CHAIN
+ GC_save_callers(GC_last_stack);
+# endif
+ GC_finish_collection();
+ }
}
}
bool GC_try_to_collect_inner(stop_func)
GC_stop_func stop_func;
{
+ if (GC_collection_in_progress()) {
+# ifdef PRINTSTATS
+ GC_printf0(
+ "GC_try_to_collect_inner: finishing collection in progress\n");
+# endif /* PRINTSTATS */
+ /* Just finish collection already in progress. */
+ while(GC_collection_in_progress()) {
+ if (stop_func()) return(FALSE);
+ GC_collect_a_little_inner(1);
+ }
+ }
# ifdef PRINTSTATS
GC_printf2(
"Initiating full world-stop collection %lu after %ld allocd bytes\n",
/* Make sure all blocks have been reclaimed, so sweep routines */
/* don't see cleared mark bits. */
/* If we're guaranteed to finish, then this is unnecessary. */
- if (stop_func != GC_never_stop_func && !GC_reclaim_all(stop_func)) {
+ if (stop_func != GC_never_stop_func
+ && !GC_reclaim_all(stop_func, FALSE)) {
/* Aborted. So far everything is still consistent. */
return(FALSE);
}
GC_save_callers(GC_last_stack);
# endif
if (!GC_stopped_mark(stop_func)) {
+ if (!GC_incremental) {
/* We're partially done and have no way to complete or use */
/* current work. Reestablish invariants as cheaply as */
/* possible. */
GC_invalidate_mark_state();
GC_unpromote_black_lists();
- if (GC_incremental) {
- /* Unlikely. But just invalidating mark state could be */
- /* expensive. */
- GC_clear_marks();
- }
- return(FALSE);
+ } /* else we claim the world is already still consistent. We'll */
+ /* finish incrementally. */
+ return(FALSE);
}
GC_finish_collection();
return(TRUE);
int GC_deficit = 0; /* The number of extra calls to GC_mark_some */
/* that we have made. */
/* Negative values are equivalent to 0. */
-extern bool GC_collection_in_progress();
void GC_collect_a_little_inner(n)
int n;
/* Minimize junk left in my registers and on the stack */
GC_clear_a_few_frames();
GC_noop(0,0,0,0,0,0);
- GC_initiate_partial();
+ GC_initiate_gc();
for(i = 0;;i++) {
if ((*stop_func)()) {
# ifdef PRINTSTATS
GC_invoke_finalizers();
DISABLE_SIGNALS();
LOCK();
+ ENTER_GC();
if (!GC_is_initialized) GC_init_inner();
/* Minimize junk left in my registers */
GC_noop(0,0,0,0,0,0);
result = (int)GC_try_to_collect_inner(stop_func);
+ EXIT_GC();
UNLOCK();
ENABLE_SIGNALS();
if(result) GC_invoke_finalizers();
GC_max_heapsize = n;
}
+GC_word GC_max_retries = 0;
+
/*
* this explicitly increases the size of the heap. It is used
* internally, but may also be invoked from GC_expand_hp by the user.
word needed_blocks;
bool ignore_off_page;
{
- static int count = 0; /* How many failures? */
+ static unsigned count = 0; /* How many failures? */
if (!GC_incremental && !GC_dont_gc && GC_should_collect()) {
GC_gcollect_inner();
}
if (!GC_expand_hp_inner(blocks_to_get)
&& !GC_expand_hp_inner(needed_blocks)) {
- if (count++ < 10) {
+ if (count++ < GC_max_retries) {
WARN("Out of Memory! Trying to continue ...\n", 0);
GC_gcollect_inner();
} else {
if (sz == 0) return(0);
while (*flh == 0) {
+ ENTER_GC();
/* Do our share of marking work */
if(GC_incremental && !GC_dont_gc) GC_collect_a_little_inner(1);
/* Sweep blocks for objects of this size */
GC_continue_reclaim(sz, kind);
+ EXIT_GC();
if (*flh == 0) {
GC_new_hblk(sz, kind);
}
if (*flh == 0) {
- if (!GC_collect_or_expand((word)1,FALSE)) return(0);
+ ENTER_GC();
+ if (!GC_collect_or_expand((word)1,FALSE)) {
+ EXIT_GC();
+ return(0);
+ }
+ EXIT_GC();
}
}
/*
* 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, December 7, 1995 10:03 am PST */
#ifndef CONFIG_H
# 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
# define LINUX
# define mach_type_known
# endif
-# if defined(__alpha)
+# if defined(__alpha) || defined(__alpha__)
# define ALPHA
+# if defined(linux) || defined(__linux__)
+# define LINUX
+# else
+# define OSF1 /* a.k.a Digital Unix */
+# endif
# define mach_type_known
# endif
# if defined(_AMIGA)
# define DJGPP /* MSDOS running the DJGPP port of GCC */
# define mach_type_known
# endif
+# if defined(__CYGWIN32__)
+# define I386
+# define CYGWIN32
+# define mach_type_known
+# endif
# if defined(__BORLANDC__)
# define I386
# define MSWIN32
# define mach_type_known
# endif
+# if defined(_UTS) && !defined(mach_type_known)
+# define S370
+# define UTS4
+# endif
/* Feel free to add more clauses here */
/* 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 */
/*
*
* 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.
# ifdef SPARC
# define MACH_TYPE "SPARC"
# define ALIGNMENT 4 /* Required by hardware */
+# define ALIGN_DOUBLE
extern int etext;
# ifdef SUNOS5
# define OS_TYPE "SUNOS5"
# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff))
# define STACKBOTTOM ((ptr_t)0xc0000000)
# define MPROTECT_VDB
+# ifdef __ELF__
+# define DYNAMIC_LOADING
+# endif
+# 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__)
# endif
# ifdef OS2
# define OS_TYPE "OS2"
extern int etext;
# define DATASTART ((ptr_t)(&etext))
# define STACKBOTTOM ((ptr_t)0x00080000)
+ /* This may not be right. It's rumored to vary. */
# endif
# ifdef FREEBSD
# define OS_TYPE "FREEBSD"
# 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)0x40000000
+# 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. */
+# ifndef IRIX_THREADS
+# define MPROTECT_VDB
+# endif
+# 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
# ifdef HP_PA
# define MACH_TYPE "HP_PA"
# define ALIGNMENT 4
+# define ALIGN_DOUBLE
extern int __data_start;
# define DATASTART ((ptr_t)(&__data_start))
# if 0
# ifdef ALPHA
# define MACH_TYPE "ALPHA"
# define ALIGNMENT 8
-# define DATASTART ((ptr_t) 0x140000000)
-# ifndef DEC_PTHREADS
-# define HEURISTIC2
+# ifdef OSF1
+# define OS_TYPE "OSF1"
+# define DATASTART ((ptr_t) 0x140000000)
+# 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)))
+ 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)
+# ifdef __ELF__
+# define DYNAMIC_LOADING
+# endif
+ /* 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. */
# endif
-# define CPP_WORDSZ 64
-# define MPROTECT_VDB
-# define DYNAMIC_LOADING
# endif
# ifdef M88K
# define MACH_TYPE "M88K"
# define ALIGNMENT 4
+# define ALIGN_DOUBLE
+ extern int etext;
# ifdef CX_UX
# define DATASTART ((((word)&etext + 0x3fffff) & ~0x3fffff) + 0x10000)
# 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
# define DATAEND (&end)
# endif
-# if defined(SUNOS5) || defined(DRSNX)
+# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
/* OS has SVR4 generic features. Probably others also qualify. */
# define SVR4
# endif
# define DEFAULT_VDB
# endif
+# if defined(IRIX_THREADS) && !defined(IRIX5)
+--> 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)
+# define THREADS
+# endif
+
# if defined(SPARC)
# define SAVE_CALL_CHAIN
+# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */
+ /* include assembly code to do it well. */
# endif
# endif
/* x, and is thus modifiable. */
char * CORD_to_char_star(CORD x);
+/* Turn a C string into a CORD. The C string is copied, and so may */
+/* subsequently be modified. */
+CORD CORD_from_char_star(const char *s);
+
/* Identical to the above, but the result may share structure with */
/* the argument and is thus not modifiable. */
const char * CORD_to_const_char_star(CORD x);
typedef struct {
CORD c;
- size_t len; /* Actual ength of c */
+ size_t len; /* Actual length of c */
} ForestElement;
static size_t min_len [ MAX_DEPTH ];
int CORD_max_len;
typedef ForestElement Forest [ MAX_DEPTH ];
- /* forest[i].min_length = fib(i+1) */
+ /* forest[i].len >= fib(i+1) */
/* The string is the concatenation */
/* of the forest in order of DECREASING */
/* indices. */
sum = CORD_cat(sum, x);
sum_len += len;
/* If x was a leaf, then sum is now balanced. To see this */
- /* consider the two cases in whichforest[i-1] either is or is */
+ /* consider the two cases in which forest[i-1] either is or is */
/* not empty. */
while (sum_len >= min_len[i]) {
if (forest[i].c != 0) {
void test_basics()
{
- CORD x = "ab";
+ CORD x = CORD_from_char_star("ab");
register int i;
char c;
CORD y;
char * CORD_to_char_star(CORD x)
{
register size_t len = CORD_len(x);
- char * result = (char *)GC_MALLOC_ATOMIC(len + 1);
+ char * result = GC_MALLOC_ATOMIC(len + 1);
if (result == 0) OUT_OF_MEMORY;
CORD_fill_buf(x, 0, len, result);
return(result);
}
+CORD CORD_from_char_star(const char *s)
+{
+ char * result;
+ size_t len = strlen(s);
+
+ if (0 == len) return(CORD_EMPTY);
+ result = GC_MALLOC_ATOMIC(len + 1);
+ if (result == 0) OUT_OF_MEMORY;
+ memcpy(result, s, len+1);
+ return(result);
+}
+
const char * CORD_to_const_char_star(CORD x)
{
if (x == 0) return("");
invalidate_map(line);
break;
case WRITE:
- if ((out = fopen(arg_file_name, "wb")) == NULL
- || CORD_put(current, out) == EOF) {
- de_error("Write failed\n");
- need_redisplay = ALL;
- } else {
- fclose(out);
- }
+ {
+ CORD name = CORD_cat(CORD_from_char_star(arg_file_name),
+ ".new");
+
+ if ((out = fopen(CORD_to_const_char_star(name), "wb")) == NULL
+ || CORD_put(current, out) == EOF) {
+ de_error("Write failed\n");
+ need_redisplay = ALL;
+ } else {
+ fclose(out);
+ }
+ }
break;
default:
{
usage:
fprintf(stderr, "Usage: %s file\n", argv[0]);
fprintf(stderr, "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n");
- fprintf(stderr, "Undo: ^U Write: ^W Quit:^D Repeat count: ^R[n]\n");
+ fprintf(stderr, "Undo: ^U Write to <file>.new: ^W");
+ fprintf(stderr, "Quit:^D Repeat count: ^R[n]\n");
fprintf(stderr, "Top: ^T Locate (search, find): ^L text ^L\n");
exit(1);
}
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
- * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
+ * Copyright 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, October 9, 1995 1:14 pm PDT */
/*
* Note that this defines a large number of tuning hooks, which can
#ifndef _GC_H
# define _GC_H
+# define __GC
+# include <stddef.h>
+
+
+#if defined(_MSC_VER) && defined(_DLL)
+#ifdef GC_BUILD
+#define GC_API __declspec(dllexport)
+#else
+#define GC_API __declspec(dllimport)
+#endif
+#endif
+
+#ifndef GC_API
+#define GC_API extern
+#endif
# if defined(__STDC__) || defined(__cplusplus)
# define GC_PROTO(args) args
extern "C" {
# endif
-# include <stddef.h>
/* Define word and signed_word to be unsigned and signed types of the */
/* size as char * or void *. There seems to be no way to do this */
/* Public read-only variables */
-extern GC_word GC_gc_no;/* Counter incremented per collection. */
+GC_API GC_word GC_gc_no;/* Counter incremented per collection. */
/* Includes empty GCs at startup. */
/* Public R/W variables */
-extern int GC_quiet; /* Disable statistics output. Only matters if */
+GC_API void * (*GC_oom_fn) GC_PROTO((size_t bytes_requested));
+ /* When there is insufficient memory to satisfy */
+ /* an allocation request, we return */
+ /* (*GC_oom_fn)(). By default this just */
+ /* returns 0. */
+ /* If it returns, it must return 0 or a valid */
+ /* pointer to a previously allocated heap */
+ /* object. */
+
+GC_API int GC_quiet; /* Disable statistics output. Only matters if */
/* collector has been compiled with statistics */
/* enabled. This involves a performance cost, */
/* and is thus not the default. */
-extern int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
- /* beacuse it's not safe. */
+GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
+ /* because it's not safe. */
-extern int GC_dont_expand;
+GC_API int GC_dont_expand;
/* Dont expand heap unless explicitly requested */
/* or forced to. */
-extern int GC_full_freq; /* Number of partial collections between */
+GC_API int GC_full_freq; /* Number of partial collections between */
/* full collections. Matters only if */
/* GC_incremental is set. */
-extern GC_word GC_non_gc_bytes;
+GC_API GC_word GC_non_gc_bytes;
/* Bytes not considered candidates for collection. */
/* Used only to control scheduling of collections. */
-extern GC_word GC_free_space_divisor;
+GC_API GC_word GC_free_space_divisor;
/* We try to make sure that we allocate at */
/* least N/GC_free_space_divisor bytes between */
/* collections, where N is the heap size plus */
/* at the expense of space. */
/* GC_free_space_divisor = 1 will effectively */
/* disable collections. */
+
+GC_API GC_word GC_max_retries;
+ /* The maximum number of GCs attempted before */
+ /* reporting out of memory after heap */
+ /* expansion fails. Initially 0. */
/* Public procedures */
* collectable. GC_malloc_uncollectable and GC_free called on the resulting
* object implicitly update GC_non_gc_bytes appropriately.
*/
-extern GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+
+/* The following is only defined if the library has been suitably */
+/* compiled: */
+GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes));
/* Explicitly deallocate an object. Dangerous if used incorrectly. */
/* Requires a pointer to the base of an object. */
/* An object should not be enable for finalization when it is */
/* explicitly deallocated. */
/* GC_free(0) is a no-op, as required by ANSI C for free. */
-extern void GC_free GC_PROTO((GC_PTR object_addr));
+GC_API void GC_free GC_PROTO((GC_PTR object_addr));
/*
* Stubborn objects may be changed only if the collector is explicitly informed.
* do so. The same applies to dropping stubborn objects that are still
* changeable.
*/
-extern void GC_change_stubborn GC_PROTO((GC_PTR));
-extern void GC_end_stubborn_change GC_PROTO((GC_PTR));
+GC_API void GC_change_stubborn GC_PROTO((GC_PTR));
+GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR));
/* Return a pointer to the base (lowest address) of an object given */
/* a pointer to a location within the object. */
/* Return 0 if displaced_pointer doesn't point to within a valid */
/* object. */
-extern GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
+GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
/* Given a pointer to the base of an object, return its size in bytes. */
/* The returned size may be slightly larger than what was originally */
/* requested. */
-extern size_t GC_size GC_PROTO((GC_PTR object_addr));
+GC_API size_t GC_size GC_PROTO((GC_PTR object_addr));
/* For compatibility with C library. This is occasionally faster than */
/* a malloc followed by a bcopy. But if you rely on that, either here */
/* 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. */
-extern GC_PTR GC_realloc GC_PROTO((GC_PTR old_object,
+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. */
-extern int GC_expand_hp GC_PROTO((size_t number_of_bytes));
+GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes));
/* Limit the heap size to n bytes. Useful when you're debugging, */
/* especially on systems that don't handle running out of memory well. */
/* n == 0 ==> unbounded. This is the default. */
-extern void GC_set_max_heap_size GC_PROTO((GC_word n));
+GC_API void GC_set_max_heap_size GC_PROTO((GC_word n));
/* Clear the set of root segments. Wizards only. */
-extern void GC_clear_roots GC_PROTO((void));
+GC_API void GC_clear_roots GC_PROTO((void));
/* Add a root segment. Wizards only. */
-extern void GC_add_roots GC_PROTO((char * low_address,
+GC_API void GC_add_roots GC_PROTO((char * low_address,
char * high_address_plus_1));
/* Add a displacement to the set of those considered valid by the */
/* retention. */
/* This is a no-op if the collector was compiled with recognition of */
/* arbitrary interior pointers enabled, which is now the default. */
-void GC_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_register_displacement GC_PROTO((GC_word n));
/* The following version should be used if any debugging allocation is */
/* being done. */
-void GC_debug_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_debug_register_displacement GC_PROTO((GC_word n));
/* Explicitly trigger a full, world-stop collection. */
-void GC_gcollect GC_PROTO((void));
+GC_API void GC_gcollect GC_PROTO((void));
/* Trigger a full world-stopped collection. Abort the collection if */
/* and when stop_func returns a nonzero value. Stop_func will be */
/* aborted collections do no useful work; the next collection needs */
/* to start from the beginning. */
typedef int (* GC_stop_func) GC_PROTO((void));
-int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
+GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
/* Return the number of bytes in the heap. Excludes collector private */
/* data structures. Includes empty blocks and fragmentation loss. */
/* Includes some pages that were allocated but never written. */
-size_t GC_get_heap_size GC_PROTO((void));
+GC_API size_t GC_get_heap_size GC_PROTO((void));
/* Return the number of bytes allocated since the last collection. */
-size_t GC_get_bytes_since_gc GC_PROTO((void));
+GC_API size_t GC_get_bytes_since_gc GC_PROTO((void));
/* Enable incremental/generational collection. */
/* Not advisable unless dirty bits are */
/* pointerfree(atomic) or immutable. */
/* Don't use in leak finding mode. */
/* Ignored if GC_dont_gc is true. */
-void GC_enable_incremental GC_PROTO((void));
+GC_API void GC_enable_incremental GC_PROTO((void));
/* Perform some garbage collection work, if appropriate. */
/* Return 0 if there is no more work to be done. */
/* progress requires it, e.g. if incremental collection is */
/* disabled. It is reasonable to call this in a wait loop */
/* until it returns 0. */
-int GC_collect_a_little GC_PROTO((void));
+GC_API int GC_collect_a_little GC_PROTO((void));
/* Allocate an object of size lb bytes. The client guarantees that */
/* as long as the object is live, it will be referenced by a pointer */
/* for arrays likely to be larger than 100K or so. For other systems, */
/* or if the collector is not configured to recognize all interior */
/* pointers, the threshold is normally much higher. */
-extern GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb));
-extern GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb));
+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));
/* Debugging (annotated) allocation. GC_gcollect will check */
/* objects allocated in this way for overwrites, etc. */
-extern GC_PTR GC_debug_malloc
+GC_API GC_PTR GC_debug_malloc
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_atomic
+GC_API GC_PTR GC_debug_malloc_atomic
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_uncollectable
+GC_API GC_PTR GC_debug_malloc_uncollectable
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_stubborn
+GC_API GC_PTR GC_debug_malloc_stubborn
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern void GC_debug_free GC_PROTO((GC_PTR object_addr));
-extern GC_PTR GC_debug_realloc
+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));
-void GC_debug_change_stubborn GC_PROTO((GC_PTR));
-void GC_debug_end_stubborn_change GC_PROTO((GC_PTR));
+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__)
__LINE__)
# define GC_FREE(p) GC_debug_free(p)
# define GC_REGISTER_FINALIZER(p, f, d, of, od) \
- GC_register_finalizer(GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_register_finalizer_ignore_self( \
- GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p)
typedef void (*GC_finalization_proc)
GC_PROTO((GC_PTR obj, GC_PTR client_data));
-extern void GC_register_finalizer
+GC_API void GC_register_finalizer
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* When obj is no longer accessible, invoke */
/* pointers will not be finalized (or collected). */
/* Thus cycles involving finalizable objects should */
/* be avoided, or broken by disappearing links. */
- /* Fn should terminate as quickly as possible, and */
- /* defer extended computation. */
/* All but the last finalizer registered for an object */
/* is ignored. */
/* Finalization may be removed by passing 0 as fn. */
+ /* Finalizers are implicitly unregistered just before */
+ /* they are invoked. */
/* The old finalizer and client data are stored in */
/* *ofn and *ocd. */
/* Fn is never invoked on an accessible object, */
/* but it's unavoidable for C++, since the compiler may */
/* silently introduce these. It's also benign in that specific */
/* case. */
-extern void GC_register_finalizer_ignore_self
+GC_API void GC_register_finalizer_ignore_self
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer_ignore_self
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* where p is a pointer that is not followed by finalization */
/* code, and should not be considered in determining */
/* finalization order. */
-extern int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Link should point to a field of a heap allocated */
/* object obj. *link will be cleared when obj is */
/* found to be inaccessible. This happens BEFORE any */
/* otherwise. */
/* Only exists for backward compatibility. See below: */
-extern int GC_general_register_disappearing_link
+GC_API int GC_general_register_disappearing_link
GC_PROTO((GC_PTR * /* link */, GC_PTR obj));
/* A slight generalization of the above. *link is */
/* cleared when obj first becomes inaccessible. This */
/* the object containing link. Explicitly deallocating */
/* obj may or may not cause link to eventually be */
/* cleared. */
-extern int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Returns 0 if link was not actually registered. */
/* Undoes a registration by either of the above two */
/* routines. */
/* Auxiliary fns to make finalization work correctly with displaced */
/* pointers introduced by the debugging allocators. */
-extern GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data));
-extern void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data));
+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_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));
-extern GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
+GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
/* Returns old warning procedure. */
/* The following is intended to be used by a higher level */
# endif /* I_HIDE_POINTERS */
typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data));
-extern GC_PTR GC_call_with_alloc_lock
+GC_API GC_PTR GC_call_with_alloc_lock
GC_PROTO((GC_fn_type fn, GC_PTR client_data));
/* Check that p and q point to the same object. */
/* Returns the first argument. */
/* Succeeds if neither p nor q points to the heap. */
/* May succeed if both p and q point to between heap objects. */
-extern GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
+GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
/* Checked pointer pre- and post- increment operations. Note that */
/* the second argument is in units of bytes, not multiples of the */
/* object size. This should either be invoked from a macro, or the */
/* call should be automatically generated. */
-extern GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
-extern GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
/* Check that p is visible */
/* to the collector as a possibly pointer containing location. */
/* untyped allocations. The idea is that it should be possible, though */
/* slow, to add such a call to all indirect pointer stores.) */
/* Currently useless for multithreaded worlds. */
-extern GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
/* Check that if p is a pointer to a heap page, then it points to */
/* a valid displacement within a heap object. */
/* Fail conspicuously if this property does not hold. */
/* Uninteresting with ALL_INTERIOR_POINTERS. */
/* Always returns its argument. */
-extern GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
/* Safer, but slow, pointer addition. Probably useful mainly with */
/* a preprocessor. Useful only for heap pointers. */
# define GC_PTR_STORE(p, q) *((p) = (q))
#endif
+/* Fynctions called to report pointer checking errors */
+GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q));
+
+GC_API void (*GC_is_valid_displacement_print_proc)
+ GC_PROTO((GC_PTR p));
+
+GC_API void (*GC_is_visible_print_proc)
+ GC_PROTO((GC_PTR p));
#ifdef SOLARIS_THREADS
/* We need to intercept calls to many of the threads primitives, so */
# define thr_continue GC_thr_continue
# define dlopen GC_dlopen
+# endif /* SOLARIS_THREADS */
+
+#ifdef IRIX_THREADS
+/* We treat these similarly. */
+# include <pthread.h>
+# include <signal.h>
+
+ int GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
+ int GC_pthread_join(pthread_t thread, void **retval);
+
+# define pthread_create GC_pthread_create
+# define pthread_sigmask GC_pthread_sigmask
+# define pthread_join GC_pthread_join
+
+#endif /* IRIX_THREADS */
+
+#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
/* 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. */
# ifndef CORD_POSITION_H
/* The representation of CORD_position. This is private to the */
-/* implementation, but the ise is known to clients. Also */
+/* implementation, but the size is known to clients. Also */
/* the implementation of some exported macros relies on it. */
/* Don't use anything defined here and not in cord.h. */
/* A structure describing an entry on the path from the root */
/* to current position. */
-typedef struct CORD_pos {
+typedef struct CORD_Pos {
size_t cur_pos;
int path_len;
# define CORD_POS_INVALID (0x55555555)
}
# ifdef FIND_LEAK
GC_free(base);
+# else
+ {
+ register hdr * hhdr = HDR(p);
+ bool uncollectable = FALSE;
+
+ if (hhdr -> hb_obj_kind == UNCOLLECTABLE) {
+ uncollectable = TRUE;
+ }
+# ifdef ATOMIC_UNCOLLECTABLE
+ if (hhdr -> hb_obj_kind == AUNCOLLECTABLE) {
+ uncollectable = TRUE;
+ }
+# endif
+ if (uncollectable) GC_free(base);
+ }
# endif
}
case PTRFREE:
result = GC_debug_malloc_atomic(lb, s, i);
break;
+ case UNCOLLECTABLE:
+ result = GC_debug_malloc_uncollectable(lb, s, i);
+ break;
default:
GC_err_printf0("GC_debug_realloc: encountered bad kind\n");
ABORT("bad kind");
(*(cl -> cl_fn))((GC_PTR)((char *)obj + sizeof(oh)), cl -> cl_data);
}
+
+# ifdef __STDC__
+ void GC_debug_register_finalizer(GC_PTR obj, GC_finalization_proc fn,
+ GC_PTR cd, GC_finalization_proc *ofn,
+ GC_PTR *ocd)
+# else
+ void GC_debug_register_finalizer(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 called with non-base-pointer 0x%lx\n",
+ obj);
+ }
+ GC_register_finalizer(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,
+ GC_PTR *ocd)
+# else
+ void GC_debug_register_finalizer_ignore_self
+ (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_ignore_self called with non-base-pointer 0x%lx\n",
+ obj);
+ }
+ GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer,
+ GC_make_closure(fn,cd), ofn, ocd);
+}
+++ /dev/null
-/* dec_threads.c
-/*
-/* Support for DECthreads in Boehm/Demer/Weiser garbage collector.
-/* Depends on a gc.a compiled with -DDEC_PTHREADS, plus local modifications.
-/* Final executable must be linked with "-lpthreads -lmach -lc_r".
-/*
-/* Author: Ian.Piumarta@INRIA.fr
-/*
-/* Copyright (C) 1996 by INRIA and Ian Piumarta
-/*
-/* Known problems:
-/*
-/* - debugging needs to be tidied up
-/*
-/* - some redundant code should be removed
-/*
-/* last edited: Wed Nov 13 15:42:21 1996 by piumarta (Ian Piumarta) on xombul
-/*
-/***************************************************************************/
-
-#ifdef DEC_PTHREADS
-
-/* Turn on debugging output */
-#undef DEBUG
-
-/* Turn on trace messages for thread creation/deletion */
-#undef TRACE
-
-/* visual indication of GC: */
-/* * = push stack for GC thread, */
-/* + = push stack for other thread */
-#undef VISUAL_CLUES
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <pthread.h>
-#include <sys/user.h>
-#include <sys/table.h>
-#include <mach/mach_interface.h>
-#include <mach/thread_status.h>
-
-extern char *GC_malloc_uncollectable();
-extern pthread_mutex_t GC_allocate_ml;
-extern int GC_pt_init_ok;
-
-#define ERR(X) { perror(X); abort(); }
-
-extern GC_err_printf();
-
-#ifdef DEBUG
-# define FPRINTF(X) GC_err_printf X
-#else
-# define FPRINTF(X)
-#endif
-
-#define PRINT_INFO(X,Y,Z) /* or #define PRINT_INFO print_info */
-
-#define CHECK_DUPLICATE (2<<0)
-#define CHECK_OVERLAP (2<<1)
-
-#define struct_base(PTR, FLD, TYP) \
- ((TYP*)((char*)(PTR)-(char*)(&(((TYP*)0)->FLD))))
-
-typedef struct QUEUE {
- struct QUEUE *flink, *blink;
-} queue;
-
-typedef struct GC_PT_THREAD_INFO {
- queue q;
- /* user thread structure */
- pthread_t thread;
- /* start routine and argument */
- pthread_startroutine_t starter;
- pthread_addr_t arg;
- /* mach port */
- port_t port;
- /* stack top */
- void *stack_top;
- /* approximate thread status */
- short active;
- short exited;
- short detached;
- /* saved register state */
- struct alpha_thread_state reg_state;
-} gc_pt_thread_info;
-
-#define TID(info) (info->port)
-
-static queue infos = { &infos, &infos };
-
-#define DISABLE_SIGNALS() GC_disable_signals()
-#define ENABLE_SIGNALS() GC_enable_signals()
-
-#define LOCK(ML) \
- if(pthread_mutex_lock(&GC_pt_##ML##_ml)<0) ERR("LOCK(GC_pt_"#ML"_ml)");
-
-#define UNLOCK(ML) \
- if(pthread_mutex_unlock(&GC_pt_##ML##_ml)<0) ERR("UNLOCK(GC_pt_"#ML"_ml)");
-
-static pthread_mutex_t GC_pt_info_ml;
-static pthread_mutex_t GC_pt_init_ml;
-
-static pthread_key_t info_key;
-
-
-static print_info(gc_pt_thread_info *info, char *msg, long arg)
-{
- GC_err_printf("********** INFO structure at %lx size %d for ",
- info, sizeof(*info));
- GC_err_printf(msg, arg);
- GC_err_printf(":\n");
- /* this is VERY naughty: this structure is supposed to be opaque. */
- GC_err_printf(" thread at %lx = (%lx, %lx)\n",
- info->thread, info->thread.field1, info->thread.field2);
- GC_err_printf(" startroutine at %lx, arg %lx\n", info->starter, info->arg);
- GC_err_printf(" mach port number is %d\n", info->port);
- GC_err_printf(" stack_top is %lx\n", info->stack_top);
- GC_err_printf(" stack pointer is %lx\n", info->reg_state.r30);
- GC_err_printf(" thread is %s", info->active ? "active" : "inactive");
- GC_err_printf(", %s", info->exited ? "exited" : "unexited");
- GC_err_printf(", %s", info->detached ? "detached" : "attached");
- GC_err_printf("\n flink %lx, blink %lx\n", info->q.flink, info->q.blink);
-}
-
-
-static print_infos()
-{
- queue *ptr= infos.flink;
-
- GC_err_printf("infos.flink = %lx\n", infos.flink);
- print_info(struct_base(ptr, q, gc_pt_thread_info), "flink", 0);
- GC_err_printf("infos.blink = %lx\n", infos.flink);
- print_info(struct_base(ptr, q, gc_pt_thread_info), "blink", 0);
-
- while (ptr != &infos) {
- GC_err_printf("q ===> %lx:", ptr);
- print_info(struct_base(ptr, q, gc_pt_thread_info), "print_infos", 0);
- ptr= ptr->flink;
- }
-}
-
-
-/* GC_pt_check
- *
- * Check the given gc_pt_thread_info structure is consistent with the others.
- *
- * Caller must hold the info lock.
- */
-static int GC_pt_check(gc_pt_thread_info *info)
-{
- queue *ptr= infos.flink;
- int fail= 0;
- while (ptr != &infos) {
- gc_pt_thread_info *old= struct_base(ptr, q, gc_pt_thread_info);
-/*
- if (info != old && info->port == old->port) {
- fail|= CHECK_DUPLICATE;
- FPRINTF((stderr, "duplicate thread: %d\n", info->port));
- }
-*/
- if (info != old && pthread_equal(info->thread, old->thread)) {
- fail|= CHECK_DUPLICATE;
- FPRINTF((stderr, "duplicate thread: %d\n", info->port));
- }
-/*
- if (info != old &&
- (((char *)info->stack_top > (char *)old->reg_state.r30 &&
- (char *)info->stack_top < (char *)old->stack_top) ||
- ((char *)info->reg_state.r30 > (char *)old->reg_state.r30 &&
- (char *)info->reg_state.r30 < (char *)old->stack_top))) {
- fail|= CHECK_OVERLAP;
- FPRINTF((stderr, "stack overlaps for threads: %lx and %lx\n",
- TID(old), TID(info)));
- }
-*/
- ptr= ptr->flink;
- }
- return fail;
-}
-
-
-/* This must be called with the info lock set.
- */
-static gc_pt_thread_info *GC_pt_find_pthread_info(pthread_t *thread)
-{
- queue *ptr= infos.flink;
- while (ptr != &infos) {
- gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info);
- if (pthread_equal(*thread, info->thread))
- return info;
- ptr= ptr->flink;
- }
- return 0;
-}
-
-
-static void GC_pt_info_destroy(pthread_addr_t infop);
-
-
-/* GC_pt_init
- *
- * Initialises DECthreads-specific data structures. Should be called
- * before any threads are created, and before any posibility of GC
- * activity.
- */
-void GC_pt_init()
-{
- gc_pt_thread_info *info;
- struct user u;
- pthread_mutexattr_t attr;
-
- if(GC_pt_init_ok) return;
- GC_pt_init_ok= 1;
- FPRINTF((stderr, "initialising GC_dt...\n"));
- FPRINTF((stderr, "ENTER GC_pt_init()\n"));
- if (pthread_mutexattr_create(&attr)
- || pthread_mutexattr_setkind_np(&attr, MUTEX_RECURSIVE_NP)
- || pthread_mutex_init(&GC_pt_info_ml, attr)
- || pthread_mutex_init(&GC_pt_init_ml, attr)
- || pthread_keycreate(&info_key, GC_pt_info_destroy))
- ERR("GC_pt_init");
-
- if (table(TBL_UAREA, getpid(), &u, 1, sizeof(struct user)) < 1) {
- GC_err_printf("problem getting u area\n");
- }
-
- info= (gc_pt_thread_info *)GC_malloc_uncollectable(sizeof(gc_pt_thread_info));
- if (!info) {
- GC_err_printf("GC_dt: could not allocate thread_info\n");
- exit(1);
- }
-
- info->port= thread_self();
- info->stack_top= u.u_stack_end;
- info->active= 1;
- info->exited= 0;
- info->detached= 0;
- info->q.flink= &infos;
- info->q.blink= &infos;
- bzero((char *)&info->reg_state, sizeof(info->reg_state));
-
- LOCK(info);
- infos.flink= infos.blink= &info->q;
- UNLOCK(info);
-
- PRINT_INFO(info, "default thread", 0);
-
- FPRINTF((stderr, "LEAVE GC_pt_init()\n"));
-}
-
-
-static void GC_pt_apply_other_threads(kern_return_t (*func)(port_t))
-{
- port_t me= thread_self();
-
- FPRINTF((stderr, "APPLYING for thread %d\n", me));
-
- LOCK(info);
- {
- queue *ptr= infos.flink;
- while (ptr != &infos)
- {
- gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info);
- if (info->port != me && info->active && !info->exited)
- {
- FPRINTF((stderr, "applying to thread %d:", info->port));
- FPRINTF((stderr, " %s %s %s\n",
- info->active ? "active" : "inactive",
- info->exited ? "exited" : "unexited",
- info->detached ? "detached" : "attached"));
- if (func(info->port) != KERN_SUCCESS)
- {
- UNLOCK(info);
- GC_err_printf("GC_pt_apply_other_threads failed:\n");
- print_info(info, "apply failed from thread %d", me);
- abort();
- }
- }
- else
- {
- FPRINTF((stderr, "NOT applying to thread %d:", info->port));
- FPRINTF((stderr, " %s %s %s\n",
- info->active ? "active" : "inactive",
- info->exited ? "exited" : "unexited",
- info->detached ? "detached" : "attached"));
- }
- ptr= ptr->flink;
- }
- }
- UNLOCK(info);
-}
-
-
-void GC_pt_stop_world()
-{
- if (!GC_pt_init_ok) GC_init();
- FPRINTF((stderr, "stopping the world...\n"));
- GC_pt_apply_other_threads(thread_suspend);
- FPRINTF((stderr, "...stopped!\n"));
-}
-
-void GC_pt_start_world()
-{
- FPRINTF((stderr, "starting the world...\n"));
- GC_pt_apply_other_threads(thread_resume);
- FPRINTF((stderr, "...started!\n"));
-}
-
-
-/* GC_pt_push_all_stacks
- *
- * Pushes all thread stacks, including the default stack. Default stack
- * pointer read from /proc, other thread stacks taken from stacks queue.
- * Locks the stacks to prevent thread creation/deletion during pushing.
- */
-void GC_pt_push_all_stacks()
-{
- queue *ptr= infos.flink;
- FPRINTF((stderr, "PUSHING THREAD STACKS:\n"));
- while (ptr != &infos)
- {
- gc_pt_thread_info *info= struct_base(ptr, q, gc_pt_thread_info);
- if (info->active && !info->exited)
- if (info->port == thread_self())
- {
-#ifdef VISUAL_CLUES
- fputc('*', stderr);
-#endif VISUAL_CLUES
- FPRINTF((stderr, "[%lx,%lx]\n", &ptr, info->stack_top));
- GC_push_all_stack(&ptr, info->stack_top);
- }
- else
- {
- int count= ALPHA_THREAD_STATE_COUNT;
- FPRINTF((stderr, "+"));
- /* get register state to (a) know sp, and (b) mark registers */
- PRINT_INFO(info, "thread %d PRE-GETSTATE", info->port);
- FPRINTF((stderr, " reg_state dumped to %lx, size %d\n",
- &(info->reg_state), sizeof(info->reg_state)));
- if (thread_get_state(info->port,
- ALPHA_THREAD_STATE,
- (thread_state_t)&(info->reg_state),
- &count) != KERN_SUCCESS)
- {
- GC_err_printf("problem in thread_get_state");
- exit(1);
- }
- PRINT_INFO(info, "thread %d POST-GETSTATE", info->port);
-#ifdef VISUAL_CLUES
- fputc('+', stderr);
-#endif VISUAL_CLUES
- FPRINTF((stderr, "[%lx,%lx]\n",
- info->reg_state.r30, info->stack_top));
- GC_push_all_stack(info->reg_state.r30, info->stack_top);
- }
- ptr= ptr->flink;
- }
-}
-
-
-/* GC_pt_thread_init
- *
- * Creates a gc_pt_thread_info structure describing the calling thread in the
- * stacks queue.
- */
-static void GC_pt_thread_init(gc_pt_thread_info *info, void *stacktop)
-{
- FPRINTF((stderr, "ENTER GC_pt_thread_init(%lx)\n", TID(info)));
- {
- int status;
- info->port= thread_self();
- info->stack_top= stacktop;
- PRINT_INFO(info, "thread %d", info->port);
- if (status= GC_pt_check(info)) {
- if (status & CHECK_DUPLICATE)
- GC_err_printf("GC_pt_thread_init: duplicated thread %lx\n",
- TID(info));
- if (status & CHECK_OVERLAP)
- GC_err_printf("GC_pt_thread_init: stack overlap %lx\n",
- TID(info));
- if (status) {
- GC_err_printf("GC_pt_thread_init: aborting, sorry...\n");
- abort();
- }
- }
- }
- FPRINTF((stderr, "LEAVE GC_pt_thread_init(%lx)\n", TID(info)));
-}
-
-
-/* GC_pt_info_destroy
- *
- * The thread is finished, and about to exit -- either due to a normal
- * return or a cancel request. If it is already detached, remove its
- * stack. If not, mark the thread as exited so that its stack will be
- * removed when it is eventually detached.
- */
-static void GC_pt_info_destroy(pthread_addr_t infop)
-{
- gc_pt_thread_info *info= (gc_pt_thread_info *)infop;
-
- FPRINTF((stderr, "ENTER GC_pt_info_destroy(%lx)\n", infop));
- if (pthread_mutex_lock(&GC_allocate_ml) < 0)
- ERR("GC_pt_info_destroy: lock(&GC_allocate_ml)");
- {
- /* safety first: check that the info structure really exists */
- queue *target= &info->q;
- queue *ptr= infos.flink;
- while (ptr != &infos)
- {
- if (ptr == target)
- break;
- else
- ptr= ptr->flink;
- }
- if (ptr != target)
- {
- if (pthread_mutex_unlock(&GC_allocate_ml) < 0)
- ERR("GC_pt_info_destroy: unlock(&GC_allocate_ml)");
- GC_err_printf("GC_pt_info_destroy: unknown info -- aborting\n");
- abort();
- }
- if (info->detached)
- {
- /* thread detached => delete stack on exit */
- queue *ptr= &info->q;
- ptr->flink->blink= ptr->blink;
- ptr->blink->flink= ptr->flink;
-#ifdef TRACE
- GC_err_printf("DELETE (exit) %lx\n", TID(info));
-#endif TRACE
- GC_free(info);
- }
- else
- {
- info->exited= 1; /* cancelled thread won't do this otherwise */
- info->active= 0;
- }
- }
- if (pthread_mutex_unlock(&GC_allocate_ml) < 0)
- ERR("GC_pt_info_destroy: unlock(&GC_allocate_ml)");
- FPRINTF((stderr, "LEAVE GC_pt_info_destroy(%lx)\n", infop));
-}
-
-
-static pthread_addr_t GC_pt_start_routine(gc_pt_thread_info *info)
-{
- pthread_addr_t status;
- LOCK(info);
- /* wait for GC_pt_pthread_create to finish building info */
- FPRINTF((stderr, "ENTER GC_pt_start_routine(%lx)\n", TID(info)));
- GC_pt_thread_init(info, &info);
- UNLOCK(info); /* it is now safe to start the next thread */
- pthread_setspecific(info_key, info); /* the reaper for the thread */
- /* this thread might be starting up during a GC! Do not proceed beyond
- this point until the allocation lock becomes available. */
- pthread_mutex_lock(&GC_allocate_ml);
- info->active= 1;
- pthread_mutex_unlock(&GC_allocate_ml);
-#ifdef TRACE
- GC_err_printf("LAUNCH (start) %lx\n", TID(info));
-#endif TRACE
- status= info->starter(info->arg);
- info->active= 0;
- info->exited= 1;
- FPRINTF((stderr, "LEAVE GC_pt_start_routine(%lx)\n", TID(info)));
- return status;
-}
-
-/* GC_pt_pthread_create
- *
- * Creates a new thread, allocates a gc_pt_thread_info object describing the
- * new thread,
- *
- * Get stack information by intercepting thread creation.
- */
-int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t attr,
- pthread_startroutine_t start_routine,
- pthread_addr_t arg)
-{
- int result;
- gc_pt_thread_info *info;
-
- FPRINTF((stderr, "ENTER GC_pt_pthread_create(%lx)\n", thread));
- info= (void *)GC_malloc_uncollectable(sizeof(gc_pt_thread_info));
- info->starter= start_routine;
- info->arg= arg;
- info->active= 0;
- info->detached= 0;
- info->exited= 0;
- LOCK(info);
- if (result= pthread_create(thread, attr,
- (pthread_startroutine_t)GC_pt_start_routine,
- (pthread_addr_t)info)) {
- GC_free(info);
- FPRINTF((stderr, "LEAVE GC_pt_pthread_create(%lx) ===> ERROR\n", thread));
- goto end; /* something is amiss */
- }
- info->thread= *thread;
- PRINT_INFO(info, "uninitialized new thread", 0);
- /* add to end of info queue */
- {
- queue *ptr= &info->q;
- ptr->flink= &infos;
- ptr->blink= infos.blink;
- infos.blink->flink= ptr;
- infos.blink= ptr;
- }
- FPRINTF((stderr, "LEAVE GC_pt_pthread_create(%lx) ===> %lx\n",
- thread, TID(info)));
- end:
- UNLOCK(info);
- return result;
-}
-
-
-/* GC_pt_pthread_detach
- *
- * If thread already exited, remove its stack. Otherwise mark the thread
- * as detached, so that its stack will be removed when it eventually exits.
- */
-int GC_pt_pthread_detach(pthread_t *thread)
-{
- int result;
- FPRINTF((stderr, "ENTER GC_pt_pthread_detach(%lx)\n", thread));
- LOCK(info);
- {
- gc_pt_thread_info *info= GC_pt_find_pthread_info(thread);
- if (!info)
- {
- UNLOCK(info);
- errno= ESRCH;
- return -1;
- }
- if (info->exited)
- {
- /* thread exited => delete stack on detach */
- queue *ptr= &info->q;
- ptr->flink->blink= ptr->blink;
- ptr->blink->flink= ptr->flink;
- FPRINTF((stderr, "DELETE (detach) %lx\n", TID(info)));
- GC_free(info);
- }
- else
- {
- info->detached= 1;
- }
- }
- UNLOCK(info);
- result= pthread_detach(thread);
- FPRINTF((stderr, "LEAVE GC_pt_pthread_detach(%lx)\n", thread));
- return result;
-}
-
-
-#endif DEC_PTHREADS
# endif
#if (defined(DYNAMIC_LOADING) || defined(MSWIN32)) && !defined(PCR)
-#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && !defined(MSWIN32) && !defined(ALPHA) && !defined(HP_PA)
- --> We only know how to find data segments of dynamic libraries under SunOS,
- --> IRIX5, DRSNX and Win32. Additional SVR4 variants might not be too
+#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && !defined(MSWIN32) && !(defined(ALPHA) && defined(OSF1)) &&!defined(HP_PA) && (!defined(LINUX) && !defined(__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.
#endif
# if defined(SUNOS4) || defined(SUNOS5DL)
/* Add dynamic library data sections to the root set. */
-# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS) \
- && !defined(MIT_PTHREADS) && !defined(DEC_PTHREADS)
+# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS)
# ifndef SRC_M3
--> fix mutual exclusion with dlopen
# endif /* We assume M3 programs don't call dlopen for now */
# endif /* SUNOS */
+#if defined(LINUX) && defined(__ELF__)
+
+/* Dynamic loading code for Linux running ELF. Somewhat tested on
+ * Linux/x86, untested but hopefully should work on Linux/Alpha.
+ * This code was derived from the Solaris/ELF support. Thanks to
+ * whatever kind soul wrote that. - Patrick Bridges */
+
+#include <elf.h>
+#include <link.h>
+
+/* Newer versions of Linux/Alpha and Linux/x86 define this macro. We
+ * define it for those older versions that don't. */
+# ifndef ElfW
+# if ELF_CLASS == ELFCLASS32
+# define ElfW(type) Elf32_##type
+# else
+# define ElfW(type) Elf64_##type
+# endif
+# endif
+
+static struct link_map *
+GC_FirstDLOpenedLinkMap()
+{
+ extern ElfW(Dyn) _DYNAMIC[];
+ ElfW(Dyn) *dp;
+ struct r_debug *r;
+ static struct link_map *cachedResult = 0;
+
+ if( cachedResult == 0 ) {
+ int tag;
+ for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
+ if( tag == DT_DEBUG ) {
+ struct link_map *lm
+ = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
+ if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
+ break;
+ }
+ }
+ }
+ return cachedResult;
+}
+
+
+void GC_register_dynamic_libraries()
+{
+ struct link_map *lm = GC_FirstDLOpenedLinkMap();
+
+
+ for (lm = GC_FirstDLOpenedLinkMap();
+ lm != (struct link_map *) 0; lm = lm->l_next)
+ {
+ ElfW(Ehdr) * e;
+ ElfW(Phdr) * p;
+ unsigned long offset;
+ char * start;
+ register int i;
+
+ e = (ElfW(Ehdr) *) lm->l_addr;
+ p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
+ offset = ((unsigned long)(lm->l_addr));
+ for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
+ switch( p->p_type ) {
+ case PT_LOAD:
+ {
+ if( !(p->p_flags & PF_W) ) break;
+ start = ((char *)(p->p_vaddr)) + offset;
+ GC_add_roots_inner(start, start + p->p_memsz, TRUE);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+#endif
+
#ifdef IRIX5
#include <sys/procfs.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <elf.h>
+#include <errno.h>
extern void * GC_roots_present();
-extern ptr_t GC_scratch_end_ptr; /* End of GC_scratch_alloc arena */
+extern ptr_t GC_scratch_last_end_ptr; /* End of GC_scratch_alloc arena */
/* We use /proc to track down all parts of the address space that are */
/* mapped by the process, and throw out regions we know we shouldn't */
register long flags;
register ptr_t start;
register ptr_t limit;
- ptr_t heap_end = (ptr_t)DATASTART;
+ ptr_t heap_start = (ptr_t)HEAP_START;
+ ptr_t heap_end = heap_start;
if (fd < 0) {
sprintf(buf, "/proc/%d", getpid());
}
}
if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
+ GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
ABORT("/proc PIOCNMAP ioctl failed");
}
if (needed_sz >= current_sz) {
addr_map = (prmap_t *)GC_scratch_alloc(current_sz * sizeof(prmap_t));
}
if (ioctl(fd, PIOCMAP, addr_map) < 0) {
+ GC_err_printf2("fd = %d, errno = %d\n", fd, errno);
ABORT("/proc PIOCMAP ioctl failed");
};
if (GC_n_heap_sects > 0) {
heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
+ GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
- if (heap_end < GC_scratch_end_ptr) heap_end = GC_scratch_end_ptr;
+ if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
}
for (i = 0; i < needed_sz; i++) {
flags = addr_map[i].pr_mflags;
/* This makes no sense to me. - HB */
start = (ptr_t)(addr_map[i].pr_vaddr);
if (GC_roots_present(start)) goto irrelevant;
- if (start < heap_end && start >= (ptr_t)DATASTART)
+ if (start < heap_end && start >= heap_start)
goto irrelevant;
limit = start + addr_map[i].pr_size;
if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
GC_add_roots_inner(start, limit, TRUE);
irrelevant: ;
}
+ /* Dont keep cached descriptor, for now. Some kernels don't like us */
+ /* to keep a /proc file descriptor around during kill -9. */
+ if (close(fd) < 0) ABORT("Couldnt close /proc file");
+ fd = -1;
}
#endif /* IRIX5 */
DWORD GC_allocation_granularity;
extern bool GC_is_heap_base (ptr_t p);
+
+# ifdef WIN32_THREADS
+ extern void GC_get_next_stack(char *start, char **lo, char **hi);
+# endif
void GC_cond_add_roots(char *base, char * limit)
{
char * stack_top
= (char *) ((word)(&dummy) & ~(GC_allocation_granularity-1));
if (base == limit) return;
- if (limit > stack_top && base < GC_stackbottom) {
- /* Part of the stack; ignore it. */
- return;
+# ifdef WIN32_THREADS
+ {
+ char * curr_base = base;
+ char * next_stack_lo;
+ char * next_stack_hi;
+
+ for(;;) {
+ GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
+ if (next_stack_lo >= limit) break;
+ GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
+ curr_base = next_stack_hi;
+ }
+ if (curr_base < limit) GC_add_roots_inner(curr_base, limit);
}
- GC_add_roots_inner(base, limit, TRUE);
+# else
+ if (limit > stack_top && base < GC_stackbottom) {
+ /* Part of the stack; ignore it. */
+ return;
+ }
+ GC_add_roots_inner(base, limit, TRUE);
+# endif
}
extern bool GC_win32s;
#endif /* MSWIN32 */
-#if defined(ALPHA)
+#if defined(ALPHA) && defined(OSF1)
#include <loader.h>
#if defined(HP_PA)
#include <dl.h>
-#include <errno.h>
-#ifndef MIT_PTHREADS
extern int errno;
extern char *sys_errlist[];
extern int sys_nerr;
-#endif
void GC_register_dynamic_libraries()
{
GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]);
} else {
GC_printf1("dynamic_load: %d\n", (long) errno);
- }
+ }
ABORT("shl_get failed");
}
}
GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename);
GC_printf1("\tindex = %d\n", index);
GC_printf1("\thandle = %08x\n",
- (unsigned long) shl_desc->handle);
+ (unsigned long) shl_desc->handle);
GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart);
GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend);
GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart);
/* register shared library's data segment as a garbage collection root */
GC_add_roots_inner((char *) shl_desc->dstart,
- (char *) shl_desc->dend, TRUE);
+ (char *) shl_desc->dend, TRUE);
index++;
}
#endif /* HP_PA */
-#if defined(MIT_PTHREADS) || defined(DEC_PTHREADS)
-#ifdef HP_PA
-
- /* We're going to protect the lot, since there is evidence */
- /* that dlopen() isn't the only place where things can break. */
-
-#undef shl_load
-#undef shl_findsym
-#undef shl_unload
-
-# define GC_DL_DEF(RTYPE, PROTO, FUNC) RTYPE PROTO \
- { RTYPE result; pthread_mutex_lock(&GC_allocate_ml); result= FUNC; \
- pthread_mutex_unlock(&GC_allocate_ml); return (result); }
-GC_DL_DEF(shl_t, GC_shl_load(const char *path, int flags, long address), shl_load(path, flags, address))
-GC_DL_DEF(int , GC_shl_findsym(shl_t *handle, const char *sym, short type, void *value),
- shl_findsym(handle, sym, type, value))
-GC_DL_DEF(int , GC_shl_unload(shl_t handle), shl_unload(handle))
-
- /* unfortunately, MIT PThreads and dynamic loading on HPUX
- causes programs to hang (not just for the GC);
- we keep everything in but define some null-op stubs;
- makes it easier to fix when pthreads is fixed */
-
- shl_t shl_load(const char *path, int flags, long address)
- {
- return NULL;
- }
-
- int shl_findsym(shl_t *handle, const char *symname, short type, void *value)
- {
- return -1;
- }
-
- int shl_unload(shl_t handle)
- {
- return -1;
- }
-
- int shl_get(int index, struct shl_descriptor **desc)
- {
- errno = EINVAL;
- return -1;
- }
-
-# define shl_load GC_shl_load
-# define shl_findsym GC_shl_findsym
-# define shl_unload GC_shl_unload
-
-#else
- /* We're going to protect the lot, since there is evidence */
- /* that dlopen() isn't the only place where things can break. */
-
-#undef dlopen
-#undef dlsym
-#undef dlclose
-#undef dlerror
-
-# define GC_DL_DEF(RTYPE, PROTO, FUNC) RTYPE PROTO \
- { RTYPE result; pthread_mutex_lock(&GC_allocate_ml); result= FUNC; \
- pthread_mutex_unlock(&GC_allocate_ml); return (result); }
-GC_DL_DEF(void *, GC_dlopen(const char *path, int mode), dlopen(path, mode))
-GC_DL_DEF(void *, GC_dlsym(void *handle, const char *symbol),
- dlsym(handle, symbol))
-GC_DL_DEF(int , GC_dlclose(void *handle), dlclose(handle))
-GC_DL_DEF(char *, GC_dlerror(), dlerror())
-
-# define dlopen GC_dlopen
-# define dlsym GC_dlsym
-# define dlclose GC_dlclose
-# define dlerror GC_dlerror
-#endif
-#endif /* PTHREADS */
-
-
#else /* !DYNAMIC_LOADING */
#ifdef PCR
ocd, GC_null_finalize_mark_proc);
}
-
/* Called with world stopped. Cause disappearing links to disappear, */
/* and invoke finalizers. */
void GC_finalize()
for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base);
if (!GC_is_marked(real_ptr)) {
- (*(curr_fo -> fo_mark_proc))(real_ptr);
- while (!GC_mark_stack_empty()) GC_mark_from_mark_stack();
- if (GC_mark_state != MS_NONE) {
- /* Mark stack overflowed. Very unlikely. */
-# ifdef PRINTSTATS
- if (GC_mark_state != MS_INVALID) ABORT("Bad mark state");
- GC_printf0("Mark stack overflowed in finalization!!\n");
-# endif
- /* Make mark bits consistent again. Forget about */
- /* finalizing this object for now. */
- GC_set_mark_bit(real_ptr);
- while (!GC_mark_some());
- }
+ GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
if (GC_is_marked(real_ptr)) {
WARN("Finalization cycle involving %lx\n", real_ptr);
}
}
-
}
}
/* Enqueue for finalization all objects that are still */
while (curr_fo != 0) {
real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base);
if (!GC_is_marked(real_ptr)) {
+# ifndef JAVA_FINALIZATION
GC_set_mark_bit(real_ptr);
+# endif
/* Delete from hash table */
next_fo = fo_next(curr_fo);
if (prev_fo == 0) {
}
}
}
+
+# ifdef JAVA_FINALIZATION
+ /* make sure we mark everything reachable from objects finalized
+ using the no_order mark_proc */
+ for (curr_fo = GC_finalize_now;
+ curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
+ real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
+ if (!GC_is_marked(real_ptr)) {
+ if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
+ GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
+ }
+ GC_set_mark_bit(real_ptr);
+ }
+ }
+# endif
+
/* Remove dangling disappearing links. */
for (i = 0; i < dl_size; i++) {
curr_dl = dl_head[i];
}
}
+#ifdef JAVA_FINALIZATION
+
+/* Enqueue all remaining finalizers to be run - Assumes lock is
+ * held, and signals are disabled */
+void GC_enqueue_all_finalizers()
+{
+ struct finalizable_object * curr_fo, * prev_fo, * next_fo;
+ ptr_t real_ptr, real_link;
+ register int i;
+ int fo_size;
+
+ fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
+ GC_words_finalized = 0;
+ for (i = 0; i < fo_size; i++) {
+ curr_fo = fo_head[i];
+ prev_fo = 0;
+ while (curr_fo != 0) {
+ real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base);
+ GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
+ GC_set_mark_bit(real_ptr);
+
+ /* Delete from hash table */
+ next_fo = fo_next(curr_fo);
+ if (prev_fo == 0) {
+ fo_head[i] = next_fo;
+ } else {
+ fo_set_next(prev_fo, next_fo);
+ }
+ GC_fo_entries--;
+
+ /* Add to list of objects awaiting finalization. */
+ fo_set_next(curr_fo, GC_finalize_now);
+ GC_finalize_now = curr_fo;
+
+ /* unhide object pointer so any future collections will */
+ /* see it. */
+ curr_fo -> fo_hidden_base =
+ (word) REVEAL_POINTER(curr_fo -> fo_hidden_base);
+
+ GC_words_finalized +=
+ ALIGNED_WORDS(curr_fo -> fo_object_size)
+ + ALIGNED_WORDS(sizeof(struct finalizable_object));
+ curr_fo = next_fo;
+ }
+ }
+
+ return;
+}
+
+/* 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.
+ * 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
+ */
+void GC_finalize_all()
+{
+ DCL_LOCK_STATE;
+
+ DISABLE_SIGNALS();
+ LOCK();
+ while (GC_fo_entries > 0) {
+ GC_enqueue_all_finalizers();
+ UNLOCK();
+ ENABLE_SIGNALS();
+ GC_invoke_finalizers();
+ DISABLE_SIGNALS();
+ LOCK();
+ }
+ UNLOCK();
+ ENABLE_SIGNALS();
+}
+#endif
+
/* Invoke finalizers for all objects that are ready to be finalized. */
/* Should be called without allocation lock. */
void GC_invoke_finalizers()
# ifdef THREADS
DISABLE_SIGNALS();
LOCK();
+ SET_LOCK_HOLDER();
# endif
result = (*fn)(client_data);
# ifdef THREADS
+ UNSET_LOCK_HOLDER();
UNLOCK();
ENABLE_SIGNALS();
# endif
return(result);
}
-
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
- * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
+ * Copyright 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, October 9, 1995 1:14 pm PDT */
/*
* Note that this defines a large number of tuning hooks, which can
#ifndef _GC_H
# define _GC_H
+# define __GC
+# include <stddef.h>
+
+
+#if defined(_MSC_VER) && defined(_DLL)
+#ifdef GC_BUILD
+#define GC_API __declspec(dllexport)
+#else
+#define GC_API __declspec(dllimport)
+#endif
+#endif
+
+#ifndef GC_API
+#define GC_API extern
+#endif
# if defined(__STDC__) || defined(__cplusplus)
# define GC_PROTO(args) args
extern "C" {
# endif
-# include <stddef.h>
/* Define word and signed_word to be unsigned and signed types of the */
/* size as char * or void *. There seems to be no way to do this */
/* Public read-only variables */
-extern GC_word GC_gc_no;/* Counter incremented per collection. */
+GC_API GC_word GC_gc_no;/* Counter incremented per collection. */
/* Includes empty GCs at startup. */
/* Public R/W variables */
-extern int GC_quiet; /* Disable statistics output. Only matters if */
+GC_API void * (*GC_oom_fn) GC_PROTO((size_t bytes_requested));
+ /* When there is insufficient memory to satisfy */
+ /* an allocation request, we return */
+ /* (*GC_oom_fn)(). By default this just */
+ /* returns 0. */
+ /* If it returns, it must return 0 or a valid */
+ /* pointer to a previously allocated heap */
+ /* object. */
+
+GC_API int GC_quiet; /* Disable statistics output. Only matters if */
/* collector has been compiled with statistics */
/* enabled. This involves a performance cost, */
/* and is thus not the default. */
-extern int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
- /* beacuse it's not safe. */
+GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
+ /* because it's not safe. */
-extern int GC_dont_expand;
+GC_API int GC_dont_expand;
/* Dont expand heap unless explicitly requested */
/* or forced to. */
-extern int GC_full_freq; /* Number of partial collections between */
+GC_API int GC_full_freq; /* Number of partial collections between */
/* full collections. Matters only if */
/* GC_incremental is set. */
-extern GC_word GC_non_gc_bytes;
+GC_API GC_word GC_non_gc_bytes;
/* Bytes not considered candidates for collection. */
/* Used only to control scheduling of collections. */
-extern GC_word GC_free_space_divisor;
+GC_API GC_word GC_free_space_divisor;
/* We try to make sure that we allocate at */
/* least N/GC_free_space_divisor bytes between */
/* collections, where N is the heap size plus */
/* at the expense of space. */
/* GC_free_space_divisor = 1 will effectively */
/* disable collections. */
+
+GC_API GC_word GC_max_retries;
+ /* The maximum number of GCs attempted before */
+ /* reporting out of memory after heap */
+ /* expansion fails. Initially 0. */
/* Public procedures */
* collectable. GC_malloc_uncollectable and GC_free called on the resulting
* object implicitly update GC_non_gc_bytes appropriately.
*/
-extern GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+
+/* The following is only defined if the library has been suitably */
+/* compiled: */
+GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes));
/* Explicitly deallocate an object. Dangerous if used incorrectly. */
/* Requires a pointer to the base of an object. */
/* An object should not be enable for finalization when it is */
/* explicitly deallocated. */
/* GC_free(0) is a no-op, as required by ANSI C for free. */
-extern void GC_free GC_PROTO((GC_PTR object_addr));
+GC_API void GC_free GC_PROTO((GC_PTR object_addr));
/*
* Stubborn objects may be changed only if the collector is explicitly informed.
* do so. The same applies to dropping stubborn objects that are still
* changeable.
*/
-extern void GC_change_stubborn GC_PROTO((GC_PTR));
-extern void GC_end_stubborn_change GC_PROTO((GC_PTR));
+GC_API void GC_change_stubborn GC_PROTO((GC_PTR));
+GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR));
/* Return a pointer to the base (lowest address) of an object given */
/* a pointer to a location within the object. */
/* Return 0 if displaced_pointer doesn't point to within a valid */
/* object. */
-extern GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
+GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
/* Given a pointer to the base of an object, return its size in bytes. */
/* The returned size may be slightly larger than what was originally */
/* requested. */
-extern size_t GC_size GC_PROTO((GC_PTR object_addr));
+GC_API size_t GC_size GC_PROTO((GC_PTR object_addr));
/* For compatibility with C library. This is occasionally faster than */
/* a malloc followed by a bcopy. But if you rely on that, either here */
/* 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. */
-extern GC_PTR GC_realloc GC_PROTO((GC_PTR old_object,
+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. */
-extern int GC_expand_hp GC_PROTO((size_t number_of_bytes));
+GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes));
/* Limit the heap size to n bytes. Useful when you're debugging, */
/* especially on systems that don't handle running out of memory well. */
/* n == 0 ==> unbounded. This is the default. */
-extern void GC_set_max_heap_size GC_PROTO((GC_word n));
+GC_API void GC_set_max_heap_size GC_PROTO((GC_word n));
/* Clear the set of root segments. Wizards only. */
-extern void GC_clear_roots GC_PROTO((void));
+GC_API void GC_clear_roots GC_PROTO((void));
/* Add a root segment. Wizards only. */
-extern void GC_add_roots GC_PROTO((char * low_address,
+GC_API void GC_add_roots GC_PROTO((char * low_address,
char * high_address_plus_1));
/* Add a displacement to the set of those considered valid by the */
/* retention. */
/* This is a no-op if the collector was compiled with recognition of */
/* arbitrary interior pointers enabled, which is now the default. */
-void GC_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_register_displacement GC_PROTO((GC_word n));
/* The following version should be used if any debugging allocation is */
/* being done. */
-void GC_debug_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_debug_register_displacement GC_PROTO((GC_word n));
/* Explicitly trigger a full, world-stop collection. */
-void GC_gcollect GC_PROTO((void));
+GC_API void GC_gcollect GC_PROTO((void));
/* Trigger a full world-stopped collection. Abort the collection if */
/* and when stop_func returns a nonzero value. Stop_func will be */
/* aborted collections do no useful work; the next collection needs */
/* to start from the beginning. */
typedef int (* GC_stop_func) GC_PROTO((void));
-int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
+GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
/* Return the number of bytes in the heap. Excludes collector private */
/* data structures. Includes empty blocks and fragmentation loss. */
/* Includes some pages that were allocated but never written. */
-size_t GC_get_heap_size GC_PROTO((void));
+GC_API size_t GC_get_heap_size GC_PROTO((void));
/* Return the number of bytes allocated since the last collection. */
-size_t GC_get_bytes_since_gc GC_PROTO((void));
+GC_API size_t GC_get_bytes_since_gc GC_PROTO((void));
/* Enable incremental/generational collection. */
/* Not advisable unless dirty bits are */
/* pointerfree(atomic) or immutable. */
/* Don't use in leak finding mode. */
/* Ignored if GC_dont_gc is true. */
-void GC_enable_incremental GC_PROTO((void));
+GC_API void GC_enable_incremental GC_PROTO((void));
/* Perform some garbage collection work, if appropriate. */
/* Return 0 if there is no more work to be done. */
/* progress requires it, e.g. if incremental collection is */
/* disabled. It is reasonable to call this in a wait loop */
/* until it returns 0. */
-int GC_collect_a_little GC_PROTO((void));
+GC_API int GC_collect_a_little GC_PROTO((void));
/* Allocate an object of size lb bytes. The client guarantees that */
/* as long as the object is live, it will be referenced by a pointer */
/* for arrays likely to be larger than 100K or so. For other systems, */
/* or if the collector is not configured to recognize all interior */
/* pointers, the threshold is normally much higher. */
-extern GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb));
-extern GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb));
+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));
/* Debugging (annotated) allocation. GC_gcollect will check */
/* objects allocated in this way for overwrites, etc. */
-extern GC_PTR GC_debug_malloc
+GC_API GC_PTR GC_debug_malloc
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_atomic
+GC_API GC_PTR GC_debug_malloc_atomic
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_uncollectable
+GC_API GC_PTR GC_debug_malloc_uncollectable
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_stubborn
+GC_API GC_PTR GC_debug_malloc_stubborn
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern void GC_debug_free GC_PROTO((GC_PTR object_addr));
-extern GC_PTR GC_debug_realloc
+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));
-void GC_debug_change_stubborn GC_PROTO((GC_PTR));
-void GC_debug_end_stubborn_change GC_PROTO((GC_PTR));
+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__)
__LINE__)
# define GC_FREE(p) GC_debug_free(p)
# define GC_REGISTER_FINALIZER(p, f, d, of, od) \
- GC_register_finalizer(GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_register_finalizer_ignore_self( \
- GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p)
typedef void (*GC_finalization_proc)
GC_PROTO((GC_PTR obj, GC_PTR client_data));
-extern void GC_register_finalizer
+GC_API void GC_register_finalizer
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* When obj is no longer accessible, invoke */
/* pointers will not be finalized (or collected). */
/* Thus cycles involving finalizable objects should */
/* be avoided, or broken by disappearing links. */
- /* Fn should terminate as quickly as possible, and */
- /* defer extended computation. */
/* All but the last finalizer registered for an object */
/* is ignored. */
/* Finalization may be removed by passing 0 as fn. */
+ /* Finalizers are implicitly unregistered just before */
+ /* they are invoked. */
/* The old finalizer and client data are stored in */
/* *ofn and *ocd. */
/* Fn is never invoked on an accessible object, */
/* but it's unavoidable for C++, since the compiler may */
/* silently introduce these. It's also benign in that specific */
/* case. */
-extern void GC_register_finalizer_ignore_self
+GC_API void GC_register_finalizer_ignore_self
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer_ignore_self
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* where p is a pointer that is not followed by finalization */
/* code, and should not be considered in determining */
/* finalization order. */
-extern int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Link should point to a field of a heap allocated */
/* object obj. *link will be cleared when obj is */
/* found to be inaccessible. This happens BEFORE any */
/* otherwise. */
/* Only exists for backward compatibility. See below: */
-extern int GC_general_register_disappearing_link
+GC_API int GC_general_register_disappearing_link
GC_PROTO((GC_PTR * /* link */, GC_PTR obj));
/* A slight generalization of the above. *link is */
/* cleared when obj first becomes inaccessible. This */
/* the object containing link. Explicitly deallocating */
/* obj may or may not cause link to eventually be */
/* cleared. */
-extern int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Returns 0 if link was not actually registered. */
/* Undoes a registration by either of the above two */
/* routines. */
/* Auxiliary fns to make finalization work correctly with displaced */
/* pointers introduced by the debugging allocators. */
-extern GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data));
-extern void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data));
+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_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));
-extern GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
+GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
/* Returns old warning procedure. */
/* The following is intended to be used by a higher level */
# endif /* I_HIDE_POINTERS */
typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data));
-extern GC_PTR GC_call_with_alloc_lock
+GC_API GC_PTR GC_call_with_alloc_lock
GC_PROTO((GC_fn_type fn, GC_PTR client_data));
/* Check that p and q point to the same object. */
/* Returns the first argument. */
/* Succeeds if neither p nor q points to the heap. */
/* May succeed if both p and q point to between heap objects. */
-extern GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
+GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
/* Checked pointer pre- and post- increment operations. Note that */
/* the second argument is in units of bytes, not multiples of the */
/* object size. This should either be invoked from a macro, or the */
/* call should be automatically generated. */
-extern GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
-extern GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
/* Check that p is visible */
/* to the collector as a possibly pointer containing location. */
/* untyped allocations. The idea is that it should be possible, though */
/* slow, to add such a call to all indirect pointer stores.) */
/* Currently useless for multithreaded worlds. */
-extern GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
/* Check that if p is a pointer to a heap page, then it points to */
/* a valid displacement within a heap object. */
/* Fail conspicuously if this property does not hold. */
/* Uninteresting with ALL_INTERIOR_POINTERS. */
/* Always returns its argument. */
-extern GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
/* Safer, but slow, pointer addition. Probably useful mainly with */
/* a preprocessor. Useful only for heap pointers. */
# define GC_PTR_STORE(p, q) *((p) = (q))
#endif
+/* Fynctions called to report pointer checking errors */
+GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q));
+
+GC_API void (*GC_is_valid_displacement_print_proc)
+ GC_PROTO((GC_PTR p));
+
+GC_API void (*GC_is_visible_print_proc)
+ GC_PROTO((GC_PTR p));
#ifdef SOLARIS_THREADS
/* We need to intercept calls to many of the threads primitives, so */
# define thr_continue GC_thr_continue
# define dlopen GC_dlopen
+# endif /* SOLARIS_THREADS */
+
+#ifdef IRIX_THREADS
+/* We treat these similarly. */
+# include <pthread.h>
+# include <signal.h>
+
+ int GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
+ int GC_pthread_join(pthread_t thread, void **retval);
+
+# define pthread_create GC_pthread_create
+# define pthread_sigmask GC_pthread_sigmask
+# define pthread_join GC_pthread_join
+
+#endif /* IRIX_THREADS */
+
+#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
/* 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. */
#endif /* SOLARIS_THREADS */
-#ifdef DEC_PTHREADS
-# include <pthread.h>
- int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t attr,
- pthread_startroutine_t start_routine,
- pthread_addr_t arg);
- int GC_pt_pthread_detach(pthread_t *thread);
-# define pthread_create GC_pt_pthread_create
-# define pthread_detach GC_pt_pthread_detach
- void * GC_malloc_many(size_t lb);
-# define GC_NEXT(p) (*(void **)(p))
-#endif /* DEC_PTHREADS */
-
-#ifdef MIT_PTHREADS
-# include <pthread.h>
- int GC_pt_pthread_create(pthread_t *thread, pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg);
-# define pthread_create GC_pt_pthread_create
- void * GC_malloc_many(size_t lb);
-# define GC_NEXT(p) (*(void **)(p))
-#endif /* MIT_PTHREADS */
-
-#if (defined(MIT_PTHREADS) || defined(DEC_PTHREADS))
-#ifdef __hp9000s700
-# include <dl.h>
- shl_t GC_shl_load(const char *path, int flags, long address);
- int GC_shl_findsym(shl_t *handle, const char *sym, short type, void *value);
- int GC_shl_unload(shl_t handle);
-# define shl_load GC_shl_load
-# define shl_findsym GC_shl_findsym
-# define shl_unload GC_shl_unload
-#else
- void *GC_dlopen(const char *pathname, int mode);
- void *GC_dlsym(void *handle, const char *name);
- int GC_dlclose(void *handle);
- char *GC_dlerror(void);
-# define dlopen GC_dlopen
-# define dlsym GC_dlsym
-# define dlclose GC_dlclose
-# define dlerror GC_dlerror
-#endif
-#endif /* PTHREADS */
-
/*
* If you are planning on putting
* the collector in a SunOS 5 dynamic library, you need to call GC_INIT()
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=cord - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to cord - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "gc - Win32 Release" && "$(CFG)" != "gc - Win32 Debug" &&\
+ "$(CFG)" != "gctest - Win32 Release" && "$(CFG)" != "gctest - Win32 Debug" &&\
+ "$(CFG)" != "cord - Win32 Release" && "$(CFG)" != "cord - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "gc.mak" CFG="cord - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "gc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "gctest - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "gctest - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "cord - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "cord - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "gctest - Win32 Debug"
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : ".\Release\gc.dll" ".\Release\gc.bsc"
+
+CLEAN :
+ -@erase ".\Release\allchblk.obj"
+ -@erase ".\Release\allchblk.sbr"
+ -@erase ".\Release\alloc.obj"
+ -@erase ".\Release\alloc.sbr"
+ -@erase ".\Release\blacklst.obj"
+ -@erase ".\Release\blacklst.sbr"
+ -@erase ".\Release\checksums.obj"
+ -@erase ".\Release\checksums.sbr"
+ -@erase ".\Release\dbg_mlc.obj"
+ -@erase ".\Release\dbg_mlc.sbr"
+ -@erase ".\Release\dyn_load.obj"
+ -@erase ".\Release\dyn_load.sbr"
+ -@erase ".\Release\finalize.obj"
+ -@erase ".\Release\finalize.sbr"
+ -@erase ".\Release\gc.bsc"
+ -@erase ".\Release\gc.dll"
+ -@erase ".\Release\gc.exp"
+ -@erase ".\Release\gc.lib"
+ -@erase ".\Release\headers.obj"
+ -@erase ".\Release\headers.sbr"
+ -@erase ".\Release\mach_dep.obj"
+ -@erase ".\Release\mach_dep.sbr"
+ -@erase ".\Release\malloc.obj"
+ -@erase ".\Release\malloc.sbr"
+ -@erase ".\Release\mallocx.obj"
+ -@erase ".\Release\mallocx.sbr"
+ -@erase ".\Release\mark.obj"
+ -@erase ".\Release\mark.sbr"
+ -@erase ".\Release\mark_rts.obj"
+ -@erase ".\Release\mark_rts.sbr"
+ -@erase ".\Release\misc.obj"
+ -@erase ".\Release\misc.sbr"
+ -@erase ".\Release\new_hblk.obj"
+ -@erase ".\Release\new_hblk.sbr"
+ -@erase ".\Release\obj_map.obj"
+ -@erase ".\Release\obj_map.sbr"
+ -@erase ".\Release\os_dep.obj"
+ -@erase ".\Release\os_dep.sbr"
+ -@erase ".\Release\ptr_chck.obj"
+ -@erase ".\Release\ptr_chck.sbr"
+ -@erase ".\Release\reclaim.obj"
+ -@erase ".\Release\reclaim.sbr"
+ -@erase ".\Release\stubborn.obj"
+ -@erase ".\Release\stubborn.sbr"
+ -@erase ".\Release\typd_mlc.obj"
+ -@erase ".\Release\typd_mlc.sbr"
+ -@erase ".\Release\win32_threads.obj"
+ -@erase ".\Release\win32_threads.sbr"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "SILENT" /D "GC_BUILD" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "SILENT" /D "GC_BUILD" /D\
+ "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D\
+ "WIN32_THREADS" /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\Release/
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc"
+BSC32_SBRS= \
+ ".\Release\allchblk.sbr" \
+ ".\Release\alloc.sbr" \
+ ".\Release\blacklst.sbr" \
+ ".\Release\checksums.sbr" \
+ ".\Release\dbg_mlc.sbr" \
+ ".\Release\dyn_load.sbr" \
+ ".\Release\finalize.sbr" \
+ ".\Release\headers.sbr" \
+ ".\Release\mach_dep.sbr" \
+ ".\Release\malloc.sbr" \
+ ".\Release\mallocx.sbr" \
+ ".\Release\mark.sbr" \
+ ".\Release\mark_rts.sbr" \
+ ".\Release\misc.sbr" \
+ ".\Release\new_hblk.sbr" \
+ ".\Release\obj_map.sbr" \
+ ".\Release\os_dep.sbr" \
+ ".\Release\ptr_chck.sbr" \
+ ".\Release\reclaim.sbr" \
+ ".\Release\stubborn.sbr" \
+ ".\Release\typd_mlc.sbr" \
+ ".\Release\win32_threads.sbr"
+
+".\Release\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/gc.pdb" /machine:I386 /out:"$(OUTDIR)/gc.dll"\
+ /implib:"$(OUTDIR)/gc.lib"
+LINK32_OBJS= \
+ ".\Release\allchblk.obj" \
+ ".\Release\alloc.obj" \
+ ".\Release\blacklst.obj" \
+ ".\Release\checksums.obj" \
+ ".\Release\dbg_mlc.obj" \
+ ".\Release\dyn_load.obj" \
+ ".\Release\finalize.obj" \
+ ".\Release\headers.obj" \
+ ".\Release\mach_dep.obj" \
+ ".\Release\malloc.obj" \
+ ".\Release\mallocx.obj" \
+ ".\Release\mark.obj" \
+ ".\Release\mark_rts.obj" \
+ ".\Release\misc.obj" \
+ ".\Release\new_hblk.obj" \
+ ".\Release\obj_map.obj" \
+ ".\Release\os_dep.obj" \
+ ".\Release\ptr_chck.obj" \
+ ".\Release\reclaim.obj" \
+ ".\Release\stubborn.obj" \
+ ".\Release\typd_mlc.obj" \
+ ".\Release\win32_threads.obj"
+
+".\Release\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : ".\Debug\gc.dll" ".\Debug\gc.bsc"
+
+CLEAN :
+ -@erase ".\Debug\allchblk.obj"
+ -@erase ".\Debug\allchblk.sbr"
+ -@erase ".\Debug\alloc.obj"
+ -@erase ".\Debug\alloc.sbr"
+ -@erase ".\Debug\blacklst.obj"
+ -@erase ".\Debug\blacklst.sbr"
+ -@erase ".\Debug\checksums.obj"
+ -@erase ".\Debug\checksums.sbr"
+ -@erase ".\Debug\dbg_mlc.obj"
+ -@erase ".\Debug\dbg_mlc.sbr"
+ -@erase ".\Debug\dyn_load.obj"
+ -@erase ".\Debug\dyn_load.sbr"
+ -@erase ".\Debug\finalize.obj"
+ -@erase ".\Debug\finalize.sbr"
+ -@erase ".\Debug\gc.bsc"
+ -@erase ".\Debug\gc.dll"
+ -@erase ".\Debug\gc.exp"
+ -@erase ".\Debug\gc.lib"
+ -@erase ".\Debug\gc.map"
+ -@erase ".\Debug\gc.pdb"
+ -@erase ".\Debug\headers.obj"
+ -@erase ".\Debug\headers.sbr"
+ -@erase ".\Debug\mach_dep.obj"
+ -@erase ".\Debug\mach_dep.sbr"
+ -@erase ".\Debug\malloc.obj"
+ -@erase ".\Debug\malloc.sbr"
+ -@erase ".\Debug\mallocx.obj"
+ -@erase ".\Debug\mallocx.sbr"
+ -@erase ".\Debug\mark.obj"
+ -@erase ".\Debug\mark.sbr"
+ -@erase ".\Debug\mark_rts.obj"
+ -@erase ".\Debug\mark_rts.sbr"
+ -@erase ".\Debug\misc.obj"
+ -@erase ".\Debug\misc.sbr"
+ -@erase ".\Debug\new_hblk.obj"
+ -@erase ".\Debug\new_hblk.sbr"
+ -@erase ".\Debug\obj_map.obj"
+ -@erase ".\Debug\obj_map.sbr"
+ -@erase ".\Debug\os_dep.obj"
+ -@erase ".\Debug\os_dep.sbr"
+ -@erase ".\Debug\ptr_chck.obj"
+ -@erase ".\Debug\ptr_chck.sbr"
+ -@erase ".\Debug\reclaim.obj"
+ -@erase ".\Debug\reclaim.sbr"
+ -@erase ".\Debug\stubborn.obj"
+ -@erase ".\Debug\stubborn.sbr"
+ -@erase ".\Debug\typd_mlc.obj"
+ -@erase ".\Debug\typd_mlc.sbr"
+ -@erase ".\Debug\vc40.idb"
+ -@erase ".\Debug\vc40.pdb"
+ -@erase ".\Debug\win32_threads.obj"
+ -@erase ".\Debug\win32_threads.sbr"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "SILENT" /D "GC_BUILD" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "SILENT" /D "GC_BUILD"\
+ /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D\
+ "WIN32_THREADS" /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch" /YX /Fo"$(INTDIR)/"\
+ /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\Debug/
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc"
+BSC32_SBRS= \
+ ".\Debug\allchblk.sbr" \
+ ".\Debug\alloc.sbr" \
+ ".\Debug\blacklst.sbr" \
+ ".\Debug\checksums.sbr" \
+ ".\Debug\dbg_mlc.sbr" \
+ ".\Debug\dyn_load.sbr" \
+ ".\Debug\finalize.sbr" \
+ ".\Debug\headers.sbr" \
+ ".\Debug\mach_dep.sbr" \
+ ".\Debug\malloc.sbr" \
+ ".\Debug\mallocx.sbr" \
+ ".\Debug\mark.sbr" \
+ ".\Debug\mark_rts.sbr" \
+ ".\Debug\misc.sbr" \
+ ".\Debug\new_hblk.sbr" \
+ ".\Debug\obj_map.sbr" \
+ ".\Debug\os_dep.sbr" \
+ ".\Debug\ptr_chck.sbr" \
+ ".\Debug\reclaim.sbr" \
+ ".\Debug\stubborn.sbr" \
+ ".\Debug\typd_mlc.sbr" \
+ ".\Debug\win32_threads.sbr"
+
+".\Debug\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/gc.pdb" /map:"$(INTDIR)/gc.map" /debug /machine:I386\
+ /out:"$(OUTDIR)/gc.dll" /implib:"$(OUTDIR)/gc.lib"
+LINK32_OBJS= \
+ ".\Debug\allchblk.obj" \
+ ".\Debug\alloc.obj" \
+ ".\Debug\blacklst.obj" \
+ ".\Debug\checksums.obj" \
+ ".\Debug\dbg_mlc.obj" \
+ ".\Debug\dyn_load.obj" \
+ ".\Debug\finalize.obj" \
+ ".\Debug\headers.obj" \
+ ".\Debug\mach_dep.obj" \
+ ".\Debug\malloc.obj" \
+ ".\Debug\mallocx.obj" \
+ ".\Debug\mark.obj" \
+ ".\Debug\mark_rts.obj" \
+ ".\Debug\misc.obj" \
+ ".\Debug\new_hblk.obj" \
+ ".\Debug\obj_map.obj" \
+ ".\Debug\os_dep.obj" \
+ ".\Debug\ptr_chck.obj" \
+ ".\Debug\reclaim.obj" \
+ ".\Debug\stubborn.obj" \
+ ".\Debug\typd_mlc.obj" \
+ ".\Debug\win32_threads.obj"
+
+".\Debug\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "gctest - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "gctest\Release"
+# PROP BASE Intermediate_Dir "gctest\Release"
+# PROP BASE Target_Dir "gctest"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "gctest\Release"
+# PROP Intermediate_Dir "gctest\Release"
+# PROP Target_Dir "gctest"
+OUTDIR=.\gctest\Release
+INTDIR=.\gctest\Release
+
+ALL : "gc - Win32 Release" ".\Release\gctest.exe"
+
+CLEAN :
+ -@erase ".\gctest\Release\test.obj"
+ -@erase ".\Release\gctest.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D\
+ "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS"\
+ /Fp"$(INTDIR)/gctest.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\gctest\Release/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/gctest.exe"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /incremental:no\
+ /pdb:"$(OUTDIR)/gctest.pdb" /machine:I386 /out:"Release/gctest.exe"
+LINK32_OBJS= \
+ ".\gctest\Release\test.obj" \
+ ".\Release\gc.lib"
+
+".\Release\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "gctest - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "gctest\Debug"
+# PROP BASE Intermediate_Dir "gctest\Debug"
+# PROP BASE Target_Dir "gctest"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "gctest\Debug"
+# PROP Intermediate_Dir "gctest\Debug"
+# PROP Target_Dir "gctest"
+OUTDIR=.\gctest\Debug
+INTDIR=.\gctest\Debug
+
+ALL : "gc - Win32 Debug" ".\Debug\gctest.exe" ".\gctest\Debug\gctest.bsc"
+
+CLEAN :
+ -@erase ".\Debug\gctest.exe"
+ -@erase ".\gctest\Debug\gctest.bsc"
+ -@erase ".\gctest\Debug\gctest.map"
+ -@erase ".\gctest\Debug\gctest.pdb"
+ -@erase ".\gctest\Debug\test.obj"
+ -@erase ".\gctest\Debug\test.sbr"
+ -@erase ".\gctest\Debug\vc40.idb"
+ -@erase ".\gctest\Debug\vc40.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS"\
+ /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR"$(INTDIR)/"\
+ /Fp"$(INTDIR)/gctest.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\gctest\Debug/
+CPP_SBRS=.\gctest\Debug/
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc"
+BSC32_SBRS= \
+ ".\gctest\Debug\test.sbr"
+
+".\gctest\Debug\gctest.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /map /debug /machine:I386 /out:"Debug/gctest.exe"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /incremental:no\
+ /pdb:"$(OUTDIR)/gctest.pdb" /map:"$(INTDIR)/gctest.map" /debug /machine:I386\
+ /out:"Debug/gctest.exe"
+LINK32_OBJS= \
+ ".\Debug\gc.lib" \
+ ".\gctest\Debug\test.obj"
+
+".\Debug\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "cord - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "cord\Release"
+# PROP BASE Intermediate_Dir "cord\Release"
+# PROP BASE Target_Dir "cord"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "cord\Release"
+# PROP Intermediate_Dir "cord\Release"
+# PROP Target_Dir "cord"
+OUTDIR=.\cord\Release
+INTDIR=.\cord\Release
+
+ALL : "gc - Win32 Release" ".\Release\de.exe"
+
+CLEAN :
+ -@erase ".\cord\Release\cordbscs.obj"
+ -@erase ".\cord\Release\cordxtra.obj"
+ -@erase ".\cord\Release\de.obj"
+ -@erase ".\cord\Release\de_win.obj"
+ -@erase ".\cord\Release\de_win.res"
+ -@erase ".\Release\de.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D\
+ "ALL_INTERIOR_POINTERS" /Fp"$(INTDIR)/cord.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\cord\Release/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/de.exe"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /incremental:no /pdb:"$(OUTDIR)/de.pdb"\
+ /machine:I386 /out:"Release/de.exe"
+LINK32_OBJS= \
+ ".\cord\Release\cordbscs.obj" \
+ ".\cord\Release\cordxtra.obj" \
+ ".\cord\Release\de.obj" \
+ ".\cord\Release\de_win.obj" \
+ ".\cord\Release\de_win.res" \
+ ".\Release\gc.lib"
+
+".\Release\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "cord\Debug"
+# PROP BASE Intermediate_Dir "cord\Debug"
+# PROP BASE Target_Dir "cord"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "cord\Debug"
+# PROP Intermediate_Dir "cord\Debug"
+# PROP Target_Dir "cord"
+OUTDIR=.\cord\Debug
+INTDIR=.\cord\Debug
+
+ALL : "gc - Win32 Debug" ".\Debug\de.exe"
+
+CLEAN :
+ -@erase ".\cord\Debug\cordbscs.obj"
+ -@erase ".\cord\Debug\cordxtra.obj"
+ -@erase ".\cord\Debug\de.obj"
+ -@erase ".\cord\Debug\de.pdb"
+ -@erase ".\cord\Debug\de_win.obj"
+ -@erase ".\cord\Debug\de_win.res"
+ -@erase ".\cord\Debug\vc40.idb"
+ -@erase ".\cord\Debug\vc40.pdb"
+ -@erase ".\Debug\de.exe"
+ -@erase ".\Debug\de.ilk"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "." /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /I "." /D "_DEBUG" /D "WIN32" /D\
+ "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /Fp"$(INTDIR)/cord.pch" /YX\
+ /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\cord\Debug/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/de.exe"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:windows /incremental:yes\
+ /pdb:"$(OUTDIR)/de.pdb" /debug /machine:I386 /out:"Debug/de.exe"
+LINK32_OBJS= \
+ ".\cord\Debug\cordbscs.obj" \
+ ".\cord\Debug\cordxtra.obj" \
+ ".\cord\Debug\de.obj" \
+ ".\cord\Debug\de_win.obj" \
+ ".\cord\Debug\de_win.res" \
+ ".\Debug\gc.lib"
+
+".\Debug\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+################################################################################
+# Begin Target
+
+# Name "gc - Win32 Release"
+# Name "gc - Win32 Debug"
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\reclaim.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_RECLA=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_RECLA=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)"
+
+".\Release\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_RECLA=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_RECLA=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)"
+
+".\Debug\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\os_dep.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_OS_DE=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_OS_DE=\
+ ".\il\PCR_IL.h"\
+ ".\mm\PCR_MM.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+ ".\vd\PCR_VD.h"\
+
+
+".\Release\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)"
+
+".\Release\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_OS_DE=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_OS_DE=\
+ ".\il\PCR_IL.h"\
+ ".\mm\PCR_MM.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+ ".\vd\PCR_VD.h"\
+
+
+".\Debug\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)"
+
+".\Debug\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\misc.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MISC_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MISC_=\
+ ".\il\PCR_IL.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)"
+
+".\Release\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MISC_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MISC_=\
+ ".\il\PCR_IL.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)"
+
+".\Debug\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mark_rts.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MARK_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MARK_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)"
+
+".\Release\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MARK_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MARK_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)"
+
+".\Debug\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mach_dep.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MACH_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MACH_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)"
+
+".\Release\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MACH_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MACH_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)"
+
+".\Debug\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\headers.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_HEADE=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_HEADE=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)"
+
+".\Release\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_HEADE=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_HEADE=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)"
+
+".\Debug\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\alloc.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_ALLOC=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_ALLOC=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)"
+
+".\Release\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_ALLOC=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_ALLOC=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)"
+
+".\Debug\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\allchblk.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_ALLCH=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_ALLCH=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)"
+
+".\Release\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_ALLCH=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_ALLCH=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)"
+
+".\Debug\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\stubborn.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_STUBB=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_STUBB=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\stubborn.obj" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)"
+
+".\Release\stubborn.sbr" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_STUBB=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_STUBB=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\stubborn.obj" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)"
+
+".\Debug\stubborn.sbr" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\obj_map.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_OBJ_M=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_OBJ_M=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)"
+
+".\Release\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_OBJ_M=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_OBJ_M=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)"
+
+".\Debug\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\new_hblk.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_NEW_H=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_NEW_H=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)"
+
+".\Release\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_NEW_H=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_NEW_H=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)"
+
+".\Debug\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mark.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MARK_C=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MARK_C=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)"
+
+".\Release\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MARK_C=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MARK_C=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)"
+
+".\Debug\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\malloc.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MALLO=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MALLO=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)"
+
+".\Release\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MALLO=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MALLO=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)"
+
+".\Debug\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mallocx.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_MALLX=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MALLX=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)"
+
+".\Release\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_MALLX=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_MALLX=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)"
+
+".\Debug\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\finalize.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_FINAL=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_FINAL=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)"
+
+".\Release\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_FINAL=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_FINAL=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)"
+
+".\Debug\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\dbg_mlc.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_DBG_M=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_DBG_M=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)"
+
+".\Release\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_DBG_M=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_DBG_M=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)"
+
+".\Debug\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\blacklst.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_BLACK=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_BLACK=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)"
+
+".\Release\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_BLACK=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_BLACK=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)"
+
+".\Debug\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\typd_mlc.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_TYPD_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ ".\gc_typed.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_TYPD_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)"
+
+".\Release\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_TYPD_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ ".\gc_typed.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_TYPD_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)"
+
+".\Debug\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\ptr_chck.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_PTR_C=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_PTR_C=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)"
+
+".\Release\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_PTR_C=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_mark.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_PTR_C=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)"
+
+".\Debug\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\dyn_load.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_DYN_L=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_DYN_L=\
+ ".\il\PCR_IL.h"\
+ ".\mm\PCR_MM.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)"
+
+".\Release\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_DYN_L=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\STAT.H"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_DYN_L=\
+ ".\il\PCR_IL.h"\
+ ".\mm\PCR_MM.h"\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)"
+
+".\Debug\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win32_threads.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_WIN32=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_WIN32=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)"
+
+".\Release\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_WIN32=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_WIN32=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)"
+
+".\Debug\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\checksums.c
+
+!IF "$(CFG)" == "gc - Win32 Release"
+
+DEP_CPP_CHECK=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_CHECK=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Release\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)"
+
+".\Release\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gc - Win32 Debug"
+
+DEP_CPP_CHECK=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_CHECK=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+".\Debug\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)"
+
+".\Debug\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "gctest - Win32 Release"
+# Name "gctest - Win32 Debug"
+
+!IF "$(CFG)" == "gctest - Win32 Release"
+
+!ELSEIF "$(CFG)" == "gctest - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Project Dependency
+
+# Project_Dep_Name "gc"
+
+!IF "$(CFG)" == "gctest - Win32 Release"
+
+"gc - Win32 Release" :
+ $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release"
+
+!ELSEIF "$(CFG)" == "gctest - Win32 Debug"
+
+"gc - Win32 Debug" :
+ $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug"
+
+!ENDIF
+
+# End Project Dependency
+################################################################################
+# Begin Source File
+
+SOURCE=.\test.c
+DEP_CPP_TEST_=\
+ ".\config.h"\
+ ".\gc.h"\
+ ".\gc_hdrs.h"\
+ ".\gc_priv.h"\
+ ".\gc_typed.h"\
+ {$(INCLUDE)}"\sys\TYPES.H"\
+
+NODEP_CPP_TEST_=\
+ ".\th\PCR_Th.h"\
+ ".\th\PCR_ThCrSec.h"\
+ ".\th\PCR_ThCtl.h"\
+
+
+!IF "$(CFG)" == "gctest - Win32 Release"
+
+
+".\gctest\Release\test.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "gctest - Win32 Debug"
+
+
+".\gctest\Debug\test.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)"
+
+".\gctest\Debug\test.sbr" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "cord - Win32 Release"
+# Name "cord - Win32 Debug"
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Project Dependency
+
+# Project_Dep_Name "gc"
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+"gc - Win32 Release" :
+ $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release"
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+"gc - Win32 Debug" :
+ $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug"
+
+!ENDIF
+
+# End Project Dependency
+################################################################################
+# Begin Source File
+
+SOURCE=.\cord\de_win.c
+DEP_CPP_DE_WI=\
+ ".\cord\cord.h"\
+ ".\cord\de_cmds.h"\
+ ".\cord\de_win.h"\
+ ".\cord\private\cord_pos.h"\
+
+NODEP_CPP_DE_WI=\
+ ".\cord\gc.h"\
+
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+
+".\cord\Release\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+
+".\cord\Debug\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cord\de.c
+DEP_CPP_DE_C2e=\
+ ".\cord\cord.h"\
+ ".\cord\de_cmds.h"\
+ ".\cord\de_win.h"\
+ ".\cord\private\cord_pos.h"\
+
+NODEP_CPP_DE_C2e=\
+ ".\cord\gc.h"\
+
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+
+".\cord\Release\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+
+".\cord\Debug\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cord\cordxtra.c
+DEP_CPP_CORDX=\
+ ".\cord\cord.h"\
+ ".\cord\ec.h"\
+ ".\cord\private\cord_pos.h"\
+
+NODEP_CPP_CORDX=\
+ ".\cord\gc.h"\
+
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+
+".\cord\Release\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+
+".\cord\Debug\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cord\cordbscs.c
+DEP_CPP_CORDB=\
+ ".\cord\cord.h"\
+ ".\cord\private\cord_pos.h"\
+
+NODEP_CPP_CORDB=\
+ ".\cord\gc.h"\
+
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+
+".\cord\Release\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+
+".\cord\Debug\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cord\de_win.RC
+
+!IF "$(CFG)" == "cord - Win32 Release"
+
+
+".\cord\Release\de_win.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "NDEBUG" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "cord - Win32 Debug"
+
+
+".\cord\Debug\de_win.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "_DEBUG" $(SOURCE)
+
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
-.TH GC_MALLOC 1L "20 April 1994"
+.TH GC_MALLOC 1L "12 February 1996"
.SH NAME
GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement
.SH SYNOPSIS
--- /dev/null
+/*
+ * 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.
+ *
+ * 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 C++ header file that is intended to replace the SGI STL
+// alloc.h.
+//
+// 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 soes not work
+// with g++ 2.7.2 and earlier.
+//
+
+#ifndef GC_ALLOC_H
+
+#define GC_ALLOC_H
+#define __ALLOC_H // Prevent inclusion of the default version. Ugly.
+
+#ifndef __ALLOC
+# define __ALLOC alloc
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+// The following is just replicated from the conventional SGI alloc.h:
+
+template<class T, class alloc>
+class simple_alloc {
+
+public:
+ static T *allocate(size_t n)
+ { return 0 == n? 0 : (T*) alloc::allocate(n * sizeof (T)); }
+ static T *allocate(void)
+ { return (T*) alloc::allocate(sizeof (T)); }
+ static void deallocate(T *p, size_t n)
+ { if (0 != n) alloc::deallocate(p, n * sizeof (T)); }
+ static void deallocate(T *p)
+ { alloc::deallocate(p, sizeof (T)); }
+};
+
+#include "gc.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);
+
+ extern void * GC_malloc_atomic_uncollectable(size_t bytes);
+}
+
+// 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 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 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_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_alloc_template<0> single_client_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 *p, size_t n) { }
+ static void ptr_free_deallocate(void *p, size_t n) { }
+};
+
+typedef gc_alloc_template < 0 > gc_alloc;
+
+template < int dummy >
+class 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 n) { GC_free(p); }
+ static void ptr_free_deallocate(void *p, size_t n) { GC_free(p); }
+};
+
+typedef alloc_template < 0 > 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, alloc)
+__GC_SPECIALIZE(int, alloc)
+__GC_SPECIALIZE(unsigned, alloc)
+__GC_SPECIALIZE(float, alloc)
+__GC_SPECIALIZE(double, 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_alloc)
+__GC_SPECIALIZE(int, single_client_alloc)
+__GC_SPECIALIZE(unsigned, single_client_alloc)
+__GC_SPECIALIZE(float, single_client_alloc)
+__GC_SPECIALIZE(double, single_client_alloc)
+
+#endif _SGI_SOURCE
+
+#endif /* GC_ALLOC_H */
mark_stack_top, mark_stack_limit) \
}
+
/*
* Push a single value onto mark stack. Mark from the object pointed to by p.
* GC_push_one is normally called by GC_push_regs, and thus must be defined.
GC_push_one_checked(p,AIP); \
}
-
+/*
+ * Mark from one finalizable object using the specified
+ * mark proc. May not mark the object pointed to by
+ * real_ptr. That is the job of the caller, if appropriate
+ */
+# define GC_MARK_FO(real_ptr, mark_proc) \
+{ \
+ (*(mark_proc))(real_ptr); \
+ while (!GC_mark_stack_empty()) GC_mark_from_mark_stack(); \
+ if (GC_mark_state != MS_NONE) { \
+ GC_set_mark_bit(real_ptr); \
+ while (!GC_mark_some()); \
+ } \
+}
extern bool GC_mark_stack_too_small;
/* We need a larger mark stack. May be */
extern mark_state_t GC_mark_state;
#endif /* GC_MARK_H */
+
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, February 9, 1996 11:41 am PST */
+/* Boehm, February 16, 1996 2:30 pm PST */
# ifndef GC_PRIVATE_H
# include "gc_hdrs.h"
# endif
-# if !defined(bool)
+# if !defined(bool) && !defined(__cplusplus)
typedef int bool;
- /* This is problematic with C++ implementations that define bool. */
- /* But those usually treat it correctly as an empty declaration. */
+ /* 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(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1020
+ /* Visual C++ 4.2 does not have bool type. */
+ typedef int bool;
+# endif
# endif
# define TRUE 1
# define FALSE 0
# include <stddef.h>
# endif
# define VOLATILE volatile
+# define CONST const
#else
# ifdef MSWIN32
# include <stdlib.h>
# endif
# define VOLATILE
+# define CONST
#endif
#ifdef AMIGA
# define GATHERSTATS
#endif
-# if defined(SOLARIS_THREADS) && !defined(SUNOS5) \
- || (defined(DEC_PTHREADS) && !defined(ALPHA))
---> inconsistent configuration
-# endif
-# if defined(PCR) || defined(SRC_M3) || defined(SOLARIS_THREADS) \
- || defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-# define THREADS
-# endif
-
-#if defined(SPARC)
-# define ALIGN_DOUBLE /* Align objects of size > 1 word on 2 word */
- /* boundaries. Wasteful of memory, but */
- /* apparently required by SPARC architecture. */
-# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */
- /* include assembly code to do it well. */
-#endif
-
-#ifdef HP_PA
-# define ALIGN_DOUBLE
-#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 */
# define LOCK() mutex_lock(&GC_allocate_ml);
# define UNLOCK() mutex_unlock(&GC_allocate_ml);
# endif
-# ifdef DEC_PTHREADS
- extern pthread_mutex_t GC_allocate_ml;
- extern int GC_pt_init_ok;
-# define LOCK() {if (!GC_pt_init_ok) GC_init(); pthread_mutex_lock(&GC_allocate_ml);}
-# define UNLOCK() if (GC_pt_init_ok) pthread_mutex_unlock(&GC_allocate_ml)
+# ifdef IRIX_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)
+# endif
+ extern unsigned long GC_allocate_lock;
+ /* This is not a mutex because mutexes that obey the (optional) */
+ /* POSIX scheduling rules are subject to convoys in high contention */
+ /* applications. This is basically a spin lock. */
+ extern pthread_t GC_lock_holder;
+ extern void GC_lock(void);
+ /* Allocation lock holder. Only set if acquired by client through */
+ /* GC_call_with_alloc_lock. */
+# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
+# define NO_THREAD (pthread_t)(-1)
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
+# ifdef UNDEFINED
+# define LOCK() pthread_mutex_lock(&GC_allocate_ml)
+# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+# else
+# define LOCK() { if (__test_and_set(&GC_allocate_lock, 1)) GC_lock(); }
+# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64))
+# define UNLOCK() __lock_release(lock)
+# else
+# define UNLOCK() GC_allocate_lock = 0
+# endif
+# endif
+ extern bool GC_collecting;
+# define ENTER_GC() \
+ { \
+ GC_collecting = 1; \
+ }
+# define EXIT_GC() GC_collecting = 0;
+# endif
+# ifdef WIN32_THREADS
+# include <windows.h>
+ GC_API CRITICAL_SECTION GC_allocate_ml;
+# define LOCK() EnterCriticalSection(&GC_allocate_ml);
+# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
# endif
-# ifdef MIT_PTHREADS
- extern pthread_mutex_t GC_allocate_ml;
- extern int GC_pt_init_ok;
-# define LOCK() if (GC_pt_init_ok) pthread_mutex_lock(&GC_allocate_ml)
-# define UNLOCK() if (GC_pt_init_ok) pthread_mutex_unlock(&GC_allocate_ml)
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
# endif
# else
# define LOCK()
# define UNLOCK()
# endif
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
+# endif
+# ifndef ENTER_GC
+# define ENTER_GC()
+# define EXIT_GC()
+# endif
# ifndef DCL_LOCK_STATE
# define DCL_LOCK_STATE
# else
# if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \
|| defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \
- || defined(NO_SIGNALS) \
- || defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
+ || defined(NO_SIGNALS) || defined(IRIX_THREADS)
/* Also useful for debugging. */
/* Should probably use thr_sigsetmask for SOLARIS_THREADS. */
# define DISABLE_SIGNALS()
PCR_allSigsBlocked, \
PCR_waitForever);
# else
-# ifdef SOLARIS_THREADS
+# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS)
+ void GC_stop_world();
+ void GC_start_world();
# define STOP_WORLD() GC_stop_world()
# define START_WORLD() GC_start_world()
# else
-# ifdef DEC_PTHREADS
-# define STOP_WORLD() GC_pt_stop_world()
-# define START_WORLD() GC_pt_start_world()
-# else
-# ifdef MIT_PTHREADS
-# define STOP_WORLD() GC_pt_stop_world()
-# define START_WORLD() GC_pt_start_world()
-# else
-# define STOP_WORLD()
-# define START_WORLD()
-# endif
-# endif
+# define STOP_WORLD()
+# define START_WORLD()
# endif
# endif
# ifdef SMALL_CONFIG
# define ABORT(msg) abort();
# else
- void GC_abort();
+ GC_API void GC_abort();
# define ABORT(msg) GC_abort(msg);
# endif
# endif
ptr_t _uobjfreelist[MAXOBJSZ+1];
/* uncollectable but traced objs */
+ /* objects on this and auobjfreelist */
+ /* are always marked, except during */
+ /* garbage collections. */
+# ifdef ATOMIC_UNCOLLECTABLE
+ ptr_t _auobjfreelist[MAXOBJSZ+1];
+# endif
+ /* uncollectable but traced objs */
# ifdef GATHERSTATS
word _composite_in_use;
#endif
};
-extern GC_FAR struct _GC_arrays GC_arrays;
+GC_API GC_FAR struct _GC_arrays GC_arrays;
# define GC_objfreelist GC_arrays._objfreelist
# define GC_aobjfreelist GC_arrays._aobjfreelist
# define GC_uobjfreelist GC_arrays._uobjfreelist
+# ifdef ATOMIC_UNCOLLECTABLE
+# define GC_auobjfreelist GC_arrays._auobjfreelist
+# endif
# define GC_sobjfreelist GC_arrays._sobjfreelist
# define GC_valid_offsets GC_arrays._valid_offsets
# define GC_modws_valid_offsets GC_arrays._modws_valid_offsets
# define beginGC_arrays ((ptr_t)(&GC_arrays))
# define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays))
+GC_API word GC_fo_entries;
# define MAXOBJKINDS 16
# define PTRFREE 0
# define NORMAL 1
# define UNCOLLECTABLE 2
-# define STUBBORN 3
+# ifdef ATOMIC_UNCOLLECTABLE
+# define AUNCOLLECTABLE 3
+# define STUBBORN 4
+# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE)
+# else
+# define STUBBORN 3
+# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE)
+# endif
extern int GC_n_kinds;
/* work of whatever kind is needed. Returns */
/* quickly if no collection is in progress. */
/* Return TRUE if mark phase finished. */
-void GC_initiate_full(); /* initiate full collection. */
-void GC_initiate_partial(); /* initiate partial collection. */
+void GC_initiate_gc(); /* initiate collection. */
+ /* If the mark state is invalid, this */
+ /* becomes full colleection. Otherwise */
+ /* it's partial. */
void GC_push_all(/*b,t*/); /* Push everything in a range */
/* onto mark stack. */
void GC_push_dirty(/*b,t*/); /* Push all possibly changed */
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 */);
/* Is the address p in one of the registered static */
/* to the collector. */
ptr_t GC_generic_malloc_inner(/* bytes, kind */);
/* Ditto, but I already hold lock, etc. */
-ptr_t GC_generic_malloc_words_small(/*words, kind*/);
+ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind));
/* As above, but size in units of words */
/* Bypasses MERGE_SIZES. Assumes */
/* words <= MAXOBJSZ. */
void GC_dump();
/* Make arguments appear live to compiler */
-void GC_noop();
+GC_API void GC_noop();
+void GC_noop1(/* word arg */);
/* Logging and diagnostic output: */
-void GC_printf GC_PROTO((char * format, long, long, long, long, long, long));
+GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long));
/* A version of printf that doesn't allocate, */
/* is restricted to long arguments, and */
/* (unfortunately) doesn't use varargs for */
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright 1996 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.
* or if conservative collector leakage is otherwise a problem (unlikely).
* Note that this is implemented completely separately from the rest
* of the collector, and is not linked in unless referenced.
+ * This does not currently support GC_DEBUG in any interesting way.
*/
/* Boehm, May 19, 1994 2:13 pm PDT */
typedef GC_word GC_descr;
-#if defined(__STDC__) || defined(__cplusplus)
- extern GC_descr GC_make_descriptor(GC_bitmap bm, size_t len);
-#else
- extern GC_descr GC_make_descriptor(/* GC_bitmap bm, size_t len */);
-#endif
+GC_API GC_descr GC_make_descriptor GC_PROTO((GC_bitmap bm, size_t len));
/* Return a type descriptor for the object whose layout */
/* is described by the argument. */
/* The least significant bit of the first word is one */
/* is intended to be called once per type, not once */
/* per allocation. */
-#if defined(__STDC__) || defined(__cplusplus)
- extern void * GC_malloc_explicitly_typed(size_t size_in_bytes, GC_descr d);
-#else
- extern char * GC_malloc_explicitly_typed(/* size_in_bytes, descriptor */);
-#endif
+GC_API void * GC_malloc_explicitly_typed
+ GC_PROTO((size_t size_in_bytes, GC_descr d));
/* Allocate an object whose layout is described by d. */
/* The resulting object MAY NOT BE PASSED TO REALLOC. */
-#if defined(__STDC__) || defined(__cplusplus)
- extern void * GC_calloc_explicitly_typed(size_t nelements,
- size_t element_size_in_bytes,
- GC_descr d);
-#else
- char * GC_calloc_explicitly_typed(/* nelements, size_in_bytes, descriptor */);
+GC_API void * GC_calloc_explicitly_typed
+ GC_PROTO((size_t nelements,
+ size_t element_size_in_bytes,
+ GC_descr d));
/* Allocate an array of nelements elements, each of the */
/* given size, and with the given descriptor. */
/* The elemnt size must be a multiple of the byte */
/* alignment required for pointers. E.g. on a 32-bit */
/* machine with 16-bit aligned pointers, size_in_bytes */
/* must be a multiple of 2. */
-#endif
+
+#ifdef GC_DEBUG
+# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes)
+# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) GC_MALLOC(n*bytes)
+#else
+# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) \
+ GC_malloc_explicitly_typed(bytes, d)
+# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) \
+ GC_calloc_explicitly_typed(n, bytes, d)
+#endif /* !GC_DEBUG */
+
#endif /* _GC_TYPED_H */
/*
* 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, October 7, 1994 9:54 pm PDT */
/*
* This implements:
static ptr_t scratch_free_ptr = 0;
ptr_t GC_scratch_end_ptr = 0;
+
+ptr_t GC_scratch_last_end_ptr = 0;
+ /* End point of last obtained scratch area */
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))
+# else
+# define GRANULARITY sizeof(word)
+# endif
+ bytes += GRANULARITY-1;
+ bytes &= ~(GRANULARITY-1);
scratch_free_ptr += bytes;
if (scratch_free_ptr <= GC_scratch_end_ptr) {
return(result);
if (bytes_to_get <= bytes) {
/* Undo the damage, and get memory directly */
+ ptr_t result = (ptr_t)GET_MEM(bytes);
scratch_free_ptr -= bytes;
- return((ptr_t)GET_MEM(bytes));
+ GC_scratch_last_end_ptr = result + bytes;
+ return(result);
}
result = (ptr_t)GET_MEM(bytes_to_get);
if (result == 0) {
}
scratch_free_ptr = result;
GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get;
+ GC_scratch_last_end_ptr = GC_scratch_end_ptr;
return(GC_scratch_alloc(bytes));
}
}
register bottom_index * p;
register bottom_index ** prev;
# ifdef HASH_TL
- register i = TL_HASH(hi);
+ register unsigned i = TL_HASH(hi);
register bottom_index * old;
old = p = GC_top_index[i];
return(0);
}
execvp(argv[2], argv+2);
+ exit(1);
Usage:
fprintf(stderr, "Usage: %s file_name command\n", argv[0]);
*
* Author: Hans-J. Boehm (boehm@parc.xerox.com)
*/
-/* Boehm, October 4, 1994 5:34 pm PDT */
+/* Boehm, October 5, 1995 4:20 pm PDT */
/*
* Cords are immutable character strings. A number of operations
void CORD_dump(CORD x);
/* The following could easily be implemented by the client. They are */
-/* provided in cord_xtra.c for convenience. */
+/* provided in cordxtra.c for convenience. */
/* Concatenate a character to the end of a cord. */
CORD CORD_cat_char(CORD x, char c);
/* x, and is thus modifiable. */
char * CORD_to_char_star(CORD x);
+/* Turn a C string into a CORD. The C string is copied, and so may */
+/* subsequently be modified. */
+CORD CORD_from_char_star(const char *s);
+
/* Identical to the above, but the result may share structure with */
/* the argument and is thus not modifiable. */
const char * CORD_to_const_char_star(CORD x);
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
- * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
+ * Copyright 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, October 9, 1995 1:14 pm PDT */
/*
* Note that this defines a large number of tuning hooks, which can
#ifndef _GC_H
# define _GC_H
+# define __GC
+# include <stddef.h>
+
+
+#if defined(_MSC_VER) && defined(_DLL)
+#ifdef GC_BUILD
+#define GC_API __declspec(dllexport)
+#else
+#define GC_API __declspec(dllimport)
+#endif
+#endif
+
+#ifndef GC_API
+#define GC_API extern
+#endif
# if defined(__STDC__) || defined(__cplusplus)
# define GC_PROTO(args) args
extern "C" {
# endif
-# include <stddef.h>
/* Define word and signed_word to be unsigned and signed types of the */
/* size as char * or void *. There seems to be no way to do this */
/* Public read-only variables */
-extern GC_word GC_gc_no;/* Counter incremented per collection. */
+GC_API GC_word GC_gc_no;/* Counter incremented per collection. */
/* Includes empty GCs at startup. */
/* Public R/W variables */
-extern int GC_quiet; /* Disable statistics output. Only matters if */
+GC_API void * (*GC_oom_fn) GC_PROTO((size_t bytes_requested));
+ /* When there is insufficient memory to satisfy */
+ /* an allocation request, we return */
+ /* (*GC_oom_fn)(). By default this just */
+ /* returns 0. */
+ /* If it returns, it must return 0 or a valid */
+ /* pointer to a previously allocated heap */
+ /* object. */
+
+GC_API int GC_quiet; /* Disable statistics output. Only matters if */
/* collector has been compiled with statistics */
/* enabled. This involves a performance cost, */
/* and is thus not the default. */
-extern int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
- /* beacuse it's not safe. */
+GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */
+ /* because it's not safe. */
-extern int GC_dont_expand;
+GC_API int GC_dont_expand;
/* Dont expand heap unless explicitly requested */
/* or forced to. */
-extern int GC_full_freq; /* Number of partial collections between */
+GC_API int GC_full_freq; /* Number of partial collections between */
/* full collections. Matters only if */
/* GC_incremental is set. */
-extern GC_word GC_non_gc_bytes;
+GC_API GC_word GC_non_gc_bytes;
/* Bytes not considered candidates for collection. */
/* Used only to control scheduling of collections. */
-extern GC_word GC_free_space_divisor;
+GC_API GC_word GC_free_space_divisor;
/* We try to make sure that we allocate at */
/* least N/GC_free_space_divisor bytes between */
/* collections, where N is the heap size plus */
/* at the expense of space. */
/* GC_free_space_divisor = 1 will effectively */
/* disable collections. */
+
+GC_API GC_word GC_max_retries;
+ /* The maximum number of GCs attempted before */
+ /* reporting out of memory after heap */
+ /* expansion fails. Initially 0. */
/* Public procedures */
* collectable. GC_malloc_uncollectable and GC_free called on the resulting
* object implicitly update GC_non_gc_bytes appropriately.
*/
-extern GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
-extern GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes));
+GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes));
+
+/* The following is only defined if the library has been suitably */
+/* compiled: */
+GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes));
/* Explicitly deallocate an object. Dangerous if used incorrectly. */
/* Requires a pointer to the base of an object. */
/* An object should not be enable for finalization when it is */
/* explicitly deallocated. */
/* GC_free(0) is a no-op, as required by ANSI C for free. */
-extern void GC_free GC_PROTO((GC_PTR object_addr));
+GC_API void GC_free GC_PROTO((GC_PTR object_addr));
/*
* Stubborn objects may be changed only if the collector is explicitly informed.
* do so. The same applies to dropping stubborn objects that are still
* changeable.
*/
-extern void GC_change_stubborn GC_PROTO((GC_PTR));
-extern void GC_end_stubborn_change GC_PROTO((GC_PTR));
+GC_API void GC_change_stubborn GC_PROTO((GC_PTR));
+GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR));
/* Return a pointer to the base (lowest address) of an object given */
/* a pointer to a location within the object. */
/* Return 0 if displaced_pointer doesn't point to within a valid */
/* object. */
-extern GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
+GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer));
/* Given a pointer to the base of an object, return its size in bytes. */
/* The returned size may be slightly larger than what was originally */
/* requested. */
-extern size_t GC_size GC_PROTO((GC_PTR object_addr));
+GC_API size_t GC_size GC_PROTO((GC_PTR object_addr));
/* For compatibility with C library. This is occasionally faster than */
/* a malloc followed by a bcopy. But if you rely on that, either here */
/* 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. */
-extern GC_PTR GC_realloc GC_PROTO((GC_PTR old_object,
+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. */
-extern int GC_expand_hp GC_PROTO((size_t number_of_bytes));
+GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes));
/* Limit the heap size to n bytes. Useful when you're debugging, */
/* especially on systems that don't handle running out of memory well. */
/* n == 0 ==> unbounded. This is the default. */
-extern void GC_set_max_heap_size GC_PROTO((GC_word n));
+GC_API void GC_set_max_heap_size GC_PROTO((GC_word n));
/* Clear the set of root segments. Wizards only. */
-extern void GC_clear_roots GC_PROTO((void));
+GC_API void GC_clear_roots GC_PROTO((void));
/* Add a root segment. Wizards only. */
-extern void GC_add_roots GC_PROTO((char * low_address,
+GC_API void GC_add_roots GC_PROTO((char * low_address,
char * high_address_plus_1));
/* Add a displacement to the set of those considered valid by the */
/* retention. */
/* This is a no-op if the collector was compiled with recognition of */
/* arbitrary interior pointers enabled, which is now the default. */
-void GC_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_register_displacement GC_PROTO((GC_word n));
/* The following version should be used if any debugging allocation is */
/* being done. */
-void GC_debug_register_displacement GC_PROTO((GC_word n));
+GC_API void GC_debug_register_displacement GC_PROTO((GC_word n));
/* Explicitly trigger a full, world-stop collection. */
-void GC_gcollect GC_PROTO((void));
+GC_API void GC_gcollect GC_PROTO((void));
/* Trigger a full world-stopped collection. Abort the collection if */
/* and when stop_func returns a nonzero value. Stop_func will be */
/* aborted collections do no useful work; the next collection needs */
/* to start from the beginning. */
typedef int (* GC_stop_func) GC_PROTO((void));
-int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
+GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func));
/* Return the number of bytes in the heap. Excludes collector private */
/* data structures. Includes empty blocks and fragmentation loss. */
/* Includes some pages that were allocated but never written. */
-size_t GC_get_heap_size GC_PROTO((void));
+GC_API size_t GC_get_heap_size GC_PROTO((void));
/* Return the number of bytes allocated since the last collection. */
-size_t GC_get_bytes_since_gc GC_PROTO((void));
+GC_API size_t GC_get_bytes_since_gc GC_PROTO((void));
/* Enable incremental/generational collection. */
/* Not advisable unless dirty bits are */
/* pointerfree(atomic) or immutable. */
/* Don't use in leak finding mode. */
/* Ignored if GC_dont_gc is true. */
-void GC_enable_incremental GC_PROTO((void));
+GC_API void GC_enable_incremental GC_PROTO((void));
/* Perform some garbage collection work, if appropriate. */
/* Return 0 if there is no more work to be done. */
/* progress requires it, e.g. if incremental collection is */
/* disabled. It is reasonable to call this in a wait loop */
/* until it returns 0. */
-int GC_collect_a_little GC_PROTO((void));
+GC_API int GC_collect_a_little GC_PROTO((void));
/* Allocate an object of size lb bytes. The client guarantees that */
/* as long as the object is live, it will be referenced by a pointer */
/* for arrays likely to be larger than 100K or so. For other systems, */
/* or if the collector is not configured to recognize all interior */
/* pointers, the threshold is normally much higher. */
-extern GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb));
-extern GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb));
+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));
/* Debugging (annotated) allocation. GC_gcollect will check */
/* objects allocated in this way for overwrites, etc. */
-extern GC_PTR GC_debug_malloc
+GC_API GC_PTR GC_debug_malloc
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_atomic
+GC_API GC_PTR GC_debug_malloc_atomic
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_uncollectable
+GC_API GC_PTR GC_debug_malloc_uncollectable
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern GC_PTR GC_debug_malloc_stubborn
+GC_API GC_PTR GC_debug_malloc_stubborn
GC_PROTO((size_t size_in_bytes, char * descr_string, int descr_int));
-extern void GC_debug_free GC_PROTO((GC_PTR object_addr));
-extern GC_PTR GC_debug_realloc
+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));
-void GC_debug_change_stubborn GC_PROTO((GC_PTR));
-void GC_debug_end_stubborn_change GC_PROTO((GC_PTR));
+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__)
__LINE__)
# define GC_FREE(p) GC_debug_free(p)
# define GC_REGISTER_FINALIZER(p, f, d, of, od) \
- GC_register_finalizer(GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_register_finalizer_ignore_self( \
- GC_base(p), GC_debug_invoke_finalizer, \
- GC_make_closure(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_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p)
typedef void (*GC_finalization_proc)
GC_PROTO((GC_PTR obj, GC_PTR client_data));
-extern void GC_register_finalizer
+GC_API void GC_register_finalizer
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* When obj is no longer accessible, invoke */
/* pointers will not be finalized (or collected). */
/* Thus cycles involving finalizable objects should */
/* be avoided, or broken by disappearing links. */
- /* Fn should terminate as quickly as possible, and */
- /* defer extended computation. */
/* All but the last finalizer registered for an object */
/* is ignored. */
/* Finalization may be removed by passing 0 as fn. */
+ /* Finalizers are implicitly unregistered just before */
+ /* they are invoked. */
/* The old finalizer and client data are stored in */
/* *ofn and *ocd. */
/* Fn is never invoked on an accessible object, */
/* but it's unavoidable for C++, since the compiler may */
/* silently introduce these. It's also benign in that specific */
/* case. */
-extern void GC_register_finalizer_ignore_self
+GC_API void GC_register_finalizer_ignore_self
+ GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
+ GC_finalization_proc *ofn, GC_PTR *ocd));
+GC_API void GC_debug_register_finalizer_ignore_self
GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd,
GC_finalization_proc *ofn, GC_PTR *ocd));
/* where p is a pointer that is not followed by finalization */
/* code, and should not be considered in determining */
/* finalization order. */
-extern int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Link should point to a field of a heap allocated */
/* object obj. *link will be cleared when obj is */
/* found to be inaccessible. This happens BEFORE any */
/* otherwise. */
/* Only exists for backward compatibility. See below: */
-extern int GC_general_register_disappearing_link
+GC_API int GC_general_register_disappearing_link
GC_PROTO((GC_PTR * /* link */, GC_PTR obj));
/* A slight generalization of the above. *link is */
/* cleared when obj first becomes inaccessible. This */
/* the object containing link. Explicitly deallocating */
/* obj may or may not cause link to eventually be */
/* cleared. */
-extern int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
+GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */));
/* Returns 0 if link was not actually registered. */
/* Undoes a registration by either of the above two */
/* routines. */
/* Auxiliary fns to make finalization work correctly with displaced */
/* pointers introduced by the debugging allocators. */
-extern GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data));
-extern void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data));
+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_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));
-extern GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
+GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p));
/* Returns old warning procedure. */
/* The following is intended to be used by a higher level */
# endif /* I_HIDE_POINTERS */
typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data));
-extern GC_PTR GC_call_with_alloc_lock
+GC_API GC_PTR GC_call_with_alloc_lock
GC_PROTO((GC_fn_type fn, GC_PTR client_data));
/* Check that p and q point to the same object. */
/* Returns the first argument. */
/* Succeeds if neither p nor q points to the heap. */
/* May succeed if both p and q point to between heap objects. */
-extern GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
+GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q));
/* Checked pointer pre- and post- increment operations. Note that */
/* the second argument is in units of bytes, not multiples of the */
/* object size. This should either be invoked from a macro, or the */
/* call should be automatically generated. */
-extern GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
-extern GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much));
+GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much));
/* Check that p is visible */
/* to the collector as a possibly pointer containing location. */
/* untyped allocations. The idea is that it should be possible, though */
/* slow, to add such a call to all indirect pointer stores.) */
/* Currently useless for multithreaded worlds. */
-extern GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p));
/* Check that if p is a pointer to a heap page, then it points to */
/* a valid displacement within a heap object. */
/* Fail conspicuously if this property does not hold. */
/* Uninteresting with ALL_INTERIOR_POINTERS. */
/* Always returns its argument. */
-extern GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
+GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p));
/* Safer, but slow, pointer addition. Probably useful mainly with */
/* a preprocessor. Useful only for heap pointers. */
# define GC_PTR_STORE(p, q) *((p) = (q))
#endif
+/* Fynctions called to report pointer checking errors */
+GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q));
+
+GC_API void (*GC_is_valid_displacement_print_proc)
+ GC_PROTO((GC_PTR p));
+
+GC_API void (*GC_is_visible_print_proc)
+ GC_PROTO((GC_PTR p));
#ifdef SOLARIS_THREADS
/* We need to intercept calls to many of the threads primitives, so */
# define thr_continue GC_thr_continue
# define dlopen GC_dlopen
+# endif /* SOLARIS_THREADS */
+
+#ifdef IRIX_THREADS
+/* We treat these similarly. */
+# include <pthread.h>
+# include <signal.h>
+
+ int GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
+ int GC_pthread_join(pthread_t thread, void **retval);
+
+# define pthread_create GC_pthread_create
+# define pthread_sigmask GC_pthread_sigmask
+# define pthread_join GC_pthread_join
+
+#endif /* IRIX_THREADS */
+
+#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
/* 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. */
--- /dev/null
+/*
+ * 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.
+ *
+ * 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 C++ header file that is intended to replace the SGI STL
+// alloc.h.
+//
+// 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 soes not work
+// with g++ 2.7.2 and earlier.
+//
+
+#ifndef GC_ALLOC_H
+
+#define GC_ALLOC_H
+#define __ALLOC_H // Prevent inclusion of the default version. Ugly.
+
+#ifndef __ALLOC
+# define __ALLOC alloc
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+// The following is just replicated from the conventional SGI alloc.h:
+
+template<class T, class alloc>
+class simple_alloc {
+
+public:
+ static T *allocate(size_t n)
+ { return 0 == n? 0 : (T*) alloc::allocate(n * sizeof (T)); }
+ static T *allocate(void)
+ { return (T*) alloc::allocate(sizeof (T)); }
+ static void deallocate(T *p, size_t n)
+ { if (0 != n) alloc::deallocate(p, n * sizeof (T)); }
+ static void deallocate(T *p)
+ { alloc::deallocate(p, sizeof (T)); }
+};
+
+#include "gc.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);
+
+ extern void * GC_malloc_atomic_uncollectable(size_t bytes);
+}
+
+// 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 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 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_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_alloc_template<0> single_client_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 *p, size_t n) { }
+ static void ptr_free_deallocate(void *p, size_t n) { }
+};
+
+typedef gc_alloc_template < 0 > gc_alloc;
+
+template < int dummy >
+class 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 n) { GC_free(p); }
+ static void ptr_free_deallocate(void *p, size_t n) { GC_free(p); }
+};
+
+typedef alloc_template < 0 > 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, alloc)
+__GC_SPECIALIZE(int, alloc)
+__GC_SPECIALIZE(unsigned, alloc)
+__GC_SPECIALIZE(float, alloc)
+__GC_SPECIALIZE(double, 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_alloc)
+__GC_SPECIALIZE(int, single_client_alloc)
+__GC_SPECIALIZE(unsigned, single_client_alloc)
+__GC_SPECIALIZE(float, single_client_alloc)
+__GC_SPECIALIZE(double, single_client_alloc)
+
+#endif _SGI_SOURCE
+
+#endif /* GC_ALLOC_H */
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
+ * Copyright 1996 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.
* or if conservative collector leakage is otherwise a problem (unlikely).
* Note that this is implemented completely separately from the rest
* of the collector, and is not linked in unless referenced.
+ * This does not currently support GC_DEBUG in any interesting way.
*/
/* Boehm, May 19, 1994 2:13 pm PDT */
typedef GC_word GC_descr;
-#if defined(__STDC__) || defined(__cplusplus)
- extern GC_descr GC_make_descriptor(GC_bitmap bm, size_t len);
-#else
- extern GC_descr GC_make_descriptor(/* GC_bitmap bm, size_t len */);
-#endif
+GC_API GC_descr GC_make_descriptor GC_PROTO((GC_bitmap bm, size_t len));
/* Return a type descriptor for the object whose layout */
/* is described by the argument. */
/* The least significant bit of the first word is one */
/* is intended to be called once per type, not once */
/* per allocation. */
-#if defined(__STDC__) || defined(__cplusplus)
- extern void * GC_malloc_explicitly_typed(size_t size_in_bytes, GC_descr d);
-#else
- extern char * GC_malloc_explicitly_typed(/* size_in_bytes, descriptor */);
-#endif
+GC_API void * GC_malloc_explicitly_typed
+ GC_PROTO((size_t size_in_bytes, GC_descr d));
/* Allocate an object whose layout is described by d. */
/* The resulting object MAY NOT BE PASSED TO REALLOC. */
-#if defined(__STDC__) || defined(__cplusplus)
- extern void * GC_calloc_explicitly_typed(size_t nelements,
- size_t element_size_in_bytes,
- GC_descr d);
-#else
- char * GC_calloc_explicitly_typed(/* nelements, size_in_bytes, descriptor */);
+GC_API void * GC_calloc_explicitly_typed
+ GC_PROTO((size_t nelements,
+ size_t element_size_in_bytes,
+ GC_descr d));
/* Allocate an array of nelements elements, each of the */
/* given size, and with the given descriptor. */
/* The elemnt size must be a multiple of the byte */
/* alignment required for pointers. E.g. on a 32-bit */
/* machine with 16-bit aligned pointers, size_in_bytes */
/* must be a multiple of 2. */
-#endif
+
+#ifdef GC_DEBUG
+# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes)
+# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) GC_MALLOC(n*bytes)
+#else
+# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) \
+ GC_malloc_explicitly_typed(bytes, d)
+# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) \
+ GC_calloc_explicitly_typed(n, bytes, d)
+#endif /* !GC_DEBUG */
+
#endif /* _GC_TYPED_H */
# ifndef CORD_POSITION_H
/* The representation of CORD_position. This is private to the */
-/* implementation, but the ise is known to clients. Also */
+/* implementation, but the size is known to clients. Also */
/* the implementation of some exported macros relies on it. */
/* Don't use anything defined here and not in cord.h. */
/* A structure describing an entry on the path from the root */
/* to current position. */
-typedef struct CORD_pos {
+typedef struct CORD_Pos {
size_t cur_pos;
int path_len;
# define CORD_POS_INVALID (0x55555555)
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, August 9, 1995 5:49 pm PDT */
+/* Boehm, February 16, 1996 2:30 pm PST */
# ifndef GC_PRIVATE_H
# include "gc_hdrs.h"
# endif
-# ifndef bool
+# 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(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1020
+ /* Visual C++ 4.2 does not have bool type. */
+ typedef int bool;
+# endif
# endif
# define TRUE 1
# define FALSE 0
# include <stddef.h>
# endif
# define VOLATILE volatile
+# define CONST const
#else
# ifdef MSWIN32
# include <stdlib.h>
# endif
# define VOLATILE
+# define CONST
#endif
#ifdef AMIGA
# define GATHERSTATS
#endif
-# if defined(SOLARIS_THREADS) && !defined(SUNOS5)
---> inconsistent configuration
-# endif
-# if defined(PCR) || defined(SRC_M3) || defined(SOLARIS_THREADS)
-# define THREADS
-# endif
-
-#if defined(SPARC)
-# define ALIGN_DOUBLE /* Align objects of size > 1 word on 2 word */
- /* boundaries. Wasteful of memory, but */
- /* apparently required by SPARC architecture. */
-# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */
- /* include assembly code to do it well. */
-#endif
-
-#ifdef HP_PA
-# define ALIGN_DOUBLE
-#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 */
# define LOCK() mutex_lock(&GC_allocate_ml);
# define UNLOCK() mutex_unlock(&GC_allocate_ml);
# endif
+# ifdef IRIX_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)
+# endif
+ extern unsigned long GC_allocate_lock;
+ /* This is not a mutex because mutexes that obey the (optional) */
+ /* POSIX scheduling rules are subject to convoys in high contention */
+ /* applications. This is basically a spin lock. */
+ extern pthread_t GC_lock_holder;
+ extern void GC_lock(void);
+ /* Allocation lock holder. Only set if acquired by client through */
+ /* GC_call_with_alloc_lock. */
+# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self()
+# define NO_THREAD (pthread_t)(-1)
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self()))
+# ifdef UNDEFINED
+# define LOCK() pthread_mutex_lock(&GC_allocate_ml)
+# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
+# else
+# define LOCK() { if (__test_and_set(&GC_allocate_lock, 1)) GC_lock(); }
+# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64))
+# define UNLOCK() __lock_release(lock)
+# else
+# define UNLOCK() GC_allocate_lock = 0
+# endif
+# endif
+ extern bool GC_collecting;
+# define ENTER_GC() \
+ { \
+ GC_collecting = 1; \
+ }
+# define EXIT_GC() GC_collecting = 0;
+# endif
+# ifdef WIN32_THREADS
+# include <windows.h>
+ GC_API CRITICAL_SECTION GC_allocate_ml;
+# define LOCK() EnterCriticalSection(&GC_allocate_ml);
+# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml);
+# endif
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
+# endif
# else
# define LOCK()
# define UNLOCK()
# endif
+# ifndef SET_LOCK_HOLDER
+# define SET_LOCK_HOLDER()
+# define UNSET_LOCK_HOLDER()
+# define I_HOLD_LOCK() FALSE
+ /* Used on platforms were locks can be reacquired, */
+ /* so it doesn't matter if we lie. */
+# endif
+# ifndef ENTER_GC
+# define ENTER_GC()
+# define EXIT_GC()
+# endif
# ifndef DCL_LOCK_STATE
# define DCL_LOCK_STATE
# else
# if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \
|| defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \
- || defined(NO_SIGNALS)
+ || defined(NO_SIGNALS) || defined(IRIX_THREADS)
/* Also useful for debugging. */
/* Should probably use thr_sigsetmask for SOLARIS_THREADS. */
# define DISABLE_SIGNALS()
PCR_allSigsBlocked, \
PCR_waitForever);
# else
-# ifdef SOLARIS_THREADS
+# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS)
+ void GC_stop_world();
+ void GC_start_world();
# define STOP_WORLD() GC_stop_world()
# define START_WORLD() GC_start_world()
# else
# ifdef SMALL_CONFIG
# define ABORT(msg) abort();
# else
- void GC_abort();
+ GC_API void GC_abort();
# define ABORT(msg) GC_abort(msg);
# endif
# endif
ptr_t _uobjfreelist[MAXOBJSZ+1];
/* uncollectable but traced objs */
+ /* objects on this and auobjfreelist */
+ /* are always marked, except during */
+ /* garbage collections. */
+# ifdef ATOMIC_UNCOLLECTABLE
+ ptr_t _auobjfreelist[MAXOBJSZ+1];
+# endif
+ /* uncollectable but traced objs */
# ifdef GATHERSTATS
word _composite_in_use;
#endif
};
-extern GC_FAR struct _GC_arrays GC_arrays;
+GC_API GC_FAR struct _GC_arrays GC_arrays;
# define GC_objfreelist GC_arrays._objfreelist
# define GC_aobjfreelist GC_arrays._aobjfreelist
# define GC_uobjfreelist GC_arrays._uobjfreelist
+# ifdef ATOMIC_UNCOLLECTABLE
+# define GC_auobjfreelist GC_arrays._auobjfreelist
+# endif
# define GC_sobjfreelist GC_arrays._sobjfreelist
# define GC_valid_offsets GC_arrays._valid_offsets
# define GC_modws_valid_offsets GC_arrays._modws_valid_offsets
# define beginGC_arrays ((ptr_t)(&GC_arrays))
# define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays))
+GC_API word GC_fo_entries;
# define MAXOBJKINDS 16
# define PTRFREE 0
# define NORMAL 1
# define UNCOLLECTABLE 2
-# define STUBBORN 3
+# ifdef ATOMIC_UNCOLLECTABLE
+# define AUNCOLLECTABLE 3
+# define STUBBORN 4
+# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE)
+# else
+# define STUBBORN 3
+# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE)
+# endif
extern int GC_n_kinds;
/* work of whatever kind is needed. Returns */
/* quickly if no collection is in progress. */
/* Return TRUE if mark phase finished. */
-void GC_initiate_full(); /* initiate full collection. */
-void GC_initiate_partial(); /* initiate partial collection. */
+void GC_initiate_gc(); /* initiate collection. */
+ /* If the mark state is invalid, this */
+ /* becomes full colleection. Otherwise */
+ /* it's partial. */
void GC_push_all(/*b,t*/); /* Push everything in a range */
/* onto mark stack. */
void GC_push_dirty(/*b,t*/); /* Push all possibly changed */
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 */);
/* Is the address p in one of the registered static */
/* to the collector. */
ptr_t GC_generic_malloc_inner(/* bytes, kind */);
/* Ditto, but I already hold lock, etc. */
-ptr_t GC_generic_malloc_words_small(/*words, kind*/);
+ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind));
/* As above, but size in units of words */
/* Bypasses MERGE_SIZES. Assumes */
/* words <= MAXOBJSZ. */
void GC_dump();
/* Make arguments appear live to compiler */
-void GC_noop();
+GC_API void GC_noop();
+void GC_noop1(/* word arg */);
/* Logging and diagnostic output: */
-void GC_printf GC_PROTO((char * format, long, long, long, long, long, long));
+GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long));
/* A version of printf that doesn't allocate, */
/* is restricted to long arguments, and */
/* (unfortunately) doesn't use varargs for */
--- /dev/null
+/*
+ * Copyright (c) 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.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose, provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ */
+/*
+ * Support code for Irix (>=6.2) Pthreads. This relies on properties
+ * not guaranteed by the Pthread standard. It may or may not be portable
+ * to other implementations.
+ */
+
+# if defined(IRIX_THREADS)
+
+# include "gc_priv.h"
+# include <pthread.h>
+# include <time.h>
+# include <errno.h>
+# include <unistd.h>
+# include <sys/mman.h>
+# include <sys/time.h>
+
+#undef pthread_create
+#undef pthread_sigmask
+#undef pthread_join
+
+#ifdef UNDEFINED
+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 CLIENT_OWNS_STACK 4
+ /* Stack was supplied by client. */
+ ptr_t stack;
+ ptr_t stack_ptr; /* Valid only when stopped. */
+ /* But must be within stack region at */
+ /* all times. */
+ size_t stack_size; /* 0 for original thread. */
+ void * status; /* Used only to avoid premature */
+ /* reclamation of any data it might */
+ /* reference. */
+} * GC_thread;
+
+GC_thread GC_lookup_thread(pthread_t id);
+
+/*
+ * The only way to suspend threads given the pthread interface is to send
+ * signals. Unfortunately, this means we have to reserve
+ * SIGUSR2, and intercept client calls to change the signal mask.
+ */
+# define SIG_SUSPEND SIGUSR2
+
+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;
+
+void GC_suspend_handler(int sig)
+{
+ int dummy;
+ GC_thread me;
+ sigset_t all_sigs;
+ sigset_t old_sigs;
+ int i;
+
+ if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
+ 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. */
+ me -> stack_ptr = (ptr_t)(&dummy);
+ GC_n_stopped++;
+ pthread_cond_signal(&GC_suspend_ack_cv);
+ pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
+ pthread_mutex_unlock(&GC_suspend_lock);
+ /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
+}
+
+
+bool GC_thr_initialized = FALSE;
+
+size_t GC_min_stack_sz;
+
+size_t GC_page_sz;
+
+# define N_FREE_LISTS 25
+ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
+ /* GC_stack_free_lists[i] is free list for stacks of */
+ /* size GC_min_stack_sz*2**i. */
+ /* Free lists are linked through first word. */
+
+/* Return a stack of size at least *stack_size. *stack_size is */
+/* replaced by the actual stack size. */
+/* Caller holds allocation lock. */
+ptr_t GC_stack_alloc(size_t * stack_size)
+{
+ register size_t requested_sz = *stack_size;
+ register size_t search_sz = GC_min_stack_sz;
+ register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */
+ register ptr_t result;
+
+ while (search_sz < requested_sz) {
+ search_sz *= 2;
+ index++;
+ }
+ if ((result = GC_stack_free_lists[index]) == 0
+ && (result = GC_stack_free_lists[index+1]) != 0) {
+ /* Try next size up. */
+ search_sz *= 2; index++;
+ }
+ if (result != 0) {
+ GC_stack_free_lists[index] = *(ptr_t *)result;
+ } else {
+ result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
+ result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
+ /* Protect hottest page to detect overflow. */
+ /* mprotect(result, GC_page_sz, PROT_NONE); */
+ result += GC_page_sz;
+ }
+ *stack_size = search_sz;
+ return(result);
+}
+
+/* Caller holds allocation lock. */
+void GC_stack_free(ptr_t stack, size_t size)
+{
+ register int index = 0;
+ register size_t search_sz = GC_min_stack_sz;
+
+ while (search_sz < size) {
+ search_sz *= 2;
+ index++;
+ }
+ if (search_sz != size) ABORT("Bad stack size");
+ *(ptr_t *)stack = GC_stack_free_lists[index];
+ GC_stack_free_lists[index] = stack;
+}
+
+
+
+# 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 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;
+
+ 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++;
+ 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--;
+ break;
+ case 0:
+ break;
+ default:
+ ABORT("thr_kill failed");
+ }
+ }
+ }
+ }
+ pthread_mutex_lock(&GC_suspend_lock);
+ while(GC_n_stopped < n_live_threads) {
+ pthread_cond_wait(&GC_suspend_ack_cv, &GC_suspend_lock);
+ }
+ pthread_mutex_unlock(&GC_suspend_lock);
+ /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
+}
+
+/* Caller holds allocation lock. */
+void GC_start_world()
+{
+ /* GC_printf0("World starting\n"); */
+ pthread_mutex_lock(&GC_suspend_lock);
+ /* All other threads are at pthread_cond_wait in signal handler. */
+ /* Otherwise we couldn't have acquired the lock. */
+ pthread_mutex_unlock(&GC_suspend_lock);
+ pthread_cond_broadcast(&GC_continue_cv);
+}
+
+
+extern ptr_t GC_approx_sp();
+
+/* 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();
+ /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
+ 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 -> stack_size != 0) {
+ hi = p -> stack + p -> stack_size;
+ } else {
+ /* The original stack. */
+ hi = GC_stackbottom;
+ }
+ GC_push_all_stack(lo, hi);
+ }
+ }
+}
+
+
+/* We hold the allocation lock. */
+GC_thr_init()
+{
+ GC_thread t;
+
+ GC_thr_initialized = TRUE;
+ GC_min_stack_sz = HBLKSIZE;
+ GC_page_sz = sysconf(_SC_PAGESIZE);
+ if (sigset(SIG_SUSPEND, GC_suspend_handler) != SIG_DFL)
+ ABORT("Previously installed SIG_SUSPEND handler");
+ /* Add the initial thread, so we can stop it. */
+ t = GC_new_thread(pthread_self());
+ t -> stack_size = 0;
+ t -> stack_ptr = (ptr_t)(&t);
+ t -> flags = DETACHED;
+}
+
+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;
+};
+
+void GC_thread_exit_proc(void *dummy)
+{
+ GC_thread me;
+
+ 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;
+
+ LOCK();
+ me = GC_lookup_thread(pthread_self());
+ UNLOCK();
+ pthread_cleanup_push(GC_thread_exit_proc, 0);
+ result = (*(si -> start_routine))(si -> arg);
+ me -> status = result;
+ me -> flags |= FINISHED;
+ pthread_cleanup_pop(1);
+ /* This involves acquiring the lock, ensuring that we can't exit */
+ /* while a collection that thinks we're alive is trying to stop */
+ /* us. */
+ return(result);
+}
+
+int
+GC_pthread_create(pthread_t *new_thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg)
+{
+ int result;
+ GC_thread t;
+ 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));
+
+ if (0 == si) return(ENOMEM);
+ 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_getstackaddr(&new_attr, &stack);
+ }
+ pthread_attr_getstacksize(&new_attr, &stacksize);
+ pthread_attr_getdetachstate(&new_attr, &detachstate);
+ if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
+ if (0 == stack) {
+ stack = (void *)GC_stack_alloc(&stacksize);
+ if (0 == stack) {
+ UNLOCK();
+ return(ENOMEM);
+ }
+ pthread_attr_setstackaddr(&new_attr, stack);
+ } else {
+ 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)) {
+ GC_stack_free(stack, stacksize);
+ }
+ UNLOCK();
+ /* pthread_attr_destroy(&new_attr); */
+ return(result);
+}
+
+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 ... */
+
+unsigned long 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;
+ unsigned junk;
+# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
+ int i;
+
+ if (!__test_and_set(&GC_allocate_lock, 1)) {
+ return;
+ }
+ 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 (!__test_and_set(&GC_allocate_lock, 1)) {
+ /*
+ * 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 (;;) {
+ if (!__test_and_set(&GC_allocate_lock, 1)) {
+ return;
+ }
+ sched_yield();
+ }
+}
+
+
+
+# else
+
+#ifndef LINT
+ int GC_no_Irix_threads;
+#endif
+
+# endif /* IRIX_THREADS */
+
# if defined(I386) &&!defined(OS2) &&!defined(SVR4) &&!defined(MSWIN32) && !defined(SCO) && (!defined(LINUX) || !defined(__ELF__))
/* I386 code, generic code does not appear to work */
/* It does appear to work under OS2, and asms dont */
+ /* This is used for some 38g UNIX variants and for CYGWIN32 */
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");
asm("pushl %ebx"); asm("call GC_push_one"); asm("addl $4,%esp");
# endif
-# if defined(I386) && defined(MSWIN32)
+# if defined(I386) && defined(MSWIN32) && !defined(USE_GENERIC)
/* I386 code, Microsoft variant */
__asm push eax
__asm call GC_push_one
# endif /* M68K/SYSV */
-# if defined(HP_PA) || defined(M88K) || defined(POWERPC) || (defined(I386) && defined(OS2))
+# if defined(HP_PA) || defined(M88K) || defined(POWERPC) || (defined(I386) && (defined(OS2) || defined(USE_GENERIC))) || defined(UTS4)
/* Generic code */
/* The idea is due to Parag Patel at HP. */
/* We're not sure whether he would like */
for (; (char *)i < lim; i++) {
*i = 0;
}
-# ifdef POWERPC
+# if defined(POWERPC) || defined(MSWIN32)
(void) setjmp(regs);
# else
(void) _setjmp(regs);
# if !(defined M68K) && !(defined VAX) && !(defined RT)
# if !(defined SPARC) && !(defined I386) && !(defined NS32K)
# if !defined(HP_PA) && !defined(M88K) && !defined(POWERPC)
+# if !defined(UTS4)
--> bad news <--
+# endif
# endif
# endif
# endif
return((ptr_t)op);
}
-/* Allocate a composite object of size n bytes. The caller guarantees */
-/* that pointers past the first page are not relevant. Caller holds */
-/* allocation lock. */
-ptr_t GC_generic_malloc_inner_ignore_off_page(lb, k)
-register size_t lb;
-register int k;
-{
- register struct hblk * h;
- register word n_blocks;
- register word lw;
- register ptr_t op;
-
- if (lb <= HBLKSIZE)
- return(GC_generic_malloc_inner((word)lb, k));
- n_blocks = divHBLKSZ(ADD_SLOP(lb) + HDR_BYTES + HBLKSIZE-1);
- if (!GC_is_initialized) GC_init_inner();
- /* Do our share of marking work */
- if(GC_incremental && !GC_dont_gc)
- GC_collect_a_little_inner((int)n_blocks);
- lw = ROUNDED_UP_WORDS(lb);
- while ((h = GC_allochblk(lw, k, IGNORE_OFF_PAGE)) == 0
- && GC_collect_or_expand(n_blocks, TRUE));
- if (h == 0) {
- op = 0;
- } else {
- op = (ptr_t) (h -> hb_body);
- GC_words_wasted += BYTES_TO_WORDS(n_blocks * HBLKSIZE) - lw;
- }
- GC_words_allocd += lw;
- return((ptr_t)op);
-}
-
-ptr_t GC_generic_malloc_ignore_off_page(lb, k)
-register size_t lb;
-register int k;
-{
- register ptr_t result;
- DCL_LOCK_STATE;
-
- GC_invoke_finalizers();
- DISABLE_SIGNALS();
- LOCK();
- result = GC_generic_malloc_inner_ignore_off_page(lb,k);
- UNLOCK();
- ENABLE_SIGNALS();
- return(result);
-}
-
-# if defined(__STDC__) || defined(__cplusplus)
- void * GC_malloc_ignore_off_page(size_t lb)
-# else
- char * GC_malloc_ignore_off_page(lb)
- register size_t lb;
-# endif
-{
- return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, NORMAL));
-}
-
-# if defined(__STDC__) || defined(__cplusplus)
- void * GC_malloc_atomic_ignore_off_page(size_t lb)
-# else
- char * GC_malloc_atomic_ignore_off_page(lb)
- register size_t lb;
-# endif
-{
- return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, PTRFREE));
-}
-
ptr_t GC_generic_malloc(lb, k)
register word lb;
register int k;
result = GC_generic_malloc_inner(lb, k);
UNLOCK();
ENABLE_SIGNALS();
- return(result);
-}
-
-
-/* Analogous to the above, but assumes a small object size, and */
-/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */
-ptr_t GC_generic_malloc_words_small(lw, k)
-register word lw;
-register int k;
-{
-register ptr_t op;
-register ptr_t *opp;
-register struct obj_kind * kind = GC_obj_kinds + k;
-DCL_LOCK_STATE;
-
- GC_invoke_finalizers();
- DISABLE_SIGNALS();
- LOCK();
- opp = &(kind -> ok_freelist[lw]);
- if( (op = *opp) == 0 ) {
- if (!GC_is_initialized) {
- GC_init_inner();
- }
- if (kind -> ok_reclaim_list == 0) {
- if (!GC_alloc_reclaim_list(kind)) goto out;
- }
- op = GC_clear_stack(GC_allocobj(lw, k));
- if (op == 0) goto out;
- }
- *opp = obj_link(op);
- obj_link(op) = 0;
- GC_words_allocd += lw;
-
-out:
- UNLOCK();
- ENABLE_SIGNALS();
- return((ptr_t)op);
-}
-
-#if defined(THREADS) && !defined(SRC_M3)
-/* Return a list of 1 or more objects of the indicated size, linked */
-/* through the first word in the object. This has the advantage that */
-/* it acquires the allocation lock only once, and may greatly reduce */
-/* time wasted contending for the allocation lock. Typical usage would */
-/* be in a thread that requires many items of the same size. It would */
-/* keep its own free list in thread-local storage, and call */
-/* GC_malloc_many or friends to replenish it. (We do not round up */
-/* object sizes, since a call indicates the intention to consume many */
-/* objects of exactly this size.) */
-/* Note that the client should usually clear the link field. */
-ptr_t GC_generic_malloc_many(lb, k)
-register word lb;
-register int k;
-{
-ptr_t op;
-register ptr_t p;
-ptr_t *opp;
-word lw;
-register word my_words_allocd;
-DCL_LOCK_STATE;
-
- if (!SMALL_OBJ(lb)) {
- op = GC_generic_malloc(lb, k);
- obj_link(op) = 0;
- return(op);
- }
- lw = ALIGNED_WORDS(lb);
- GC_invoke_finalizers();
- DISABLE_SIGNALS();
- LOCK();
- opp = &(GC_obj_kinds[k].ok_freelist[lw]);
- if( (op = *opp) == 0 ) {
- if (!GC_is_initialized) {
- GC_init_inner();
- }
- op = GC_clear_stack(GC_allocobj(lw, k));
- if (op == 0) goto out;
- }
- *opp = 0;
- my_words_allocd = 0;
- for (p = op; p != 0; p = obj_link(p)) {
- my_words_allocd += lw;
- if (my_words_allocd >= BODY_SZ) {
- *opp = obj_link(p);
- obj_link(p) = 0;
- break;
- }
+ if (0 == result) {
+ return((*GC_oom_fn)(lb));
+ } else {
+ return(result);
}
- GC_words_allocd += my_words_allocd;
-
-out:
- UNLOCK();
- ENABLE_SIGNALS();
- return(op);
-
-}
-
-void * GC_malloc_many(size_t lb)
-{
- return(GC_generic_malloc_many(lb, NORMAL));
-}
+}
-/* Note that the "atomic" version of this would be unsafe, since the */
-/* links would not be seen by the collector. */
-# endif
#define GENERAL_MALLOC(lb,k) \
(GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k))
}
# endif /* REDIRECT_MALLOC */
-/* Allocate lb bytes of pointerful, traced, but not collectable data */
-# ifdef __STDC__
- GC_PTR GC_malloc_uncollectable(size_t lb)
-# else
- GC_PTR GC_malloc_uncollectable(lb)
- size_t lb;
-# endif
-{
-register ptr_t op;
-register ptr_t *opp;
-register word lw;
-DCL_LOCK_STATE;
-
- if( SMALL_OBJ(lb) ) {
-# ifdef MERGE_SIZES
-# ifdef ADD_BYTE_AT_END
- if (lb != 0) lb--;
- /* We don't need the extra byte, since this won't be */
- /* collected anyway. */
-# endif
- lw = GC_size_map[lb];
-# else
- lw = ALIGNED_WORDS(lb);
-# endif
- opp = &(GC_uobjfreelist[lw]);
- FASTLOCK();
- if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) {
- /* See above comment on signals. */
- *opp = obj_link(op);
- obj_link(op) = 0;
- GC_words_allocd += lw;
- GC_set_mark_bit(op);
- GC_non_gc_bytes += WORDS_TO_BYTES(lw);
- FASTUNLOCK();
- return((GC_PTR) op);
- }
- FASTUNLOCK();
- op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
- } else {
- op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
- }
- /* We don't need the lock here, since we have an undisguised */
- /* pointer. We do need to hold the lock while we adjust */
- /* mark bits. */
- {
- register struct hblk * h;
-
- h = HBLKPTR(op);
- lw = HDR(h) -> hb_sz;
-
- DISABLE_SIGNALS();
- LOCK();
- GC_set_mark_bit(op);
- GC_non_gc_bytes += WORDS_TO_BYTES(lw);
- UNLOCK();
- ENABLE_SIGNALS();
- return((GC_PTR) op);
- }
-}
-
GC_PTR GC_generic_or_special_malloc(lb,knd)
word lb;
int knd;
return(GC_malloc((size_t)lb));
case UNCOLLECTABLE:
return(GC_malloc_uncollectable((size_t)lb));
+# ifdef ATOMIC_UNCOLLECTABLE
+ case AUNCOLLECTABLE:
+ return(GC_malloc_atomic_uncollectable((size_t)lb));
+# endif /* ATOMIC_UNCOLLECTABLE */
default:
return(GC_generic_malloc(lb,knd));
}
descr = GC_obj_kinds[obj_kind].ok_descriptor;
if (GC_obj_kinds[obj_kind].ok_relocate_descr) descr += sz;
hhdr -> hb_descr = descr;
- if (obj_kind == UNCOLLECTABLE) GC_non_gc_bytes += (sz - orig_sz);
+ if (IS_UNCOLLECTABLE(obj_kind)) GC_non_gc_bytes += (sz - orig_sz);
/* Extra area is already cleared by allochblk. */
}
if (ADD_SLOP(lb) <= sz) {
/* Could also return original object. But this */
/* gives the client warning of imminent disaster. */
BCOPY(p, result, lb);
- GC_free(p);
+# ifndef IGNORE_FREE
+ GC_free(p);
+# endif
return(result);
}
} else {
if (result == 0) return(0);
BCOPY(p, result, sz);
- GC_free(p);
+# ifndef IGNORE_FREE
+ GC_free(p);
+# endif
return(result);
}
}
GC_mem_freed += sz;
/* A signal here can make GC_mem_freed and GC_non_gc_bytes */
/* inconsistent. We claim this is benign. */
- if (knd == UNCOLLECTABLE) GC_non_gc_bytes -= WORDS_TO_BYTES(sz);
+ if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= WORDS_TO_BYTES(sz);
+ /* Its unnecessary to clear the mark bit. If the */
+ /* object is reallocated, it doesn't matter. O.w. the */
+ /* collector will do it, since it's on a free list. */
if (ok -> ok_init) {
BZERO((word *)p + 1, WORDS_TO_BYTES(sz-1));
}
DISABLE_SIGNALS();
LOCK();
GC_mem_freed += sz;
- if (knd == UNCOLLECTABLE) GC_non_gc_bytes -= WORDS_TO_BYTES(sz);
+ if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= WORDS_TO_BYTES(sz);
GC_freehblk(h);
UNLOCK();
ENABLE_SIGNALS();
GC_PTR p;
# endif
{
+# ifndef IGNORE_FREE
GC_free(p);
+# endif
}
# endif /* REDIRECT_MALLOC */
--- /dev/null
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * These are extra allocation routines which are likely to be less
+ * frequently used than those in malloc.c. They are separate in the
+ * hope that the .o file will be excluded from statically linked
+ * executables. We should probably break this up further.
+ */
+
+#include <stdio.h>
+#include "gc_priv.h"
+
+extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */
+void GC_extend_size_map(); /* in misc.c. */
+
+/* Some externally visible but unadvertised variables to allow access to */
+/* free lists from inlined allocators without including gc_priv.h */
+/* or introducing dependencies on internal data structure layouts. */
+ptr_t * CONST GC_objfreelist_ptr = GC_objfreelist;
+ptr_t * CONST GC_aobjfreelist_ptr = GC_aobjfreelist;
+ptr_t * CONST GC_uobjfreelist_ptr = GC_uobjfreelist;
+# ifdef ATOMIC_UNCOLLECTABLE
+ ptr_t * CONST GC_auobjfreelist_ptr = GC_auobjfreelist;
+# endif
+
+/* Allocate a composite object of size n bytes. The caller guarantees */
+/* that pointers past the first page are not relevant. Caller holds */
+/* allocation lock. */
+ptr_t GC_generic_malloc_inner_ignore_off_page(lb, k)
+register size_t lb;
+register int k;
+{
+ register struct hblk * h;
+ register word n_blocks;
+ register word lw;
+ register ptr_t op;
+
+ if (lb <= HBLKSIZE)
+ return(GC_generic_malloc_inner((word)lb, k));
+ n_blocks = divHBLKSZ(ADD_SLOP(lb) + HDR_BYTES + HBLKSIZE-1);
+ if (!GC_is_initialized) GC_init_inner();
+ /* Do our share of marking work */
+ if(GC_incremental && !GC_dont_gc)
+ GC_collect_a_little_inner((int)n_blocks);
+ lw = ROUNDED_UP_WORDS(lb);
+ while ((h = GC_allochblk(lw, k, IGNORE_OFF_PAGE)) == 0
+ && GC_collect_or_expand(n_blocks, TRUE));
+ if (h == 0) {
+ op = 0;
+ } else {
+ op = (ptr_t) (h -> hb_body);
+ GC_words_wasted += BYTES_TO_WORDS(n_blocks * HBLKSIZE) - lw;
+ }
+ GC_words_allocd += lw;
+ return((ptr_t)op);
+}
+
+ptr_t GC_generic_malloc_ignore_off_page(lb, k)
+register size_t lb;
+register int k;
+{
+ register ptr_t result;
+ DCL_LOCK_STATE;
+
+ GC_invoke_finalizers();
+ DISABLE_SIGNALS();
+ LOCK();
+ result = GC_generic_malloc_inner_ignore_off_page(lb,k);
+ UNLOCK();
+ ENABLE_SIGNALS();
+ if (0 == result) {
+ return((*GC_oom_fn)(lb));
+ } else {
+ return(result);
+ }
+}
+
+# if defined(__STDC__) || defined(__cplusplus)
+ void * GC_malloc_ignore_off_page(size_t lb)
+# else
+ char * GC_malloc_ignore_off_page(lb)
+ register size_t lb;
+# endif
+{
+ return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, NORMAL));
+}
+
+# if defined(__STDC__) || defined(__cplusplus)
+ void * GC_malloc_atomic_ignore_off_page(size_t lb)
+# else
+ char * GC_malloc_atomic_ignore_off_page(lb)
+ register size_t lb;
+# endif
+{
+ return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, PTRFREE));
+}
+
+/* Increment GC_words_allocd from code that doesn't have direct access */
+/* to GC_arrays. */
+# ifdef __STDC__
+void GC_incr_words_allocd(size_t n)
+{
+ GC_words_allocd += n;
+}
+
+/* The same for GC_mem_freed. */
+void GC_incr_mem_freed(size_t n)
+{
+ GC_mem_freed += n;
+}
+# endif /* __STDC__ */
+
+/* Analogous to the above, but assumes a small object size, and */
+/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */
+#ifdef __STDC__
+ ptr_t GC_generic_malloc_words_small(size_t lw, int k)
+#else
+ ptr_t GC_generic_malloc_words_small(lw, k)
+ register size_t lw;
+ register int k;
+#endif
+{
+register ptr_t op;
+register ptr_t *opp;
+register struct obj_kind * kind = GC_obj_kinds + k;
+DCL_LOCK_STATE;
+
+ GC_invoke_finalizers();
+ DISABLE_SIGNALS();
+ LOCK();
+ opp = &(kind -> ok_freelist[lw]);
+ if( (op = *opp) == 0 ) {
+ if (!GC_is_initialized) {
+ GC_init_inner();
+ }
+ if (kind -> ok_reclaim_list != 0 || GC_alloc_reclaim_list(kind)) {
+ op = GC_clear_stack(GC_allocobj(lw, k));
+ }
+ if (op == 0) {
+ UNLOCK();
+ ENABLE_SIGNALS();
+ return ((*GC_oom_fn)(WORDS_TO_BYTES(lw)));
+ }
+ }
+ *opp = obj_link(op);
+ obj_link(op) = 0;
+ GC_words_allocd += lw;
+ UNLOCK();
+ ENABLE_SIGNALS();
+ return((ptr_t)op);
+}
+
+#if defined(THREADS) && !defined(SRC_M3)
+/* Return a list of 1 or more objects of the indicated size, linked */
+/* through the first word in the object. This has the advantage that */
+/* it acquires the allocation lock only once, and may greatly reduce */
+/* time wasted contending for the allocation lock. Typical usage would */
+/* be in a thread that requires many items of the same size. It would */
+/* keep its own free list in thread-local storage, and call */
+/* GC_malloc_many or friends to replenish it. (We do not round up */
+/* object sizes, since a call indicates the intention to consume many */
+/* objects of exactly this size.) */
+/* Note that the client should usually clear the link field. */
+ptr_t GC_generic_malloc_many(lb, k)
+register word lb;
+register int k;
+{
+ptr_t op;
+register ptr_t p;
+ptr_t *opp;
+word lw;
+register word my_words_allocd;
+DCL_LOCK_STATE;
+
+ if (!SMALL_OBJ(lb)) {
+ op = GC_generic_malloc(lb, k);
+ if(0 != op) obj_link(op) = 0;
+ return(op);
+ }
+ lw = ALIGNED_WORDS(lb);
+ GC_invoke_finalizers();
+ DISABLE_SIGNALS();
+ LOCK();
+ opp = &(GC_obj_kinds[k].ok_freelist[lw]);
+ if( (op = *opp) == 0 ) {
+ if (!GC_is_initialized) {
+ GC_init_inner();
+ }
+ op = GC_clear_stack(GC_allocobj(lw, k));
+ if (op == 0) {
+ UNLOCK();
+ ENABLE_SIGNALS();
+ op = (*GC_oom_fn)(lb);
+ if(0 != op) obj_link(op) = 0;
+ return(op);
+ }
+ }
+ *opp = 0;
+ my_words_allocd = 0;
+ for (p = op; p != 0; p = obj_link(p)) {
+ my_words_allocd += lw;
+ if (my_words_allocd >= BODY_SZ) {
+ *opp = obj_link(p);
+ obj_link(p) = 0;
+ break;
+ }
+ }
+ GC_words_allocd += my_words_allocd;
+
+out:
+ UNLOCK();
+ ENABLE_SIGNALS();
+ return(op);
+
+}
+
+void * GC_malloc_many(size_t lb)
+{
+ return(GC_generic_malloc_many(lb, NORMAL));
+}
+
+/* Note that the "atomic" version of this would be unsafe, since the */
+/* links would not be seen by the collector. */
+# endif
+
+/* Allocate lb bytes of pointerful, traced, but not collectable data */
+# ifdef __STDC__
+ GC_PTR GC_malloc_uncollectable(size_t lb)
+# else
+ GC_PTR GC_malloc_uncollectable(lb)
+ size_t lb;
+# endif
+{
+register ptr_t op;
+register ptr_t *opp;
+register word lw;
+DCL_LOCK_STATE;
+
+ if( SMALL_OBJ(lb) ) {
+# ifdef MERGE_SIZES
+# ifdef ADD_BYTE_AT_END
+ if (lb != 0) lb--;
+ /* We don't need the extra byte, since this won't be */
+ /* collected anyway. */
+# endif
+ lw = GC_size_map[lb];
+# else
+ lw = ALIGNED_WORDS(lb);
+# endif
+ opp = &(GC_uobjfreelist[lw]);
+ FASTLOCK();
+ if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) {
+ /* See above comment on signals. */
+ *opp = obj_link(op);
+ obj_link(op) = 0;
+ GC_words_allocd += lw;
+ /* Mark bit ws already set on free list. It will be */
+ /* cleared only temporarily during a collection, as a */
+ /* result of the normal free list mark bit clearing. */
+ GC_non_gc_bytes += WORDS_TO_BYTES(lw);
+ FASTUNLOCK();
+ return((GC_PTR) op);
+ }
+ FASTUNLOCK();
+ op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
+ } else {
+ op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
+ }
+ if (0 == op) return(0);
+ /* We don't need the lock here, since we have an undisguised */
+ /* pointer. We do need to hold the lock while we adjust */
+ /* mark bits. */
+ {
+ register struct hblk * h;
+
+ h = HBLKPTR(op);
+ lw = HDR(h) -> hb_sz;
+
+ DISABLE_SIGNALS();
+ LOCK();
+ GC_set_mark_bit(op);
+ GC_non_gc_bytes += WORDS_TO_BYTES(lw);
+ UNLOCK();
+ ENABLE_SIGNALS();
+ return((GC_PTR) op);
+ }
+}
+
+# ifdef ATOMIC_UNCOLLECTABLE
+/* Allocate lb bytes of pointerfree, untraced, uncollectable data */
+/* This is normally roughly equivalent to the system malloc. */
+/* But it may be useful if malloc is redefined. */
+# ifdef __STDC__
+ GC_PTR GC_malloc_atomic_uncollectable(size_t lb)
+# else
+ GC_PTR GC_malloc_atomic_uncollectable(lb)
+ size_t lb;
+# endif
+{
+register ptr_t op;
+register ptr_t *opp;
+register word lw;
+DCL_LOCK_STATE;
+
+ if( SMALL_OBJ(lb) ) {
+# ifdef MERGE_SIZES
+# ifdef ADD_BYTE_AT_END
+ if (lb != 0) lb--;
+ /* We don't need the extra byte, since this won't be */
+ /* collected anyway. */
+# endif
+ lw = GC_size_map[lb];
+# else
+ lw = ALIGNED_WORDS(lb);
+# endif
+ opp = &(GC_auobjfreelist[lw]);
+ FASTLOCK();
+ if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) {
+ /* See above comment on signals. */
+ *opp = obj_link(op);
+ obj_link(op) = 0;
+ GC_words_allocd += lw;
+ /* Mark bit was already set while object was on free list. */
+ GC_non_gc_bytes += WORDS_TO_BYTES(lw);
+ FASTUNLOCK();
+ return((GC_PTR) op);
+ }
+ FASTUNLOCK();
+ op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE);
+ } else {
+ op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE);
+ }
+ if (0 == op) return(0);
+ /* We don't need the lock here, since we have an undisguised */
+ /* pointer. We do need to hold the lock while we adjust */
+ /* mark bits. */
+ {
+ register struct hblk * h;
+
+ h = HBLKPTR(op);
+ lw = HDR(h) -> hb_sz;
+
+ DISABLE_SIGNALS();
+ LOCK();
+ GC_set_mark_bit(op);
+ GC_non_gc_bytes += WORDS_TO_BYTES(lw);
+ UNLOCK();
+ ENABLE_SIGNALS();
+ return((GC_PTR) op);
+ }
+}
+
+#endif /* ATOMIC_UNCOLLECTABLE */
/*VARARGS*/
void GC_noop() {}
+/* Single argument version, robust against whole program analysis. */
+void GC_noop1(x)
+word x;
+{
+ static VOLATILE word sink;
+
+ sink = x;
+}
+
mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0};
word GC_n_mark_procs = 0;
/* UNCOLLECTABLE */
{ &GC_uobjfreelist[0], 0,
0 | DS_LENGTH, TRUE /* add length to descr */, TRUE },
+# ifdef ATOMIC_UNCOLLECTABLE
+ /* AUNCOLLECTABLE */
+ { &GC_auobjfreelist[0], 0,
+ 0 | DS_LENGTH, FALSE /* add length to descr */, FALSE },
+# endif
# ifdef STUBBORN_ALLOC
/*STUBBORN*/ { &GC_sobjfreelist[0], 0,
0 | DS_LENGTH, TRUE /* add length to descr */, TRUE },
# endif
};
-# ifdef STUBBORN_ALLOC
- int GC_n_kinds = 4;
+# ifdef ATOMIC_UNCOLLECTABLE
+# ifdef STUBBORN_ALLOC
+ int GC_n_kinds = 5;
+# else
+ int GC_n_kinds = 4;
+# endif
# else
- int GC_n_kinds = 3;
+# ifdef STUBBORN_ALLOC
+ int GC_n_kinds = 4;
+# else
+ int GC_n_kinds = 3;
+# endif
# endif
BZERO(hhdr -> hb_marks, MARK_BITS_SZ*sizeof(word));
}
+/* Set all mark bits in the header. Used for uncollectable blocks. */
+void GC_set_hdr_marks(hhdr)
+register hdr * hhdr;
+{
+ register int i;
+
+ for (i = 0; i < MARK_BITS_SZ; ++i) {
+ hhdr -> hb_marks[i] = ONES;
+ }
+}
+
/*
* Clear all mark bits associated with block h.
*/
{
register hdr * hhdr = HDR(h);
- if (hhdr -> hb_obj_kind == UNCOLLECTABLE) return;
+ if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) return;
/* Mark bit for these is cleared only once the object is */
/* explicitly deallocated. This either frees the block, or */
/* the bit is cleared once the object is on the free list. */
/*
* Clear mark bits in all allocated heap blocks. This invalidates
* the marker invariant, and sets GC_mark_state to reflect this.
- * (This implicitly starts marking to reestablish the
+ * (This implicitly starts marking to reestablish the invariant.)
*/
void GC_clear_marks()
{
}
-/* Initiate full marking. */
-void GC_initiate_full()
-{
-# ifdef PRINTSTATS
- GC_printf2("***>Full mark for collection %lu after %ld allocd bytes\n",
- (unsigned long) GC_gc_no+1,
- (long)WORDS_TO_BYTES(GC_words_allocd));
-# endif
- GC_promote_black_lists();
- GC_reclaim_or_delete_all();
- GC_clear_marks();
- GC_read_dirty();
-# ifdef STUBBORN_ALLOC
- GC_read_changed();
-# endif
-# ifdef CHECKSUMS
- {
- extern void GC_check_dirty();
-
- GC_check_dirty();
- }
-# endif
-# ifdef GATHERSTATS
- GC_n_rescuing_pages = 0;
-# endif
-}
-
-/* Initiate partial marking. */
+/* Initiate a garbage collection. Initiates a full collection if the */
+/* mark state is invalid. */
/*ARGSUSED*/
-void GC_initiate_partial()
+void GC_initiate_gc()
{
if (GC_dirty_maintained) GC_read_dirty();
# ifdef STUBBORN_ALLOC
>= 0) {
# endif
current_p = GC_mark_stack_top_reg -> mse_start;
+ retry:
descr = GC_mark_stack_top_reg -> mse_descr;
- retry:
if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | DS_TAGS)) {
word tag = descr & DS_TAGS;
mark_stack_limit, ENV(descr));
continue;
case DS_PER_OBJECT:
- descr = *(word *)((ptr_t)current_p + descr - tag);
+ GC_mark_stack_top_reg -> mse_descr =
+ *(word *)((ptr_t)current_p + descr - tag);
goto retry;
}
} else {
--- /dev/null
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+# define call_push(x) move $4,x; jal GC_push_one
+
+ .text
+/* Mark from machine registers that are saved by C compiler */
+# define FRAMESZ 32
+# define RAOFF FRAMESZ-SZREG
+# define GPOFF FRAMESZ-(2*SZREG)
+ NESTED(GC_push_regs, FRAMESZ, ra)
+ move t0,gp
+ SETUP_GPX(t8)
+ PTR_SUBU sp,FRAMESZ
+# ifdef SETUP_GP64
+ SETUP_GP64(GPOFF, GC_push_regs)
+# endif
+ SAVE_GP(GPOFF)
+ REG_S ra,RAOFF(sp)
+# if (_MIPS_SIM == _MIPS_SIM_ABI32)
+ call_push($2)
+ call_push($3)
+# endif
+ call_push($16)
+ call_push($17)
+ call_push($18)
+ call_push($19)
+ call_push($20)
+ call_push($21)
+ call_push($22)
+ call_push($23)
+ call_push($30)
+ REG_L ra,RAOFF(sp)
+# ifdef RESTORE_GP64
+ RESTORE_GP64
+# endif
+ PTR_ADDU sp,FRAMESZ
+ j ra
+ .end GC_push_regs
# ifdef SOLARIS_THREADS
mutex_t GC_allocate_ml; /* Implicitly initialized. */
# else
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
- pthread_mutex_t GC_allocate_ml;
- int GC_pt_init_ok= 0;
-# else
- --> declare allocator lock here
-# endif
+# ifdef WIN32_THREADS
+ GC_API CRITICAL_SECTION GC_allocate_ml;
+# else
+# ifdef IRIX_THREADS
+# ifdef UNDEFINED
+ pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
+# endif
+ pthread_t GC_lock_holder = NO_THREAD;
+# else
+ --> declare allocator lock here
+# endif
+# endif
# endif
# endif
# endif
bool GC_quiet = 0;
+/*ARGSUSED*/
+void * GC_default_oom_fn GC_PROTO((size_t bytes_requested))
+{
+ return(0);
+}
+
+void * (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn;
+
extern signed_word GC_mem_found;
# ifdef MERGE_SIZES
}
/* Make sure the recursive call is not a tail call, and the bzero */
/* call is not recognized as dead code. */
- GC_noop(dummy);
+ GC_noop1((word)dummy);
return(arg);
}
#endif
ptr_t arg;
{
register word sp = (word)GC_approx_sp(); /* Hotter than actual sp */
- register word limit;
# ifdef THREADS
word dummy[CLEAR_SIZE];;
+# else
+ register word limit;
# endif
# define SLOP 400
register word limit;
r = (word)p;
+ if (!GC_is_initialized) return 0;
h = HBLKPTR(r);
GET_BI(r, bi);
- if (bi == 0) {
- /* Collector uninitialized. Nothing allocated yet. */
- return(0);
- }
candidate_hdr = HDR_FROM_BI(bi, r);
if (candidate_hdr == 0) return(0);
/* If it's a pointer to the middle of a large object, move it */
{
DCL_LOCK_STATE;
-# ifdef DEC_PTHREADS
- /* initialise the DECthreads support */
- {
- pthread_mutexattr_t attr;
- pthread_mutexattr_create(&attr);
- pthread_mutexattr_setkind_np(&attr, MUTEX_RECURSIVE_NP);
- pthread_mutex_init(&GC_allocate_ml, attr);
- GC_pt_init();
- }
-# endif
-
DISABLE_SIGNALS();
LOCK();
GC_init_inner();
extern void GC_init_win32();
#endif
+#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
+ extern void GC_thr_init();
+#endif
+
void GC_init_inner()
{
- word dummy;
+# ifndef THREADS
+ word dummy;
+# endif
if (GC_is_initialized) return;
-#ifdef MIT_PTHREADS
- /* We need to beat the GC to initialisation, so that it doesn't
- hang when it first tries to use the allocator lock. */
- {
- pthread_mutexattr_t attr;
- extern void *pthread_initial;
- if (!pthread_initial) pthread_init();
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEXTYPE_RECURSIVE);
- pthread_mutex_init(&GC_allocate_ml, &attr);
- if (!GC_pt_init_ok) GC_pt_init(); /* faut eviter cela... */
- }
-#endif
# ifdef MSWIN32
GC_init_win32();
# endif
# ifdef SOLARIS_THREADS
+ GC_thr_init();
/* We need dirty bits in order to find live stack sections. */
GC_dirty_init();
# endif
-# if !defined(THREADS) || defined(SOLARIS_THREADS)
+# ifdef IRIX_THREADS
+ GC_thr_init();
+# endif
+# if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS)
if (GC_stackbottom == 0) {
GC_stackbottom = GC_get_stack_base();
}
void GC_enable_incremental GC_PROTO(())
{
- /* this is broken under DECthreads */
-#ifndef DEC_PTHREADS
DCL_LOCK_STATE;
# ifndef FIND_LEAK
UNLOCK();
ENABLE_SIGNALS();
# endif
-#endif /* DEC_PTHREADS */
}
+++ /dev/null
-/* mit_threads.c
-/*
-/* Support for MIT-pthreads in Boehm/Demer/Weiser garbage collector.
-/* Depends on a gc.a compiled with -DMIT_PTHREADS, plus local modifications.
-/* All files should be compiled with pgcc.
-/*
-/* GC_init() should be called before doing anything else (the collector tries
-/* to do this early enough, but may not always succeed).
-/*
-/* Author: Ian.Piumarta@INRIA.fr
-/*
-/* Copyright (C) 1996 by INRIA and Ian Piumarta
-/*
-/* last edited: Sun Nov 17 22:49:05 1996 by piumarta (Ian Piumarta) on corto
-/*
-/***************************************************************************/
-
-#ifdef MIT_PTHREADS
-
-#undef DEBUG
-
-#include "config.h"
-
-/* there seems to be no other (easy) way to get at these... */
-
-#if defined(ALPHA)
-# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[34] /* JB_SP */
-#elif defined(SPARC)
-# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[2] /* sc_sp */
-#elif defined(LINUX)
-# define THREAD_SP(thr) (thr)->machdep_data.machdep_state->__sp /* __sp */
-#elif defined(HP_PA)
-# define THREAD_SP(thr) (((int*)((thr)->machdep_data.machdep_state))[1])
-#elif defined(MIPS)
-# define THREAD_SP(thr) (thr)->machdep_data.machdep_state[JB_SP]
-#else
-/* ...define THREAD_SP for your architecture here...
- */
---> where is your stack pointer?
-#endif
-
-#include <stdio.h>
-#define PTHREAD_KERNEL
-#include <pthread.h>
-#undef RETURN
-
-extern void *GC_get_stack_base();
-
-/* use malloc-safe printf from the GC, and GC-safe malloc from pthreads
- */
-extern GC_err_printf();
-extern void *malloc(int);
-
-#ifdef DEBUG
-# define _CR GC_err_printf("\n")
-# define ENTER(X) static char*_me_=#X;GC_err_printf("ENTER: %s\n",_me_)
-# define RETURN GC_err_printf("LEAVE: %s\n",_me_);return
-# define PRINT(X) GC_err_printf("%s: %s\n",_me_,(X))
-# define PRINT1(X,P) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P));_CR
-# define PRINT2(X,P,Q) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P),(Q));_CR
-# define PRINT3(X,P,Q,R) GC_err_printf("%s: ",_me_);GC_err_printf((X),(P),(Q),(R));_CR
-# define ERR(X) {GC_err_printf("%s: ",_me_);perror(X);abort();}
-#else
-# define ENTER(X)
-# define RETURN return
-# define PRINT(X)
-# define PRINT1(X,P)
-# define PRINT2(X,P,Q)
-# define PRINT3(X,P,Q,R)
-# define ERR(X) {perror(X);abort();}
-#endif
-
-/* dequeues */
-
-#define struct_base(PTR, FLD, TYP) ((TYP*)((char*)(PTR)-(char*)(&(((TYP*)0)->FLD))))
-
-typedef struct QUEUE {
- struct QUEUE *flink, *blink;
-} queue;
-
-/* GC thread info structure */
-
-typedef struct GC_PT_INFO {
- queue q; /* the dequeue on which this structure exists */
- pthread_t thread; /* the corresponding thread structure */
- void *stack_top; /* the highest address in this thread's stack */
- void *(*launch)(void *); /* the thread's real start routine */
- void *arg; /* the thread's real start routine argument */
- int mark_state; /* this info structures current civil status */
-} gc_pt_info;
-
-#define GC_PT_UNMARKED 0
-#define GC_PT_MARKED 1
-#define GC_PT_NASCENT 2
-
-static queue infos = { &infos, &infos }; /* the dequeue of info structures */
-extern int GC_pt_init_ok; /* true when init is done */
-
-static pthread_key_t info_key; /* identity mechanism */
-
-
-/* stop the threads world, somewhat gracelessly
- */
-void GC_pt_stop_world()
-{
- pthread_kernel_lock++;
-}
-
-/* restart the threads world; also lacks charm
- */
-void GC_pt_start_world()
-{
- pthread_kernel_lock--;
-}
-
-
-extern pthread_mutex_t GC_allocate_ml;
-
-/* initialise the threads-related GC stuff
- */
-void GC_pt_init()
-{
- ENTER(GC_pt_init);
- if (!GC_pt_init_ok)
- {
- GC_pt_init_ok= 1;
- if (pthread_key_create(&info_key, 0)) ERR("pthread_key_create");
- /*
- * create an info structure for the initial thread and push it onto
- * the info dequeue
- */
- {
- gc_pt_info *info= (gc_pt_info *)malloc(sizeof(gc_pt_info));
- if (!info) ERR("malloc");
- infos.flink= infos.blink= &info->q;
- info->q.flink= info->q.blink= &infos;
- info->thread= pthread_initial;
- info->stack_top= GC_get_stack_base();
- PRINT3("initial thread %lx: info %lx, stack %lx",
- pthread_initial, info, info->stack_top);
- if (pthread_setspecific(info_key, info)) ERR("pthread_setspecific");
- /* GC_err_printf("THREAD INITIAL %lx: info set to %lx\n", pthread_run, info);*/
- }
- }
- RETURN;
-}
-
-
-static void GC_pt_print_threads()
-{
- pthread_t thread;
-
- GC_err_printf("PLL:");
-
- for(thread= pthread_link_list; thread; thread= thread->pll)
- GC_err_printf(" %lx->%lx", thread, thread->pll);
- GC_err_printf("\n");
-}
-
-
-/* print the info list
- */
-static void gc_pt_print_info()
-{
- queue *ptr= infos.flink;
- ENTER(GC_pt_print_info);
- while (ptr != &infos)
- {
- gc_pt_info *info= struct_base(ptr, q, gc_pt_info);
- GC_err_printf("INFO %lx:\n", info);
- GC_err_printf(" queue: %lx\n", info->q);
- GC_err_printf(" thread: %lx\n", info->thread);
- GC_err_printf(" stack_top: %lx\n", info->stack_top);
- GC_err_printf(" launch: %lx\n", info->launch);
- GC_err_printf(" arg: %lx\n", info->arg);
- GC_err_printf(" mark_state: %lx\n", info->mark_state);
- ptr= ptr->flink;
- }
- RETURN;
-}
-
-
-#define FIND_INFO_BY_LOOKUP
-
-/* given some thread, find the corresponding info
- */
-static gc_pt_info *GC_pt_find_info(pthread_t target)
-{
-#ifdef FIND_INFO_BY_LOOKUP
- queue *ptr= infos.flink;
- ENTER(GC_pt_find_info);
- PRINT1("looking for thread %lx", target);
- while (ptr != &infos)
- {
- gc_pt_info *info= struct_base(ptr, q, gc_pt_info);
- PRINT1("looking at %lx", info->thread);
- if (info->thread == target)
- {
- RETURN(info);
- }
- ptr= ptr->flink;
- }
- GC_err_printf("I can't find the info -- giving up.\n");
- abort();
-#else
-
-# ifdef FIND_INFO_BY_GETSPECIFIC /* THIS APPEARS TO BE BROKEN */
- gc_pt_info *info= (gc_pt_info *)pthread_getspecific(info_key);
- ENTER(GC_pt_find_info);
- PRINT1("looking for thread %lx", target);
- /* GC_err_printf("THREAD %lx: found info %lx\n", target, info);*/
- return info;
-# else
-
-# ifdef FIND_INFO_BY_INTERNALS
- ENTER(GC_pt_find_info);
- PRINT1("looking for thread %lx", target);
- if (!target->specific_data)
- {
- PRINT("GC_pt_find_info: no specific data\n");
- return 0;
- }
- if (!target->specific_data[info_key])
- {
- PRINT("GC_pt_find_info: info is null\n");
- return 0;
- }
- return (gc_pt_info *)target->specific_data[info_key];
-# else
-
---> so how do you do want me to find the info?
-
-# endif
-# endif
-#endif
-}
-
-
-/* unmark all info structures
- */
-static void GC_pt_unmark_info()
-{
- int size =0;
- queue *ptr= infos.flink;
- ENTER(GC_pt_unmark_info);
- while (ptr != &infos)
- {
- gc_pt_info *info= struct_base(ptr, q, gc_pt_info);
- if (info->mark_state != GC_PT_NASCENT)
- info->mark_state= GC_PT_UNMARKED;
- ptr= ptr->flink;
- size++;
- }
- PRINT1("INFO LIST SIZE IS %d", size);
-}
-
-
-/* cleanup for info structure -- this used to be called as a destructor
- * for the thread-specific data, but now it's only ever called explicitly
- * from within this file
- */
-static void GC_pt_delete_info(gc_pt_info *info)
-{
- ENTER(GC_pt_delete_info);
- PRINT1("pthread_run is %lx", pthread_run);
- PRINT1("info is %lx", info);
- /* GC_err_printf("THREAD %lx: DELETE INFO %lx\n", info->thread, info);*/
- pthread_mutex_lock(&GC_allocate_ml);
- {
- info->q.blink->flink= info->q.flink;
- info->q.flink->blink= info->q.blink;
- }
- pthread_mutex_unlock(&GC_allocate_ml);
- PRINT1("info is %lx", info);
- free(info);
- RETURN;
-}
-
-
-/* sweep up unused info structures
- */
-static void GC_pt_sweep_info()
-{
- queue *ptr= infos.flink;
- ENTER(GC_pt_sweep_info);
- while (ptr != &infos)
- {
- gc_pt_info *info= struct_base(ptr, q, gc_pt_info);
- ptr= ptr->flink;
- if (info->mark_state == GC_PT_UNMARKED)
- GC_pt_delete_info(info);
- }
-}
-
-
-/* traverse the pthread linked list (containing all threads) pushing
- * the pthread structures and stack (if appropriate) for each thread.
- */
-void GC_pt_push_all_stacks()
-{
- pthread_t thread;
- ENTER(GC_pt_push_all_stacks);
-
- /* GC_err_printf("MARKING STACKS...\n");*/
-
- if ((infos.flink == &infos) && (infos.blink == &infos)) { RETURN; }
-
- /* unmark all info structures */
- GC_pt_unmark_info();
-
- /* flush (register windows and other) state for running thread */
- machdep_save_state();
-
- /* GC_err_printf("THREAD LOOP: START\n");*/
-
- for(thread= pthread_link_list; thread; thread= thread->pll)
- {
- /* GC_err_printf("THREAD LOOP: THREAD %lx, PLL %lx\n", thread, thread->pll);*/
-
- PRINT("thread structures");
- /* mark from the pthread internal structures */
- PRINT2("GC_push_all(%lx, %lx)", thread, (char *)thread + sizeof(*thread));
- GC_push_all(thread, (char *)thread + sizeof(*thread));
- PRINT2("GC_push_all(%lx, %lx)", thread->machdep_data.machdep_state,
- (char *)thread->machdep_data.machdep_state
- + sizeof(*thread->machdep_data.machdep_state));
- GC_push_all(thread->machdep_data.machdep_state,
- (char *)thread->machdep_data.machdep_state
- + sizeof(*thread->machdep_data.machdep_state));
- PRINT("specific data");
- /* mark the thread-specific data area */
- if (thread->specific_data_count)
- {
- PRINT2("GC_push_all(%lx, %lx)\n", (void *)thread->specific_data,
- ((void **)thread->specific_data) + PTHREAD_DATAKEYS_MAX - 1);
- GC_push_all((void *)thread->specific_data,
- ((void **)thread->specific_data) + PTHREAD_DATAKEYS_MAX - 1);
- }
- PRINT("thread stacks");
- /* mark from the thread's stack */
-/* if (thread->state != PS_DEAD)
- */
- {
- gc_pt_info *info= GC_pt_find_info(thread);
- /* GC_err_printf("LOOKING UP INFO FOR %lx\n", thread);*/
- if (info)
- {
- /* GC_err_printf("INFO FOR %lx AT %lx\n", thread, info);*/
- GC_push_all(info, (char *)info+sizeof(*info));
- /* GC_err_printf("THREAD %lx: MARK INFO %lx\n", info->thread, info);*/
- info->mark_state= GC_PT_MARKED;
- if (thread == pthread_run)
- {
- PRINT2("(RUN) GC_push_all_stack(%lx, %lx)", &thread, info->stack_top);
- if ((void*)&thread < info->stack_top)
- GC_push_all_stack(&thread, info->stack_top);
- else
- GC_push_all_stack(info->stack_top, &thread);
- }
- else /* suspended */
- {
- if ((void*)THREAD_SP(thread) < info->stack_top)
- {
- PRINT2("(SUS) GC_push_all_stack(%lx, %lx)",
- THREAD_SP(thread), info->stack_top);
- GC_push_all_stack(THREAD_SP(thread), info->stack_top);
- }
- else
- {
- /*PRINT2("(SUS) GC_push_all_stack(%lx, %lx) --- IGNORING BAD SP!!!",
- THREAD_SP(thread), info->stack_top);*/
- GC_push_all_stack(info->stack_top, THREAD_SP(thread));
- }
-
- }
- }
- else
- GC_err_printf("THREAD %lx: NO INFO!!!!!!!!!\n", thread);
- /* else /* we're a dead thread */
- /* {
- /* PRINT1("NO STACK (thread %lx is dead)", thread);
- /* }
- */
- }
- }
-
- /* GC_err_printf("THREAD LOOP: END\n");*/
-
- /* sweep up dead info */
- GC_pt_sweep_info();
-
- RETURN;
-}
-
-
-/* start routine wrapper, which places the threads id into the info
- * structure and registers it as thread-specific data; called with the
- * info structure as argument.
- */
-static void *GC_pt_starter(void *arg)
-{
- gc_pt_info *info= (gc_pt_info *)arg;
- void *result;
- ENTER(GC_pt_starter);
- if (pthread_setspecific(info_key, info)) ERR("pthread_setspecific");
- /* GC_err_printf("THREAD %lx: info set to %lx\n", pthread_run, info);*/
- if (!GC_pt_find_info(pthread_run))
- {
- GC_err_printf("GC_pt_starter: the active thread does not exist!\n");
- abort();
- }
- PRINT3("*** STARTING %lx: pthread_run is %lx, info %lx",
- info->thread, pthread_run, info);
- /* GC_err_printf("THREAD RUN: %lx\n", info->thread);*/
- info->mark_state= GC_PT_MARKED;
- result= info->launch(info->arg);
- /* GC_err_printf("THREAD EXIT: %lx\n", info->thread);*/
- PRINT3("*** EXITING %lx: pthread_run is %lx, info %lx",
- info->thread, pthread_run, info);
- RETURN(result);
-}
-
-
-/* pthread_create wrapper, which creates an info structure on the info
- * dequeue and then has the thread start up in GC_pt_starter.
- */
-int GC_pt_pthread_create(pthread_t *thread,
- const pthread_attr_t *attr,
- void *(*start_routine)(void *),
- void *arg)
-{
- int status;
- gc_pt_info *info= (gc_pt_info *)malloc(sizeof(gc_pt_info));
- ENTER(GC_pt_pthread_create);
- if (!info) ERR("malloc");
-
- /* thread mustn't start until we've built the info struct */
- GC_pt_stop_world();
-
- status= pthread_create(thread, attr, GC_pt_starter, (void *)info);
- if (!attr) attr= &pthread_attr_default; /* no stack size? -- use the default */
- /* push the info onto the dequeue */
- info->q.flink= infos.flink;
- info->q.blink= &infos;
- infos.flink->blink= &info->q;
- infos.flink= &info->q;
- /* fill in the blanks */
- info->thread= *thread;
- info->launch= start_routine;
- info->arg= arg;
- info->mark_state= GC_PT_NASCENT; /* thread not yet born */
- /* pthread_create filled in the initial SP -- profitons-en ! */
- info->stack_top= (void *)THREAD_SP(*thread);
-
- /* GC_err_printf("THREAD CREATE: %lx\n", *thread);*/
-
- /* GC_pt_print_threads();*/
-
- PRINT1("*** CREATED THREAD %lx", *thread);
-
- /* we're now ready for the thread to begin */
- GC_pt_start_world();
- RETURN(status);
-}
-
-
-#endif /* MIT_PTHREADS */
/*
* Allocate a new heapblock for small objects of size n.
* Add all of the heapblock's objects to the free list for objects
- * of that size. Will fail to do anything if we are out of memory.
+ * of that size.
+ * Set all mark bits if objects are uncollectable.
+ * Will fail to do anything if we are out of memory.
*/
void GC_new_hblk(sz, kind)
register word sz;
h = GC_allochblk(sz, kind, 0);
if (h == 0) return;
+ /* Mark all objects if appropriate. */
+ if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(HDR(h));
+
/* Handle small objects sizes more efficiently. For larger objects */
/* the difference is less significant. */
# ifndef SMALL_CONFIG
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, February 7, 1996 11:09 am PST */
+/* Boehm, March 8, 1996 12:00 pm PST */
# include "gc_priv.h"
# ifdef LINUX
/* in some later Linux releases, asm/sigcontext.h may have to */
/* be included instead. */
# define __KERNEL__
-# ifndef MIT_PTHREADS
-# include <asm/signal.h>
-# else
-# include <asm/sigcontext.h>
-# endif
+# include <asm/signal.h>
# undef __KERNEL__
# endif
# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS)
# define NEED_FIND_LIMIT
# endif
-# if (defined(SUNOS4) & defined(DYNAMIC_LOADING)) && !defined(PCR)
+# if defined(IRIX_THREADS)
# define NEED_FIND_LIMIT
# endif
-# if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR)
+# if (defined(SUNOS4) & defined(DYNAMIC_LOADING)) && !defined(PCR)
# define NEED_FIND_LIMIT
# endif
-# if defined(MIT_PTHREADS)
+# if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR)
# define NEED_FIND_LIMIT
-# if defined (ALPHA)
- extern void (*signal(int sig, void (*function)(int)))(int);
- /* Get a definition for struct sigcontext, which the pthreads */
- /* headers fail to define. Temporarily define _KERNEL to avoid */
- /* a redefinition of sig_atomic_t. */
-# define _KERNEL
-# include <machine/signal.h>
-# undef _KERNEL
-# endif
- /* Provide the usual definition for signal(), which the pthreads */
- /* headers again fail to define. (This may be an incorrect */
- /* assumption on some architectures!) */
- extern void (*signal(int sig, void (*function)(int)))(int);
# endif
#ifdef NEED_FIND_LIMIT
#ifdef IRIX5
# include <sys/uio.h>
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <malloc.h> /* for locking */
#endif
#ifdef SUNOS5SIGS
# define jmp_buf sigjmp_buf
#endif
+#ifdef DJGPP
+ /* Apparently necessary for djgpp 2.01. May casuse problems with */
+ /* other versions. */
+ typedef caddr_t long unsigned int;
+#endif
+
#ifdef PCR
# include "il/PCR_IL.h"
# include "th/PCR_ThCtl.h"
# include "mm/PCR_MM.h"
#endif
+#if defined(PROT_EXEC) && !defined(NO_EXECUTE_PERMISSION)
+# define OPT_PROT_EXEC PROT_EXEC
+#else
+# define OPT_PROT_EXEC 0
+#endif
+
# ifdef OS2
# include <stddef.h>
# if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
&& !defined(MACOS) && !defined(DJGPP)
-# ifdef sigmask
+# if defined(sigmask) && !defined(UTS4)
/* Use the traditional BSD interface */
# define SIGSET_T int
# define SIG_DEL(set, signal) (set) &= ~(sigmask(signal))
static handler old_segv_handler, old_bus_handler;
# endif
- GC_setup_temporary_fault_handler()
+ void GC_setup_temporary_fault_handler()
{
# ifdef SUNOS5SIGS
struct sigaction act;
# endif
}
- GC_reset_fault_handler()
+ void GC_reset_fault_handler()
{
# ifdef SUNOS5SIGS
(void) sigaction(SIGSEGV, &oldact, 0);
} else {
result -= MIN_PAGE_SIZE;
}
- GC_noop(*result);
+ GC_noop1((word)(*result));
}
}
GC_reset_fault_handler();
(ptr_t)LMGetCurrentA5(), FALSE);
# else
# if defined(__MWERKS__)
- extern long __datastart, __dataend;
- GC_add_roots_inner((ptr_t)&__datastart, (ptr_t)&__dataend, FALSE);
-# endif
-# endif
+# if !__POWERPC__
+ extern void* GC_MacGetDataStart(void);
+ /* globals begin above stack and end at a5. */
+ GC_add_roots_inner((ptr_t)GC_MacGetDataStart(),
+ (ptr_t)LMGetCurrentA5(), FALSE);
+# else
+ extern char __data_start__[], __data_end__[];
+ GC_add_roots_inner((ptr_t)&__data_start__,
+ (ptr_t)&__data_end__, FALSE);
+# endif /* __POWERPC__ */
+# endif /* __MWERKS__ */
+# endif /* !THINK_C */
}
# endif /* MACOS */
# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
&& !defined(MSWIN32) && !defined(MACOS)
-# if !defined(DEC_PTHREADS)
extern caddr_t sbrk();
-# endif
# ifdef __STDC__
-# define SBRK_ARG_T size_t
+# define SBRK_ARG_T ptrdiff_t
# else
# define SBRK_ARG_T int
# endif
SBRK_ARG_T lsbs = (word)cur_brk & (HBLKSIZE-1);
static caddr_t my_brk_val = 0;
+ if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
if (lsbs != 0) {
if(sbrk(HBLKSIZE - lsbs) == (caddr_t)(-1)) return(0);
}
return((ptr_t)result);
}
-#else
+#else /* Not RS6000 */
+
+#if defined(USE_MMAP)
+/* Tested only under IRIX5 */
+
+ptr_t GC_unix_get_mem(bytes)
+word bytes;
+{
+ static bool initialized = FALSE;
+ static int fd;
+ void *result;
+ static ptr_t last_addr = HEAP_START;
+ static size_t page_size;
+
+ if (!initialized) {
+ fd = open("/dev/zero", O_RDONLY);
+ page_size = getpagesize();
+ initialized = TRUE;
+ }
+ result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+ MAP_PRIVATE | MAP_FIXED, fd, 0/* offset */);
+ if (result == MAP_FAILED) return(0);
+ last_addr = (ptr_t)result + bytes + page_size - 1;
+ last_addr = (ptr_t)((word)last_addr & ~(page_size - 1));
+ return((ptr_t)result);
+}
+
+#else /* Not RS6000, USE_MMAP */
ptr_t GC_unix_get_mem(bytes)
word bytes;
{
+ caddr_t result;
+# ifdef IRIX5
+ /* Bare sbrk isn't thread safe. Play by malloc rules. */
+ /* The equivalent may be needed on other systems as well. */
+ __LOCK_MALLOC();
+# endif
+ {
caddr_t cur_brk = sbrk(0);
- caddr_t result;
SBRK_ARG_T lsbs = (word)cur_brk & (HBLKSIZE-1);
+ if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */
if (lsbs != 0) {
if(sbrk(HBLKSIZE - lsbs) == (caddr_t)(-1)) return(0);
}
result = sbrk((SBRK_ARG_T)bytes);
- if (result == (caddr_t)(-1)) return(0);
- return((ptr_t)result);
+ if (result == (caddr_t)(-1)) result = 0;
+ }
+# ifdef IRIX5
+ __UNLOCK_MALLOC();
+# endif
+ return((ptr_t)result);
}
-#endif
-# endif
+#endif /* Not USE_MMAP */
+#endif /* Not RS6000 */
+
+# endif /* UN*X */
# ifdef OS2
# endif /* SRC_M3 */
-# ifdef SOLARIS_THREADS
+# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || defined(IRIX_THREADS)
+
+extern void GC_push_all_stacks();
void GC_default_push_other_roots()
{
GC_push_all_stacks();
}
-# endif /* SOLARIS_THREADS */
-
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-void GC_default_push_other_roots()
-{
- GC_pt_push_all_stacks();
-}
-# endif
+# endif /* SOLARIS_THREADS || ... */
void (*GC_push_other_roots)() = GC_default_push_other_roots;
/* Initialize virtual dirty bit implementation. */
void GC_dirty_init()
{
+ GC_dirty_maintained = TRUE;
}
/* Retrieve system dirty bits for heap to a local buffer. */
# define PROTECT(addr, len) \
if (mprotect((caddr_t)(addr), (int)(len), \
- PROT_READ | PROT_EXEC) < 0) { \
+ PROT_READ | OPT_PROT_EXEC) < 0) { \
ABORT("mprotect failed"); \
}
# define UNPROTECT(addr, len) \
if (mprotect((caddr_t)(addr), (int)(len), \
- PROT_WRITE | PROT_READ | PROT_EXEC) < 0) { \
+ PROT_WRITE | PROT_READ | OPT_PROT_EXEC ) < 0) { \
ABORT("un-mprotect failed"); \
}
for (i = 0; i < GC_n_heap_sects; i++) {
start = (word) GC_heap_sects[i].hs_start;
end = start + (word)GC_heap_sects[i].hs_bytes;
- if (addr < start && addr >= (start & ~mask)
- || addr >= end && addr < ((end + mask) & ~mask)) {
+ if (addr < ((start + mask) & ~mask) && addr >= (start & ~mask)
+ || addr >= (end & ~mask) && addr < ((end + mask) & ~mask)) {
return(TRUE);
}
}
return(FALSE);
}
-#if defined(SUNOS4) && defined(MIT_PTHREADS)
-# include <vm/faultcode.h>
-#endif
-
#if defined(SUNOS4) || defined(FREEBSD)
typedef void (* SIG_PF)();
#endif
-#if defined(SUNOS5SIGS) || defined(ALPHA) /* OSF1 */ || defined(LINUX)
+#if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX)
typedef void (* SIG_PF)(int);
#endif
#if defined(MSWIN32)
# define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1)
#endif
-#if defined(IRIX5) || defined(ALPHA) /* OSF1 */
+#if defined(IRIX5) || defined(OSF1)
typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
#endif
#if defined(SUNOS5SIGS)
# define CODE_OK (code == BUS_PAGE_FAULT)
# endif
# endif
-# if defined(IRIX5) || defined(ALPHA) /* OSF1 */
+# if defined(IRIX5) || defined(OSF1)
# include <errno.h>
void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
# define SIG_OK (sig == SIGSEGV)
-# ifdef ALPHA
+# ifdef OSF1
# define CODE_OK (code == 2 /* experimentally determined */)
# endif
# ifdef IRIX5
# ifdef IRIX5
char * addr = (char *) (scp -> sc_badvaddr);
# endif
-# ifdef ALPHA
+# if defined(OSF1) && defined(ALPHA)
char * addr = (char *) (scp -> sc_traparg_a0);
# endif
# ifdef SUNOS5SIGS
char * addr = (char *) (scp -> si_addr);
# endif
# ifdef LINUX
+# ifdef I386
char * addr = (char *) (sc.cr2);
+# else
+ char * addr = /* As of 1.3.90 there seemed to be no way to do this. */;
+# endif
# endif
# if defined(MSWIN32)
char * addr = (char *) (exc_info -> ExceptionRecord
if (SIG_OK && CODE_OK) {
register struct hblk * h =
(struct hblk *)((word)addr & ~(GC_page_size-1));
+ bool in_allocd_block;
- if (HDR(addr) == 0 && !GC_just_outside_heap((word)addr)) {
+# ifdef SUNOS5SIGS
+ /* Address is only within the correct physical page. */
+ in_allocd_block = FALSE;
+ for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
+ if (HDR(h+i) != 0) {
+ in_allocd_block = TRUE;
+ }
+ }
+# else
+ in_allocd_block = (HDR(addr) != 0);
+# endif
+ if (!in_allocd_block && !GC_just_outside_heap((word)addr)) {
SIG_PF old_handler;
if (sig == SIGSEGV) {
(*(REAL_SIG_PF)old_handler) (sig, sc);
return;
# endif
-# if defined (IRIX5) || defined(ALPHA)
+# if defined (IRIX5) || defined(OSF1)
(*(REAL_SIG_PF)old_handler) (sig, code, scp);
return;
# endif
set_pht_entry_from_index(GC_dirty_pages, index);
}
UNPROTECT(h, GC_page_size);
-# if defined(IRIX5) || defined(ALPHA) || defined(LINUX)
+# if defined(IRIX5) || defined(OSF1) || defined(LINUX)
/* These reset the signal handler each time by default. */
signal(SIGSEGV, (SIG_PF) GC_write_fault_handler);
# endif
return;
# endif
}
-
+#ifdef MSWIN32
+ return EXCEPTION_CONTINUE_SEARCH;
+#else
ABORT("Unexpected bus error or segmentation fault");
+#endif
}
+/*
+ * We hold the allocation lock. We expect block h to be written
+ * shortly.
+ */
void GC_write_hint(h)
struct hblk *h;
{
}
}
-#if defined(SUNOS5) || defined(DRSNX)
+#if defined(SVR4)
#include <unistd.h>
int
GC_getpagesize()
# endif
}
# endif
-# if defined(IRIX5) || defined(ALPHA) || defined(SUNOS4) || defined(LINUX)
+# if defined(IRIX5) || 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!?");
}
}
-# ifdef THREADS
- /* HJB say that this bug manifests itself once in a lifetime, so let's
- take the risk... */
-# if !defined(DEC_PTHREADS) && !defined(MIT_PTHREADS)
---> The following is broken. We can lose dirty bits. We would need
---> the signal handler to cooperate, as in PCR.
-# endif
-# endif
-
+/* We assume that either the world is stopped or its OK to lose dirty */
+/* bits while this is happenning (as in GC_enable_incremental). */
void GC_read_dirty()
{
BCOPY((word *)GC_dirty_pages, GC_grungy_pages,
}
/*
- * If this code needed to be thread-safe, the following would need to
- * acquire and release the allocation lock. This is tricky, since e.g.
- * the cord package issues a read while it already holds the allocation lock.
+ * Acquiring the allocation lock here is dangerous, since this
+ * can be called from within GC_call_with_alloc_lock, and the cord
+ * package does so. On systems that allow nested lock acquisition, this
+ * happens to work.
+ * On other systems, SET_LOCK_HOLDER and friends must be suitably defined.
*/
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-void GC_begin_syscall() { LOCK(); }
-void GC_end_syscall() { UNLOCK(); }
-# else
-# ifdef THREADS
- --> fix this
-# endif
void GC_begin_syscall()
{
+ if (!I_HOLD_LOCK()) LOCK();
}
void GC_end_syscall()
{
+ if (!I_HOLD_LOCK()) UNLOCK();
}
-# endif
void GC_unprotect_range(addr, len)
ptr_t addr;
/* Replacement for UNIX system call. */
/* Other calls that write to the heap */
/* should be handled similarly. */
-# if !defined(LINT)
-# if defined(MIT_PTHREADS)
- /* read() is defined in libpthread.a, so we can't redefine it here. */
- ssize_t GC_read(fd, buf, nbyte)
-# else
-# if defined(__GNUC__)
- ssize_t read(fd, buf, nbyte)
-# else
+# ifndef LINT
int read(fd, buf, nbyte)
-# endif
-# endif
# else
int GC_read(fd, buf, nbyte)
# endif
-# if defined(MIT_PTHREADS) || defined(DEC_PTHREADS)
-int fd;
-void *buf;
-size_t nbyte;
-# else
int fd;
char *buf;
int nbyte;
-# endif
{
int result;
#include "gc_priv.h"
#include "gc_mark.h"
-void GC_default_same_obj_print_proc(p,q)
-ptr_t p, q;
+#ifdef __STDC__
+void GC_default_same_obj_print_proc(GC_PTR p, GC_PTR q)
+#else
+void GC_default_same_obj_print_proc (p, q)
+GC_PTR p, q;
+#endif
{
GC_err_printf2("0x%lx and 0x%lx are not in the same object\n",
(unsigned long)p, (unsigned long)q);
ABORT("GC_same_obj test failed");
}
-void (*GC_same_obj_print_proc)() = GC_default_same_obj_print_proc;
+void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR, GC_PTR))
+ = GC_default_same_obj_print_proc;
/* Check that p and q point to the same object. Call */
/* *GC_same_obj_print_proc if they don't. */
return(p);
}
-
-void GC_default_is_valid_displacement_print_proc(p)
-ptr_t p;
+#ifdef __STDC__
+void GC_default_is_valid_displacement_print_proc (GC_PTR p)
+#else
+void GC_default_is_valid_displacement_print_proc (p)
+GC_PTR p;
+#endif
{
GC_err_printf1("0x%lx does not point to valid object displacement\n",
(unsigned long)p);
ABORT("GC_is_valid_displacement test failed");
}
-void (*GC_is_valid_displacement_print_proc)() =
+void (*GC_is_valid_displacement_print_proc) GC_PROTO((GC_PTR)) =
GC_default_is_valid_displacement_print_proc;
/* Check that if p is a pointer to a heap page, then it points to */
return(p);
}
-
+#ifdef __STDC__
+void GC_default_is_visible_print_proc(GC_PTR p)
+#else
void GC_default_is_visible_print_proc(p)
-ptr_t p;
+GC_PTR p;
+#endif
{
GC_err_printf1("0x%lx is not a GC visible pointer location\n",
(unsigned long)p);
ABORT("GC_is_visible test failed");
}
-void (*GC_is_visible_print_proc)() =
+void (*GC_is_visible_print_proc) GC_PROTO((GC_PTR p)) =
GC_default_is_visible_print_proc;
/* Could p be a stack address? */
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, April 18, 1995 1:59 pm PDT */
+/* Boehm, February 15, 1996 2:41 pm PST */
#include <stdio.h>
#include "gc_priv.h"
register word sz; /* size of objects in current block */
register struct obj_kind * ok;
register ptr_t * flh;
+ register int kind;
hhdr = HDR(hbp);
sz = hhdr -> hb_sz;
hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no;
- ok = &GC_obj_kinds[hhdr -> hb_obj_kind];
+ kind = hhdr -> hb_obj_kind;
+ ok = &GC_obj_kinds[kind];
flh = &(ok -> ok_freelist[sz]);
GC_write_hint(hbp);
break;
}
}
+ if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(hhdr);
}
/*
/* Routines to gather and print heap block info */
/* intended for debugging. Otherwise should be called */
/* with lock. */
-static number_of_blocks;
-static total_bytes;
+static size_t number_of_blocks;
+static size_t total_bytes;
/* Number of set bits in a word. Not performance critical. */
static int set_bits(n)
word dummy;
{
register hdr * hhdr = HDR(h);
- register bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
+ register size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
GC_printf3("(%lu:%lu,%lu)", (unsigned long)(hhdr -> hb_obj_kind),
(unsigned long)bytes,
}
}
-/*
- * Reclaim all blocks that have been recently reclaimed.
- * Clear lists of blocks waiting to be reclaimed.
- * Must be done before clearing mark bits with the world running,
- * since otherwise a subsequent reclamation of block would see
- * the wrong mark bits. (Alternatively, GC_reclaim_all
- * may be used.)
- * SHOULD PROBABLY BE INCREMENTAL
- */
-void GC_reclaim_or_delete_all()
-{
- register word sz;
- register int kind;
- register hdr * hhdr;
- register struct hblk * hbp;
- register struct obj_kind * ok;
- struct hblk ** rlp;
- struct hblk ** rlh;
-# ifdef PRINTTIMES
- CLOCK_TYPE start_time;
- CLOCK_TYPE done_time;
-
- GET_TIME(start_time);
-# endif
-
- for (kind = 0; kind < GC_n_kinds; kind++) {
- ok = &(GC_obj_kinds[kind]);
- rlp = ok -> ok_reclaim_list;
- if (rlp == 0) continue;
- for (sz = 1; sz <= MAXOBJSZ; sz++) {
- rlh = rlp + sz;
- while ((hbp = *rlh) != 0) {
- hhdr = HDR(hbp);
- *rlh = hhdr -> hb_next;
- if (hhdr -> hb_last_reclaimed == GC_gc_no - 1) {
- /* It's likely we'll need it this time, too */
- /* It's been touched recently, so this */
- /* shouldn't trigger paging. */
- GC_reclaim_small_nonempty_block(hbp, FALSE);
- }
- }
- }
- }
-# ifdef PRINTTIMES
- GET_TIME(done_time);
- GC_printf1("Disposing of reclaim lists took %lu msecs\n",
- MS_TIME_DIFF(done_time,start_time));
-# endif
-}
-
/*
* Reclaim all small blocks waiting to be reclaimed.
* Abort and return FALSE when/if (*stop_func)() returns TRUE.
* If this returns TRUE, then it's safe to restart the world
* with incorrectly cleared mark bits.
+ * If ignore_old is TRUE, then reclain only blocks that have been
+ * recently reclaimed, and discard the rest.
+ * Stop_func may be 0.
*/
-bool GC_reclaim_all(stop_func)
+bool GC_reclaim_all(stop_func, ignore_old)
GC_stop_func stop_func;
+bool ignore_old;
{
register word sz;
register int kind;
for (sz = 1; sz <= MAXOBJSZ; sz++) {
rlh = rlp + sz;
while ((hbp = *rlh) != 0) {
- if ((*stop_func)()) return(FALSE);
+ if (stop_func != (GC_stop_func)0 && (*stop_func)()) {
+ return(FALSE);
+ }
hhdr = HDR(hbp);
*rlh = hhdr -> hb_next;
- GC_reclaim_small_nonempty_block(hbp, FALSE);
+ if (!ignore_old || hhdr -> hb_last_reclaimed == GC_gc_no - 1) {
+ /* It's likely we'll need it this time, too */
+ /* It's been touched recently, so this */
+ /* shouldn't trigger paging. */
+ GC_reclaim_small_nonempty_block(hbp, FALSE);
+ }
}
}
}
}
#endif
-#if defined(SUNOS5) || defined(DRSNX)
+#if defined(SVR4)
#include <unistd.h>
int
getpagesize()
if (i >= MAX_LWPS) ABORT("Too many lwps");
}
+bool GC_multithreaded = 0;
void GC_stop_world()
{
preempt_off();
- stop_all_lwps();
+ if (GC_multithreaded)
+ stop_all_lwps();
}
void GC_start_world()
{
- restart_all_lwps();
+ if (GC_multithreaded)
+ restart_all_lwps();
preempt_on();
}
+void GC_thr_init();
+
bool GC_thr_initialized = FALSE;
size_t GC_min_stack_sz;
register struct hblk * h;
int dummy;
- if (!GC_thr_initialized) GC_thr_init();
+ GC_thr_init();
for (i = 0, sz= GC_min_stack_sz; i < N_FREE_LISTS;
i++, sz *= 2) {
for (p = GC_stack_free_lists[i]; p != 0; p = *(ptr_t *)p) {
} else { \
GC_push_all_stack((bottom), (top)); \
}
- if (!GC_thr_initialized) GC_thr_init();
+ GC_thr_init();
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> stack_size != 0) {
UNLOCK();
} else {
t = GC_lookup_thread(departed);
+ GC_multithreaded--;
if (!(t -> flags & CLIENT_OWNS_STACK)) {
GC_stack_free(t -> stack, t -> stack_size);
}
}
/* We hold the allocation lock. */
-GC_thr_init()
+void GC_thr_init()
{
GC_thread t;
+ if (GC_thr_initialized) return;
GC_thr_initialized = TRUE;
GC_min_stack_sz = ((thr_min_stack() + 128*1024 + HBLKSIZE-1)
& ~(HBLKSIZE - 1));
/* Add the initial thread, so we can stop it. */
t = GC_new_thread(thr_self());
t -> stack_size = 0;
- t -> flags = DETACHED;
+ t -> flags = DETACHED | CLIENT_OWNS_STACK;
if (thr_create(0 /* stack */, 0 /* stack_size */, GC_thr_daemon,
0 /* arg */, THR_DETACHED | THR_DAEMON,
0 /* thread_id */) != 0) {
void * stack = stack_base;
LOCK();
- if (!GC_thr_initialized) GC_thr_init();
+ GC_thr_init();
+ GC_multithreaded++;
if (stack == 0) {
if (stack_size == 0) stack_size = GC_min_stack_sz;
stack = (void *)GC_stack_alloc(&stack_size);
t -> stack_size = stack_size;
if (new_thread != 0) *new_thread = my_new_thread;
cond_signal(&GC_create_cv);
- } else if (!(my_flags & CLIENT_OWNS_STACK)) {
- GC_stack_free(stack, stack_size);
+ } else {
+ GC_multithreaded--;
+ if (!(my_flags & CLIENT_OWNS_STACK)) {
+ GC_stack_free(stack, stack_size);
+ }
}
UNLOCK();
return(result);
/*
* 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.
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
-/* Boehm, September 21, 1995 5:43 pm PDT */
/* An incomplete test for the garbage collector. */
/* Some more obscure entry points are not tested at all. */
# include <stdlib.h>
# endif
# include <stdio.h>
+# 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 */
# ifdef PCR
# include "th/PCR_ThCrSec.h"
# include "th/PCR_Th.h"
+# undef GC_printf0
+# define GC_printf0 printf
+# undef GC_printf1
+# define GC_printf1 printf
# endif
# ifdef SOLARIS_THREADS
# include <synch.h>
# endif
-# if defined(PCR) || defined(SOLARIS_THREADS) \
- || defined(MIT_PTHREADS) || defined(DEC_PTHREADS)
+# ifdef IRIX_THREADS
+# include <pthread.h>
+# endif
+
+# ifdef WIN32_THREADS
+# include <process.h>
+ static CRITICAL_SECTION incr_cs;
+# endif
+# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS)
# define THREADS
# endif
# define INT_TO_SEXPR(x) ((sexpr)(unsigned long)(x))
-extern sexpr cons();
-
# undef nil
# define nil (INT_TO_SEXPR(0))
# define car(x) ((x) -> sexpr_car)
{
register sexpr r;
register int *p;
- register my_extra = extra_count;
+ register int my_extra = extra_count;
r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra);
if (r == 0) {
register sexpr r;
r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR));
+assert(GC_is_marked(r));
if (r == 0) {
(void)GC_printf0("Out of memory\n");
exit(1);
}
}
-/* Too check uncollectable allocation we build lists with disguised cdr */
+/* To check uncollectable allocation we build lists with disguised cdr */
/* pointers, and make sure they don't go away. */
sexpr uncollectable_ints(low, up)
int low, up;
sexpr list;
int low, up;
{
+assert(GC_is_marked(list));
if ((int)(GC_word)(car(car(list))) != low) {
(void)GC_printf0(
"Uncollectable list corrupted - collector is broken\n");
}
if (low == up) {
if (UNCOLLECTABLE_CDR(list) != nil) {
- (void)GC_printf0("Uncollectable ist too long - collector is broken\n");
+ (void)GC_printf0("Uncollectable list too long - collector is broken\n");
FAIL;
}
} else {
int finalized_count = 0;
VOLATILE int dropped_something = 0;
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-static pthread_mutex_t incr_lock;
-# endif
-
# ifdef __STDC__
void finalizer(void * obj, void * client_data)
# else
static mutex_t incr_lock;
mutex_lock(&incr_lock);
# endif
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
+# ifdef IRIX_THREADS
+ static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&incr_lock);
+# endif
+# ifdef WIN32_THREADS
+ EnterCriticalSection(&incr_cs);
# endif
if ((int)(GC_word)client_data != t -> level) {
(void)GC_printf0("Wrong finalization data - collector is broken\n");
# ifdef SOLARIS_THREADS
mutex_unlock(&incr_lock);
# endif
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
+# ifdef IRIX_THREADS
pthread_mutex_unlock(&incr_lock);
# endif
+# ifdef WIN32_THREADS
+ LeaveCriticalSection(&incr_cs);
+# endif
}
size_t counter = 0;
static mutex_t incr_lock;
mutex_lock(&incr_lock);
# endif
-# if defined (DEC_PTHREADS) || defined(MIT_PTHREADS)
+# ifdef IRIX_THREADS
+ static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&incr_lock);
+# endif
+# ifdef WIN32_THREADS
+ EnterCriticalSection(&incr_cs);
# endif
/* Losing a count here causes erroneous report of failure. */
finalizable_count++;
# ifdef SOLARIS_THREADS
mutex_unlock(&incr_lock);
# endif
-# if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
- pthread_mutex_unlock(&incr_lock);
+# ifdef IRIX_THREADS
+ pthread_mutex_unlock(&incr_lock);
+# endif
+# ifdef WIN32_THREADS
+ LeaveCriticalSection(&incr_cs);
# endif
}
return(my_free_list);
}
-#elif defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-pthread_key_t fl_key;
-
-void * alloc8bytes()
-{
- void ** my_free_list_ptr;
- void * my_free_list;
-
-# ifdef DEC_PTHREADS
- if (pthread_getspecific(fl_key, (void **)(&my_free_list_ptr)) != 0)
- {
- (void)GC_printf0("pthread_getspecific failed\n");
- FAIL;
- }
-# endif
-# ifdef MIT_PTHREADS
- if (!(my_free_list_ptr= pthread_getspecific(fl_key)))
- {
- /* there's no way to tell if this is an error, so what's to do? */
- }
-# endif
- 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);
-}
-
#else
# define alloc8bytes() GC_MALLOC_ATOMIC(8)
#endif
unsigned n_tests = 0;
+GC_word bm_huge[10] = {
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0x00ffffff,
+};
+
+
/* A very simple test of explicitly typed allocation */
void typed_test()
{
GC_descr dummy = GC_make_descriptor(&bm_large, 32);
# 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);
register int i;
old = 0;
old = new;
new = (GC_word *)(old[1]);
}
+ GC_gcollect();
+ GC_noop(x);
}
int fail_count = 0;
+#ifndef __STDC__
/*ARGSUSED*/
-void fail_proc(x)
-ptr_t x;
+void fail_proc1(x)
+GC_PTR x;
{
fail_count++;
}
-extern void (*GC_is_valid_displacement_print_proc)();
+#else
-extern void (*GC_is_visible_print_proc)();
+/*ARGSUSED*/
+void fail_proc1(GC_PTR x)
+{
+ fail_count++;
+}
+
+#endif /* __STDC__ */
#ifdef THREADS
# define TEST_FAIL_COUNT(n) 1
# ifdef LINT
char *y = 0;
# else
- char *y = (char *)fail_proc;
+ char *y = (char *)(size_t)fail_proc1;
# endif
DCL_LOCK_STATE;
(void)GC_printf0("GC_malloc_uncollectable(0) failed\n");
FAIL;
}
- GC_is_valid_displacement_print_proc = fail_proc;
- GC_is_visible_print_proc = fail_proc;
+ GC_is_valid_displacement_print_proc = fail_proc1;
+ GC_is_visible_print_proc = fail_proc1;
x = GC_malloc(16);
if (GC_base(x + 13) != x) {
(void)GC_printf0("GC_base(heap ptr) produced incorrect result\n");
FAIL;
}
if (!TEST_FAIL_COUNT(1)) {
-# ifndef RS6000
+# if!(defined(RS6000) || defined(POWERPC))
/* ON RS6000s function pointers point to a descriptor in the */
/* data segment, so there should have been no failures. */
(void)GC_printf0("GC_is_visible produced wrong failure indication\n");
FAIL;
}
# ifndef ALL_INTERIOR_POINTERS
-# ifdef RS6000
+# if defined(RS6000) || defined(POWERPC)
if (!TEST_FAIL_COUNT(1)) {
# else
if (!TEST_FAIL_COUNT(2)) {
LOCK();
n_tests++;
UNLOCK();
-
+ /* GC_printf1("Finished %x\n", pthread_self()); */
}
void check_heap_stats()
still_live++;
}
}
- if (still_live != finalizable_count - finalized_count) {
- (void)GC_printf1
- ("%lu disappearing links remain - disappearing links are broken\n",
- (unsigned long) still_live);
- FAIL;
+ 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",
+ (unsigned long) still_live, (unsigned long)i);
+ if (i > 10) {
+ GC_printf0("\tVery suspicious!\n");
+ } else {
+ GC_printf0("\tSlightly suspicious, but probably OK.\n");
+ }
}
(void)GC_printf1("Total number of bytes allocated is %lu\n",
(unsigned long)
}
-#if !defined(PCR) && !defined(SOLARIS_THREADS) \
- && !defined(DEC_PTHREADS) && !defined(MIT_PTHREADS) \
- || defined(LINT)
-
+#if !defined(PCR) && !defined(SOLARIS_THREADS) && !defined(WIN32_THREADS) && !defined(IRIX_THREADS) || defined(LINT)
#ifdef MSWIN32
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n)
#else
}
# endif
+#ifdef WIN32_THREADS
+
+unsigned __stdcall thr_run_one_test(void *arg)
+{
+ run_one_test();
+ return 0;
+}
+
+#define NTEST 2
+
+int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n)
+{
+# if NTEST > 0
+ HANDLE h[NTEST];
+# endif
+ int i;
+ unsigned thread_id;
+# if 0
+ GC_enable_incremental();
+# endif
+ InitializeCriticalSection(&incr_cs);
+ (void) GC_set_warn_proc(warn_proc);
+ for (i = 0; i < NTEST; i++) {
+ h[i] = (HANDLE)_beginthreadex(NULL, 0, thr_run_one_test, 0, 0, &thread_id);
+ if (h[i] == (HANDLE)-1) {
+ (void)GC_printf1("Thread creation failed %lu\n", (unsigned long)GetLastError());
+ FAIL;
+ }
+ }
+ run_one_test();
+ for (i = 0; i < NTEST; i++)
+ if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) {
+ (void)GC_printf1("Thread wait failed %lu\n", (unsigned long)GetLastError());
+ FAIL;
+ }
+ check_heap_stats();
+ (void)fflush(stdout);
+ return(0);
+}
+
+#endif /* WIN32_THREADS */
+
+
#ifdef PCR
test()
{
}
#endif
-#if defined(SOLARIS_THREADS) || defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
+#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS)
void * thr_run_one_test(void * arg)
{
run_one_test();
return(0);
}
-# ifdef GC_DEBUG
+#ifdef GC_DEBUG
# define GC_free GC_debug_free
-# endif
-
#endif
#ifdef SOLARIS_THREADS
-
main()
{
thread_t th1;
(void)fflush(stdout);
return(0);
}
-#endif
-
-#if defined(DEC_PTHREADS) || defined(MIT_PTHREADS)
-
+#else /* pthreads */
main()
{
pthread_t th1;
pthread_t th2;
+ pthread_attr_t attr;
int code;
- pthread_attr_t thr_attr;
- void *status;
-
-# ifdef DEC_PTHREADS
- pthread_mutex_init(&incr_lock, pthread_mutexattr_default);
- pthread_mutexattr_create(&thr_attr);
-# else
- pthread_mutex_init(&incr_lock, NULL);
- pthread_attr_init(&thr_attr);
-# endif
- pthread_attr_setstacksize(&thr_attr, (long)1024*1024);
+# 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 */
+# endif /* IRIX_THREADS */
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1000000);
n_tests = 0;
- GC_init(); /* Only needed if gc is dynamic library. */
- /*GC_enable_incremental();*/
+ GC_enable_incremental();
(void) GC_set_warn_proc(warn_proc);
-# ifdef DEC_PTHREADS
- if (pthread_keycreate(&fl_key, GC_free) != 0)
-# else
- if (pthread_key_create(&fl_key, GC_free) != 0)
-# endif
- {
- (void)GC_printf1("Key creation failed %lu\n", (long)code);
- FAIL;
- }
-# ifdef DEC_PTHREADS
- if ((code = pthread_create(&th1, thr_attr, thr_run_one_test, 0)) != 0) {
-# else
- if ((code = pthread_create(&th1, &thr_attr, thr_run_one_test, 0)) != 0) {
-# endif
+ 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;
}
-# ifdef DEC_PTHREADS
- if ((code = pthread_create(&th2, thr_attr, thr_run_one_test, 0)) != 0) {
-# else
- if ((code = pthread_create(&th2, &thr_attr, thr_run_one_test, 0)) != 0) {
-# endif
+ if ((code = pthread_create(&th2, &attr, thr_run_one_test, 0)) != 0) {
(void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code);
FAIL;
}
run_one_test();
- if ((code = pthread_join(th1, &status)) != 0) {
+ if ((code = pthread_join(th1, 0)) != 0) {
(void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code);
FAIL;
}
- if ((code = pthread_join(th2, &status)) != 0) {
+ if (pthread_join(th2, 0) != 0) {
(void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code);
FAIL;
}
check_heap_stats();
(void)fflush(stdout);
+ pthread_attr_destroy(&attr);
+ GC_printf1("Completed %d collections\n", GC_gc_no);
return(0);
}
-#endif
+#endif /* pthreads */
+#endif /* SOLARIS_THREADS || IRIX_THREADS */
#include "gc_cpp.h"
#include <stdio.h>
#include <stdlib.h>
+#ifndef __GNUC__
+# include "gc_alloc.h"
+#endif
extern "C" {
#include "gc_priv.h"
}
static void CleanUp( void* obj, void* data ) {
D* self = (D*) obj;
nFreed++;
- my_assert( self->i == (int) data );}
+ my_assert( self->i == (int) (long) data );}
static void Test() {
my_assert( nFreed >= .8 * nAllocated );}
#endif
int i, iters, n;
+# ifndef __GNUC__
+ int *x = (int *)alloc::allocate(sizeof(int));
+ *x = 29;
+ x -= 3;
+# endif
if (argc != 2 || (0 >= (n = atoi( argv[ 1 ] )))) {
GC_printf0( "usage: test_cpp number-of-iterations\n" );
exit( 1 );}
D::Test();
F::Test();}
+# ifndef __GNUC__
+ my_assert (29 == x[3]);
+# endif
GC_printf0( "The test appears to have succeeded.\n" );
return( 0 );}
--- /dev/null
+# include "config.h"
+# include <stdio.h>
+
+int main()
+{
+# ifdef IRIX_THREADS
+ printf("-lpthread\n");
+# endif
+# ifdef SOLARIS_THREADS
+ printf("-lthread -ldl\n");
+# endif
+ return 0;
+}
+
+ GC_descr_obj_size(d -> sd.sd_second));
default:
ABORT("Bad complex descriptor");
- /*NOTREACHED*/
+ /*NOTREACHED*/ return 0; /*NOTREACHED*/
}
}
}
default:
ABORT("Bad complex descriptor");
- /*NOTREACHED*/
- }
+ /*NOTREACHED*/ return 0; /*NOTREACHED*/
+ }
}
/*ARGSUSED*/
if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) {
FASTUNLOCK();
op = (ptr_t)GENERAL_MALLOC((word)lb, GC_explicit_kind);
+ if (0 == op) return(0);
# ifdef MERGE_SIZES
lw = GC_size_map[lb]; /* May have been uninitialized. */
# endif
}
} else {
op = (ptr_t)GENERAL_MALLOC((word)lb, GC_explicit_kind);
+ if (0 == op) return(0);
lw = BYTES_TO_WORDS(GC_size(op));
}
((word *)op)[lw - 1] = d;
if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) {
FASTUNLOCK();
op = (ptr_t)GENERAL_MALLOC((word)lb, GC_array_kind);
+ if (0 == op) return(0);
# ifdef MERGE_SIZES
lw = GC_size_map[lb]; /* May have been uninitialized. */
# endif
}
} else {
op = (ptr_t)GENERAL_MALLOC((word)lb, GC_array_kind);
+ if (0 == op) return(0);
lw = BYTES_TO_WORDS(GC_size(op));
}
if (descr_type == LEAF) {
((word *)op+lw-1),
(GC_PTR) op);
if (ff != GC_finalization_failures) {
- /* We may have failed to register op due to lack of memory. */
- /* We were out of memory very recently, so we can safely */
- /* punt. */
- ((word *)op)[lw - 1] = 0;
- return(0);
+ /* Couldn't register it due to lack of memory. Punt. */
+ /* This will probably fail too, but gives the recovery code */
+ /* a chance. */
+ return(GC_malloc(n*lb));
}
}
return((GC_PTR) op);
--- /dev/null
+#ifdef WIN32_THREADS
+
+#include "gc_priv.h"
+
+#define STRICT
+#include <windows.h>
+
+#define MAX_THREADS 64
+
+struct thread_entry {
+ DWORD id;
+ HANDLE handle;
+ void *stack; /* The cold end of the stack. */
+ CONTEXT context;
+};
+
+struct thread_entry thread_table[MAX_THREADS];
+
+void GC_stop_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 (SuspendThread(thread_table[i].handle) == (DWORD)-1)
+ ABORT("SuspendThread failed");
+ }
+}
+
+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 (ResumeThread(thread_table[i].handle) == (DWORD)-1)
+ ABORT("ResumeThread failed");
+ }
+}
+
+ptr_t GC_current_stackbottom()
+{
+ DWORD thread_id = GetCurrentThreadId();
+ int i;
+ for (i = 0; i < MAX_THREADS; i++)
+ if (thread_table[i].stack && thread_table[i].id == thread_id)
+ return thread_table[i].stack;
+ ABORT("no thread table entry for current thread");
+}
+
+ptr_t GC_get_lo_stack_addr(ptr_t s)
+{
+ ptr_t bottom;
+ MEMORY_BASIC_INFORMATION info;
+ VirtualQuery(s, &info, sizeof(info));
+ do {
+ bottom = info.BaseAddress;
+ VirtualQuery(bottom - 1, &info, sizeof(info));
+ } while ((info.Protect & PAGE_READWRITE) && !(info.Protect & PAGE_GUARD));
+ return(bottom);
+}
+
+void GC_push_all_stacks()
+{
+ DWORD thread_id = GetCurrentThreadId();
+ int i;
+ for (i = 0; i < MAX_THREADS; i++)
+ if (thread_table[i].stack) {
+ ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
+ if (thread_table[i].id == thread_id)
+ GC_push_all(&i, thread_table[i].stack);
+ else {
+ thread_table[i].context.ContextFlags
+ = (CONTEXT_INTEGER|CONTEXT_CONTROL);
+ if (!GetThreadContext(thread_table[i].handle,
+ &thread_table[i].context))
+ ABORT("GetThreadContext failed");
+ if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
+ || thread_table[i].context.Esp < (DWORD)bottom)
+ ABORT("Thread stack pointer out of range");
+ GC_push_all_stack(thread_table[i].context.Esp, thread_table[i].stack);
+ }
+ }
+}
+
+void GC_get_next_stack(char *start, char **lo, char **hi)
+{
+ int i;
+# define ADDR_LIMIT (char *)(-1L)
+ char * current_min = ADDR_LIMIT;
+
+ for (i = 0; i < MAX_THREADS; i++) {
+ char * s = (char *)thread_table[i].stack;
+
+ if (0 != s && s > start && s < current_min) {
+ current_min = s;
+ }
+ }
+ *hi = current_min;
+ if (current_min == ADDR_LIMIT) {
+ *lo = ADDR_LIMIT;
+ return;
+ }
+ *lo = GC_get_lo_stack_addr(current_min);
+ if (*lo < start) *lo = start;
+}
+
+LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
+
+/*
+ * This isn't generally safe, since DllMain is not premptible.
+ * If another thread holds the lock while this runs we're in trouble.
+ * Pontus Rydin suggests wrapping the thread start routine instead.
+ */
+BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
+{
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ InitializeCriticalSection(&GC_allocate_ml);
+ /* fall through */
+ case DLL_THREAD_ATTACH:
+ {
+ int i;
+ LOCK();
+ /* 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++) {
+ 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(),
+ GetCurrentProcess(),
+ &thread_table[i].handle,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS)) {
+ DWORD last_error = GetLastError();
+ GC_printf1("Last error code: %lx\n", last_error);
+ ABORT("DuplicateHandle failed");
+ }
+ UNLOCK();
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ case DLL_THREAD_DETACH:
+ {
+ int i;
+ DWORD thread_id = GetCurrentThreadId();
+ LOCK();
+ 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;
+ CloseHandle(thread_table[i].handle);
+ BZERO(&thread_table[i].context, sizeof(CONTEXT));
+ UNLOCK();
+ }
+ break;
+ }
+ return TRUE;
+}
+
+#endif /* WIN32_THREADS */