diff options
72 files changed, 5096 insertions, 4952 deletions
diff --git a/boehm-gc/BCC_MAKEFILE b/boehm-gc/BCC_MAKEFILE index a8e06827ebc..3f86ed56fc4 100644 --- a/boehm-gc/BCC_MAKEFILE +++ b/boehm-gc/BCC_MAKEFILE @@ -1,29 +1,31 @@ -# Makefile for Borland C++ 4.5 on NT
-# For Borland 5.0, replace bc45 by bc5.
+# Makefile for Borland C++ 5.5 on NT
# If you have the Borland assembler, remove "-DUSE_GENERIC"
#
-bc= c:\bc45
-bcbin= $(bc)\bin
-bclib= $(bc)\lib
+bc= c:\Borland\BCC55
+bcbin= $(bc)\bin
+bclib= $(bc)\lib
bcinclude= $(bc)\include
-cc= $(bcbin)\bcc32
-rc= $(bcbin)\brc32
-lib= $(bcbin)\tlib
-link= $(bcbin)\tlink32
-cflags= -R -v -vi -H -H=gc.csm -I$(bcinclude);cord -L$(bclib) \
- -w-pro -w-aus -w-par -w-ccc -w-rch -a4 -D__STDC__=0
+gcinclude1 = $(bc)\gc6.2\include
+gcinclude2 = $(bc)\gc6.2\cord
+
+cc= $(bcbin)\bcc32
+rc= $(bcbin)\brc32
+lib= $(bcbin)\tlib
+link= $(bcbin)\ilink32
+cflags= -O2 -R -v- -vi -H -H=gc.csm -I$(bcinclude);$(gcinclude1);$(gcinclude2) -L$(bclib) \
+ -w-pro -w-aus -w-par -w-ccc -w-rch -a4 -D__STDC__=0
#defines= -DSILENT
-defines= -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS -DUSE_GENERIC
+defines= -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS -DUSE_GENERIC -DNO_GETENV -DJAVA_FINALIZATION -DGC_OPERATOR_NEW_ARRAY
.c.obj:
$(cc) @&&|
- $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.c
+ $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.c
|
.cpp.obj:
$(cc) @&&|
- $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.cpp
+ $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.cpp
|
.rc.res:
@@ -39,31 +41,31 @@ OBJS= $(XXXOBJS:XXX=) all: gctest.exe cord\de.exe test_cpp.exe
-$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h gcconfig.h MAKEFILE
+$(OBJS) test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h MAKEFILE
gc.lib: $(OBJS)
- -del gc.lib
- tlib $* @&&|
- $(XXXOBJS:XXX=+)
+ del gc.lib
+ $(lib) $* @&&|
+ $(XXXOBJS:XXX=+)
|
gctest.exe: tests\test.obj gc.lib
$(cc) @&&|
- $(cflags) -W -e$* tests\test.obj gc.lib
+ $(cflags) -W -e$* tests\test.obj gc.lib
|
-cord\de.obj cord\de_win.obj: cord\cord.h cord\private\cord_pos.h cord\de_win.h \
+cord\de.obj cord\de_win.obj: include\cord.h include\private\cord_pos.h cord\de_win.h \
cord\de_cmds.h
cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj \
- cord\de_win.res gc.lib
+ cord\de_win.res gc.lib
$(cc) @&&|
- $(cflags) -W -e$* cord\cordbscs.obj cord\cordxtra.obj \
- cord\de.obj cord\de_win.obj gc.lib
+ $(cflags) -W -e$* cord\cordbscs.obj cord\cordxtra.obj \
+ cord\de.obj cord\de_win.obj gc.lib
|
$(rc) cord\de_win.res cord\de.exe
-gc_cpp.obj: gc_cpp.h gc.h
+gc_cpp.obj: include\gc_cpp.h include\gc.h
gc_cpp.cpp: gc_cpp.cc
copy gc_cpp.cc gc_cpp.cpp
@@ -71,12 +73,16 @@ gc_cpp.cpp: gc_cpp.cc test_cpp.cpp: tests\test_cpp.cc
copy tests\test_cpp.cc test_cpp.cpp
-test_cpp.exe: test_cpp.obj gc_cpp.h gc.h gc.lib
+test_cpp.exe: test_cpp.obj include\gc_cpp.h include\gc.h gc.lib
$(cc) @&&|
- $(cflags) -W -e$* test_cpp.obj gc.lib
+ $(cflags) -W -e$* test_cpp.obj gc.lib
|
scratch:
-del *.obj *.res *.exe *.csm cord\*.obj cord\*.res cord\*.exe cord\*.csm
+clean:
+ del gc.lib
+ del *.obj
+ del tests\test.obj
diff --git a/boehm-gc/ChangeLog b/boehm-gc/ChangeLog index 91acf278de0..afb1ce9fc08 100644 --- a/boehm-gc/ChangeLog +++ b/boehm-gc/ChangeLog @@ -1,3 +1,88 @@ +2003-07-28 Jeff Sturm <jsturm@one-point.com> + + Import GC 6.3alpha1. + * BCC_MAKEFILE: Merge with GC 6.3alpha1 release. + * ChangeLog: Likewise. + * Makefile.am: Likewise. + * Makefile.direct: Likewise. + * Makefile.dj: Likewise. + * allchblk.c: Likewise. + * alloc.c: Likewise. + * backgraph.c: Likewise. + * configure.host: Likewise. + * configure.in: Likewise. + * dbg_mlc.c: Likewise. + * dyn_load.c: Likewise. + * finalize.c: Likewise. + * gc_cpp.cc: Likewise. + * gc_dlopen.c: Likewise. + * gcj_mlc.c: Likewise. + * if_mach.c: Likewise. + * mach_dep.c: Likewise. + * malloc.c: Likewise. + * mallocx.c: Likewise. + * mark.c: Likewise. + * mark_rts.c: Likewise. + * misc.c: Likewise. + * os_dep.c: Likewise. + * ptr_chck.c: Likewise. + * reclaim.c: Likewise. + * solaris_pthreads.c: Likewise. + * solaris_threads.c: Likewise. + * sparc_mach_dep.S: Likewise. + * threadlibs.c: Likewise. + * typd_mlc.c: Likewise. + * version.h: Likewise. + * win32_threads.c: Likewise. + * Mac_files/MacOS_Test_config.h: Likewise. + * Mac_files/MacOS_config.h: Likewise. + * cord/cordbscs.c: Likewise. + * cord/cordprnt.c: Likewise. + * cord/de_win.c: Likewise. + * doc/README: Likewise. + * doc/README.MacOSX: Likewise. + * doc/README.changes: Likewise. + * doc/README.environment: Likewise. + * doc/README.ews4800: Likewise. + * doc/README.linux: Likewise. + * doc/README.macros: Likewise. + * doc/README.win32: Likewise. + * doc/debugging.html: Likewise. + * doc/gcdescr.html: Likewise. + * doc/tree.html: Likewise. + * include/Makefile.in: Likewise. + * include/gc.h: Likewise. + * include/gc_cpp.h: Likewise. + * include/gc_local_alloc.h: Likewise. + * include/gc_mark.h: Likewise. + * include/gc_pthread_redirects.h: Likewise. + * include/gc_typed.h: Likewise. + * include/new_gc_alloc.h: Likewise. + * include/private/dbg_mlc.h: Likewise. + * include/private/gc_hdrs.h: Likewise. + * include/private/gc_locks.h: Likewise. + * include/private/gc_pmark.h: Likewise. + * include/private/gc_priv.h: Likewise. + * include/private/gcconfig.h: Likewise. + * include/private/solaris_threads.h: Likewise. + * include/private/specific.h: Likewise. + * tests/test.c: Likewise. + * tests/test_cpp.cc: Likewise. + + * configure: Rebuild. + * Makefile.in: Rebuild. + + * mips_sgi_mach_dep.s: Add. + + * alpha_mach_dep.s: Remove. + * irix_threads.c: Remove. + * linux_threads.c: Remove. + * mips_sgi_mach_dep.S: Remove. + * missing: Remove. + * powerpc_macosx_mach_dep.s: Remove. + * doc/Makefile.am: Remove. + * doc/Makefile.in: Remove. + 2003-07-25 Roger Sayle <roger@eyesopen.com> * configure.host: Only use +ESdbgasm when using the HPUX native diff --git a/boehm-gc/Mac_files/MacOS_Test_config.h b/boehm-gc/Mac_files/MacOS_Test_config.h index c95f4bb2f3e..4e5d2527788 100644 --- a/boehm-gc/Mac_files/MacOS_Test_config.h +++ b/boehm-gc/Mac_files/MacOS_Test_config.h @@ -74,7 +74,7 @@ // 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. -// -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +// -DGC_OPERATOR_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. diff --git a/boehm-gc/Mac_files/MacOS_config.h b/boehm-gc/Mac_files/MacOS_config.h index 93c3c97a955..407bdf154a2 100644 --- a/boehm-gc/Mac_files/MacOS_config.h +++ b/boehm-gc/Mac_files/MacOS_config.h @@ -72,7 +72,7 @@ // 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. -// -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +// -DGC_OPERATOR_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. @@ -86,4 +86,4 @@ // since some ports use malloc or calloc to obtain system memory. // (Probably works for UNIX, and win32.) // -DNO_DEBUG removes GC_dump and the debugging routines it calls. -// Reduces code size slightly at the expense of debuggability.
\ No newline at end of file +// Reduces code size slightly at the expense of debuggability. diff --git a/boehm-gc/Makefile.am b/boehm-gc/Makefile.am index 4c22a0f3dac..717af6e4ac1 100644 --- a/boehm-gc/Makefile.am +++ b/boehm-gc/Makefile.am @@ -18,15 +18,23 @@ MULTICLEAN = true noinst_LTLIBRARIES = libgcjgc.la libgcjgc_convenience.la +if POWERPC_DARWIN +asm_libgc_sources = powerpc_darwin_mach_dep.s +else +asm_libgc_sources = +endif + GC_SOURCES = allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \ -dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c irix_threads.c \ -linux_threads.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \ +dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c aix_irix_threads.c \ +malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \ obj_map.c os_dep.c pcr_interface.c ptr_chck.c real_malloc.c reclaim.c \ solaris_pthreads.c solaris_threads.c specific.c stubborn.c typd_mlc.c \ -backgraph.c win32_threads.c +backgraph.c win32_threads.c \ +pthread_support.c pthread_stop_world.c darwin_stop_world.c \ +$(asm_libgc_sources) -EXTRA_GC_SOURCES = alpha_mach_dep.s \ -mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \ +EXTRA_GC_SOURCES = alpha_mach_dep.S \ +mips_sgi_mach_dep.s mips_ultrix_mach_dep.s powerpc_darwin_mach_dep.s \ rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \ sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s @@ -63,7 +71,9 @@ TESTS = gctest ## FIXME: relies on internal code generated by automake. all_objs = @addobjs@ $(libgcjgc_la_OBJECTS) $(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \ -include/private/gc_hdrs.h include/gc.h include/gc_gcj.h include/gc_mark.h +include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \ +include/gc_pthread_redirects.h include/gc_config_macros.h \ +include/gc_mark.h @addincludes@ ## FIXME: we shouldn't have to do this, but automake forces us to. .s.lo: diff --git a/boehm-gc/Makefile.direct b/boehm-gc/Makefile.direct index af6192ebc45..a884ee5e0bf 100644 --- a/boehm-gc/Makefile.direct +++ b/boehm-gc/Makefile.direct @@ -27,8 +27,11 @@ CFLAGS= -O -I$(srcdir)/include -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DNO_EXECUTE_ # To build the parallel collector on Linux, add to the above: # -DGC_LINUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC -# To build the parallel collector n a static library on HP/UX, add to the above: +# To build the parallel collector in a static library on HP/UX, +# add to the above: # -DGC_HPUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC -DUSE_HPUX_TLS -D_POSIX_C_SOURCE=199506L +# To build the thread-safe collector on Tru64, add to the above: +# -pthread -DGC_OSF1_THREADS # HOSTCC and HOSTCFLAGS are used to build executables that will be run as # part of the build process, i.e. on the build machine. These will usually @@ -60,6 +63,16 @@ HOSTCFLAGS=$(CFLAGS) # Also requires -D_REENTRANT or -D_POSIX_C_SOURCE=199506L. See README.hp. # -DGC_LINUX_THREADS enables support for Xavier Leroy's Linux threads. # see README.linux. -D_REENTRANT may also be required. +# -DGC_OSF1_THREADS enables support for Tru64 pthreads. Untested. +# -DGC_FREEBSD_THREADS enables support for FreeBSD pthreads. Untested. +# Appeared to run into some underlying thread problems. +# -DGC_DARWIN_THREADS enables support for Mac OS X pthreads. Untested. +# -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads. +# See README.DGUX386. +# -DGC_WIN32_THREADS enables support for win32 threads. That makes sense +# for this Makefile only under Cygwin. +# -DGC_THREADS should set the appropriate one of the above macros. +# It assumes pthreads for Solaris. # -DALL_INTERIOR_POINTERS allows all pointers to the interior # of objects to be recognized. (See gc_priv.h for consequences.) # Alternatively, GC_all_interior_pointers can be set at process @@ -93,13 +106,15 @@ HOSTCFLAGS=$(CFLAGS) # See gc_cpp.h for details. No effect on the C part of the collector. # This is defined implicitly in a few environments. Must also be defined # by clients that use gc_cpp.h. -# -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be -# defined as aliases for X, GC_realloc, and GC_free, respectively. +# -DREDIRECT_MALLOC=X causes malloc to be defined as alias for X. +# Unless the following macros are defined, realloc is also redirected +# to GC_realloc, and free is redirected to GC_free. # Calloc and strdup are redefined in terms of the new malloc. X should # be either GC_malloc or GC_malloc_uncollectable, or # GC_debug_malloc_replacement. (The latter invokes GC_debug_malloc # with dummy source location information, but still results in -# properly remembered call stacks on Linux/X86 and Solaris/SPARC.) +# properly remembered call stacks on Linux/X86 and Solaris/SPARC. +# It requires that the following two macros also be used.) # 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, @@ -111,6 +126,9 @@ HOSTCFLAGS=$(CFLAGS) # The canonical use is -DREDIRECT_REALLOC=GC_debug_realloc_replacement, # together with -DREDIRECT_MALLOC=GC_debug_malloc_replacement to # generate leak reports with call stacks for both malloc and realloc. +# This also requires the following: +# -DREDIRECT_FREE=X causes free to be redirected to X. The +# canonical use is -DREDIRECT_FREE=GC_debug_free. # -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. @@ -197,8 +215,11 @@ HOSTCFLAGS=$(CFLAGS) # 15% or so. # -DUSE_3DNOW_PREFETCH causes the collector to issue AMD 3DNow style # prefetch instructions. Same restrictions as USE_I686_PREFETCH. -# UNTESTED!! -# -DGC_USE_LD_WRAP in combination with the gld flags listed in README.linux +# Minimally tested. Didn't appear to be an obvious win on a K6-2/500. +# -DUSE_PPC_PREFETCH causes the collector to issue PowerPC style +# prefetch instructions. No effect except on PowerPC OS X platforms. +# Performance impact untested. +# -DGC_USE_LD_WRAP in combination with the old flags listed in README.linux # causes the collector some system and pthread calls in a more transparent # fashion than the usual macro-based approach. Requires GNU ld, and # currently probably works only with Linux. @@ -226,6 +247,24 @@ HOSTCFLAGS=$(CFLAGS) # -DSTUBBORN_ALLOC allows allocation of "hard to change" objects, and thus # makes incremental collection easier. Was enabled by default until 6.0. # Rarely used, to my knowledge. +# -DHANDLE_FORK attempts to make GC_malloc() work in a child process fork()ed +# from a multithreaded parent. Currently only supported by pthread_support.c. +# (Similar code should work on Solaris or Irix, but it hasn't been tried.) +# -DTEST_WITH_SYSTEM_MALLOC causes gctest to allocate (and leak) large chunks +# of memory with the standard system malloc. This will cause the root +# set and collected heap to grow significantly if malloced memory is +# somehow getting traced by the collector. This has no impact on the +# generated library; it only affects the test. +# -DPOINTER_MASK=0x... causes candidate pointers to be ANDed with the +# given mask before being considered. If either this or the following +# macro is defined, it will be assumed that all pointers stored in +# the heap need to be processed this way. Stack and register pointers +# will be considered both with and without processing. +# These macros are normally needed only to support systems that use +# high-order pointer tags. EXPERIMENTAL. +# -DPOINTER_SHIFT=n causes the collector to left shift candidate pointers +# by the indicated amount before trying to interpret them. Applied +# after POINTER_MASK. EXPERIMENTAL. See also the preceding macro. # CXXFLAGS= $(CFLAGS) @@ -233,15 +272,15 @@ AR= ar RANLIB= ranlib -OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.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 aix_irix_threads.o pthread_support.o pthread_stop_world.o darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.o win32_threads.o -CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.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 aix_irix_threads.c pthread_support.c pthread_stop_world.c darwin_stop_world.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.c win32_threads.c CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c include/cord.h include/ec.h include/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_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o -SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ +SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.S \ sparc_mach_dep.S include/gc.h include/gc_typed.h \ include/private/gc_hdrs.h include/private/gc_priv.h \ include/private/gcconfig.h include/private/gc_pmark.h \ @@ -249,14 +288,17 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ threadlibs.c if_mach.c if_not_there.c gc_cpp.cc include/gc_cpp.h \ gcname.c include/weakpointer.h include/private/gc_locks.h \ gcc_support.c mips_ultrix_mach_dep.s include/gc_alloc.h \ - include/new_gc_alloc.h include/javaxfc.h sparc_sunos4_mach_dep.s \ - sparc_netbsd_mach_dep.s \ + include/new_gc_alloc.h include/gc_allocator.h \ + include/javaxfc.h sparc_sunos4_mach_dep.s sparc_netbsd_mach_dep.s \ include/private/solaris_threads.h include/gc_backptr.h \ hpux_test_and_clear.s include/gc_gcj.h \ include/gc_local_alloc.h include/private/dbg_mlc.h \ - include/private/specific.h powerpc_macosx_mach_dep.s \ + include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h include/gc_amiga_redirects.h \ - include/gc_pthread_redirects.h $(CORD_SRCS) + include/gc_pthread_redirects.h ia64_save_regs_in_stack.s \ + include/gc_config_macros.h include/private/pthread_support.h \ + include/private/pthread_stop_world.h include/private/darwin_semaphore.h \ + include/private/darwin_stop_world.h $(CORD_SRCS) DOC_FILES= README.QUICK doc/README.Mac doc/README.MacOSX doc/README.OS2 \ doc/README.amiga doc/README.cords doc/debugging.html \ @@ -265,15 +307,19 @@ DOC_FILES= README.QUICK doc/README.Mac doc/README.MacOSX doc/README.OS2 \ doc/README.win32 doc/barrett_diagram doc/README \ doc/README.contributors doc/README.changes doc/gc.man \ doc/README.environment doc/tree.html doc/gcdescr.html \ - doc/README.autoconf doc/README.macros doc/README.ews4800 + doc/README.autoconf doc/README.macros doc/README.ews4800 \ + doc/README.DGUX386 doc/README.arm.cross doc/leak.html \ + doc/scale.html doc/gcinterface.html doc/README.darwin TESTS= tests/test.c tests/test_cpp.cc tests/trace_test.c \ tests/leak_test.c tests/thread_leak_test.c GNU_BUILD_FILES= configure.in Makefile.am configure acinclude.m4 \ libtool.m4 install-sh configure.host Makefile.in \ - aclocal.m4 config.sub config.guess ltconfig \ - ltmain.sh mkinstalldirs + ltconfig aclocal.m4 config.sub config.guess \ + include/Makefile.am include/Makefile.in \ + doc/Makefile.am doc/Makefile.in \ + ltmain.sh mkinstalldirs depcomp missing OTHER_MAKEFILES= OS2_MAKEFILE NT_MAKEFILE NT_THREADS_MAKEFILE gc.mak \ BCC_MAKEFILE EMX_MAKEFILE WCC_MAKEFILE Makefile.dj \ @@ -285,7 +331,7 @@ OTHER_FILES= Makefile setjmp_t.c callprocs pc_excludes \ MacProjects.sit.hqx MacOS.c \ Mac_files/datastart.c Mac_files/dataend.c \ Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ - add_gc_prefix.c gc_cpp.cpp win32_threads.c \ + add_gc_prefix.c gc_cpp.cpp \ version.h AmigaOS.c \ $(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES) @@ -330,16 +376,16 @@ mach_dep.o $(SRCS) $(OBJS) tests/test.o dyn_load.o dyn_load_sunos53.o: \ $(srcdir)/include/private/gc_priv.h \ $(srcdir)/include/private/gc_hdrs.h $(srcdir)/include/private/gc_locks.h \ - $(srcdir)/include/gc.h \ + $(srcdir)/include/gc.h $(srcdir)/include/gc_pthread_redirects.h \ $(srcdir)/include/private/gcconfig.h $(srcdir)/include/gc_typed.h \ - Makefile + $(srcdir)/include/gc_config_macros.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 ptr_chck.o: $(srcdir)/include/gc_mark.h $(srcdir)/include/private/gc_pmark.h -specific.o linux_threads.o: $(srcdir)/include/private/specific.h +specific.o pthread_support.o: $(srcdir)/include/private/specific.h solaris_threads.o solaris_pthreads.o: $(srcdir)/include/private/solaris_threads.h @@ -434,17 +480,18 @@ liblinuxgc.so: $(OBJS) dyn_load.o # gcc -shared -Wl,-soname=libgc.so.0 -o libgc.so.0 $(LIBOBJS) dyn_load.lo # touch liblinuxgc.so -mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s $(srcdir)/mips_ultrix_mach_dep.s \ - $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_macosx_mach_dep.s $(UTILS) +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s \ + $(srcdir)/mips_ultrix_mach_dep.s \ + $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_darwin_mach_dep.s \ + $(srcdir)/sparc_mach_dep.S $(srcdir)/sparc_sunos4_mach_dep.s \ + $(srcdir)/ia64_save_regs_in_stack.s \ + $(srcdir)/sparc_netbsd_mach_dep.s $(UTILS) rm -f mach_dep.o - ./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_mach_dep.s + ./if_mach MIPS IRIX5 $(CC) -c -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 POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_macosx_mach_dep.s -# ./if_mach ALPHA "" $(AS) -o mach_dep.o $(srcdir)/alpha_mach_dep.s -# alpha_mach_dep.s assumes that pointers are not saved in fp registers. -# Gcc on a 21264 can spill pointers to fp registers. Oops. + ./if_mach POWERPC DARWIN $(AS) -o mach_dep.o $(srcdir)/powerpc_darwin_mach_dep.s + ./if_mach ALPHA LINUX $(CC) -c -o mach_dep.o $(srcdir)/alpha_mach_dep.S ./if_mach SPARC SUNOS5 $(CC) -c -o mach_dep.o $(srcdir)/sparc_mach_dep.S ./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s ./if_mach SPARC OPENBSD $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s @@ -491,7 +538,7 @@ cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(UTILS) ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lucb `./threadlibs` ./if_mach HP_PA HPUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -ldld `./threadlibs` ./if_mach RS6000 "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses - ./if_mach POWERPC MACOSX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a + ./if_mach POWERPC DARWIN $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` ./if_mach ALPHA LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` ./if_mach IA64 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` @@ -510,7 +557,7 @@ if_not_there: $(srcdir)/if_not_there.c clean: rm -f gc.a *.o *.exe tests/*.o gctest gctest_dyn_link test_cpp \ setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ - threadlibs $(CORD_OBJS) cord/cordtest cord/de + threadlibs $(CORD_OBJS) cord/cordtest cord/de -rm -f *~ gctest: tests/test.o gc.a $(UTILS) diff --git a/boehm-gc/Makefile.dj b/boehm-gc/Makefile.dj index 7aadaf34b44..6097293ff0e 100644 --- a/boehm-gc/Makefile.dj +++ b/boehm-gc/Makefile.dj @@ -152,7 +152,7 @@ CFLAGS= -O -I$(srcdir)/include -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DALL_INTERIO # currently probably works only with Linux. -CXXFLAGS= $(CFLAGS) -DOPERATOR_NEW_ARRAY +CXXFLAGS= $(CFLAGS) -DGC_OPERATOR_NEW_ARRAY AR= ar RANLIB= ranlib @@ -165,8 +165,8 @@ CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordt CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o -SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ - sparc_mach_dep.s include/gc.h include/gc_typed.h \ +SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \ + sparc_mach_dep.S include/gc.h include/gc_typed.h \ include/private/gc_hdrs.h include/private/gc_priv.h \ include/private/gcconfig.h include/private/gc_mark.h \ include/gc_inl.h include/gc_inline.h gc.man \ @@ -177,7 +177,7 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ include/private/solaris_threads.h include/gc_backptr.h \ hpux_test_and_clear.s include/gc_gcj.h \ include/gc_local_alloc.h include/private/dbg_mlc.h \ - include/private/specific.h powerpc_macosx_mach_dep.s \ + include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h $(CORD_SRCS) OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ @@ -284,16 +284,16 @@ liblinuxgc.so: $(OBJS) dyn_load.o gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o -lo ln liblinuxgc.so libgc.so -mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s $(srcdir)/mips_ultrix_mach_dep.s \ - $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_macosx_mach_dep.s $(UTILS) +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S $(srcdir)/mips_ultrix_mach_dep.s \ + $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_darwin_mach_dep.s $(UTILS) rm -f mach_dep.o - ./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_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 POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_macosx_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_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_darwin_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_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s ./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c diff --git a/boehm-gc/Makefile.in b/boehm-gc/Makefile.in index edecc834988..4e5bb369e7a 100644 --- a/boehm-gc/Makefile.in +++ b/boehm-gc/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -66,9 +66,11 @@ target_triplet = @target@ AR = @AR@ AS = @AS@ CC = @CC@ +CFLAGS = @CFLAGS@ CPP = @CPP@ CXX = @CXX@ CXXCPP = @CXXCPP@ +CXXFLAGS = @CXXFLAGS@ CXXINCLUDES = @CXXINCLUDES@ DLLTOOL = @DLLTOOL@ EXEEXT = @EXEEXT@ @@ -89,7 +91,10 @@ RANLIB = @RANLIB@ STRIP = @STRIP@ THREADLIBS = @THREADLIBS@ VERSION = @VERSION@ +addincludes = @addincludes@ +addlibs = @addlibs@ addobjs = @addobjs@ +addtests = @addtests@ gc_basedir = @gc_basedir@ mkinstalldirs = @mkinstalldirs@ target_all = @target_all@ @@ -109,17 +114,21 @@ MULTIDO = true MULTICLEAN = true noinst_LTLIBRARIES = libgcjgc.la libgcjgc_convenience.la +@POWERPC_DARWIN_TRUE@asm_libgc_sources = @POWERPC_DARWIN_TRUE@powerpc_darwin_mach_dep.s +@POWERPC_DARWIN_FALSE@asm_libgc_sources = GC_SOURCES = allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \ -dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c irix_threads.c \ -linux_threads.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \ +dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c aix_irix_threads.c \ +malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \ obj_map.c os_dep.c pcr_interface.c ptr_chck.c real_malloc.c reclaim.c \ solaris_pthreads.c solaris_threads.c specific.c stubborn.c typd_mlc.c \ -backgraph.c win32_threads.c +backgraph.c win32_threads.c \ +pthread_support.c pthread_stop_world.c darwin_stop_world.c \ +$(asm_libgc_sources) -EXTRA_GC_SOURCES = alpha_mach_dep.s \ -mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \ +EXTRA_GC_SOURCES = alpha_mach_dep.S \ +mips_sgi_mach_dep.s mips_ultrix_mach_dep.s powerpc_darwin_mach_dep.s \ rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \ sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s @@ -213,24 +222,53 @@ DEFS = @DEFS@ -I. -I$(srcdir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ -libgcjgc_la_OBJECTS = allchblk.lo alloc.lo blacklst.lo checksums.lo \ -dbg_mlc.lo dyn_load.lo finalize.lo gc_dlopen.lo gcj_mlc.lo headers.lo \ -irix_threads.lo linux_threads.lo malloc.lo mallocx.lo mark.lo \ -mark_rts.lo misc.lo new_hblk.lo obj_map.lo os_dep.lo pcr_interface.lo \ -ptr_chck.lo real_malloc.lo reclaim.lo solaris_pthreads.lo \ -solaris_threads.lo specific.lo stubborn.lo typd_mlc.lo backgraph.lo \ -win32_threads.lo +@POWERPC_DARWIN_FALSE@libgcjgc_la_OBJECTS = allchblk.lo alloc.lo \ +@POWERPC_DARWIN_FALSE@blacklst.lo checksums.lo dbg_mlc.lo dyn_load.lo \ +@POWERPC_DARWIN_FALSE@finalize.lo gc_dlopen.lo gcj_mlc.lo headers.lo \ +@POWERPC_DARWIN_FALSE@aix_irix_threads.lo malloc.lo mallocx.lo mark.lo \ +@POWERPC_DARWIN_FALSE@mark_rts.lo misc.lo new_hblk.lo obj_map.lo \ +@POWERPC_DARWIN_FALSE@os_dep.lo pcr_interface.lo ptr_chck.lo \ +@POWERPC_DARWIN_FALSE@real_malloc.lo reclaim.lo solaris_pthreads.lo \ +@POWERPC_DARWIN_FALSE@solaris_threads.lo specific.lo stubborn.lo \ +@POWERPC_DARWIN_FALSE@typd_mlc.lo backgraph.lo win32_threads.lo \ +@POWERPC_DARWIN_FALSE@pthread_support.lo pthread_stop_world.lo \ +@POWERPC_DARWIN_FALSE@darwin_stop_world.lo +@POWERPC_DARWIN_TRUE@libgcjgc_la_OBJECTS = allchblk.lo alloc.lo \ +@POWERPC_DARWIN_TRUE@blacklst.lo checksums.lo dbg_mlc.lo dyn_load.lo \ +@POWERPC_DARWIN_TRUE@finalize.lo gc_dlopen.lo gcj_mlc.lo headers.lo \ +@POWERPC_DARWIN_TRUE@aix_irix_threads.lo malloc.lo mallocx.lo mark.lo \ +@POWERPC_DARWIN_TRUE@mark_rts.lo misc.lo new_hblk.lo obj_map.lo \ +@POWERPC_DARWIN_TRUE@os_dep.lo pcr_interface.lo ptr_chck.lo \ +@POWERPC_DARWIN_TRUE@real_malloc.lo reclaim.lo solaris_pthreads.lo \ +@POWERPC_DARWIN_TRUE@solaris_threads.lo specific.lo stubborn.lo \ +@POWERPC_DARWIN_TRUE@typd_mlc.lo backgraph.lo win32_threads.lo \ +@POWERPC_DARWIN_TRUE@pthread_support.lo pthread_stop_world.lo \ +@POWERPC_DARWIN_TRUE@darwin_stop_world.lo powerpc_darwin_mach_dep.lo libgcjgc_convenience_la_LDFLAGS = -libgcjgc_convenience_la_OBJECTS = allchblk.lo alloc.lo blacklst.lo \ -checksums.lo dbg_mlc.lo dyn_load.lo finalize.lo gc_dlopen.lo gcj_mlc.lo \ -headers.lo irix_threads.lo linux_threads.lo malloc.lo mallocx.lo \ -mark.lo mark_rts.lo misc.lo new_hblk.lo obj_map.lo os_dep.lo \ -pcr_interface.lo ptr_chck.lo real_malloc.lo reclaim.lo \ -solaris_pthreads.lo solaris_threads.lo specific.lo stubborn.lo \ -typd_mlc.lo backgraph.lo win32_threads.lo +@POWERPC_DARWIN_FALSE@libgcjgc_convenience_la_OBJECTS = allchblk.lo \ +@POWERPC_DARWIN_FALSE@alloc.lo blacklst.lo checksums.lo dbg_mlc.lo \ +@POWERPC_DARWIN_FALSE@dyn_load.lo finalize.lo gc_dlopen.lo gcj_mlc.lo \ +@POWERPC_DARWIN_FALSE@headers.lo aix_irix_threads.lo malloc.lo \ +@POWERPC_DARWIN_FALSE@mallocx.lo mark.lo mark_rts.lo misc.lo \ +@POWERPC_DARWIN_FALSE@new_hblk.lo obj_map.lo os_dep.lo pcr_interface.lo \ +@POWERPC_DARWIN_FALSE@ptr_chck.lo real_malloc.lo reclaim.lo \ +@POWERPC_DARWIN_FALSE@solaris_pthreads.lo solaris_threads.lo \ +@POWERPC_DARWIN_FALSE@specific.lo stubborn.lo typd_mlc.lo backgraph.lo \ +@POWERPC_DARWIN_FALSE@win32_threads.lo pthread_support.lo \ +@POWERPC_DARWIN_FALSE@pthread_stop_world.lo darwin_stop_world.lo +@POWERPC_DARWIN_TRUE@libgcjgc_convenience_la_OBJECTS = allchblk.lo \ +@POWERPC_DARWIN_TRUE@alloc.lo blacklst.lo checksums.lo dbg_mlc.lo \ +@POWERPC_DARWIN_TRUE@dyn_load.lo finalize.lo gc_dlopen.lo gcj_mlc.lo \ +@POWERPC_DARWIN_TRUE@headers.lo aix_irix_threads.lo malloc.lo \ +@POWERPC_DARWIN_TRUE@mallocx.lo mark.lo mark_rts.lo misc.lo new_hblk.lo \ +@POWERPC_DARWIN_TRUE@obj_map.lo os_dep.lo pcr_interface.lo ptr_chck.lo \ +@POWERPC_DARWIN_TRUE@real_malloc.lo reclaim.lo solaris_pthreads.lo \ +@POWERPC_DARWIN_TRUE@solaris_threads.lo specific.lo stubborn.lo \ +@POWERPC_DARWIN_TRUE@typd_mlc.lo backgraph.lo win32_threads.lo \ +@POWERPC_DARWIN_TRUE@pthread_support.lo pthread_stop_world.lo \ +@POWERPC_DARWIN_TRUE@darwin_stop_world.lo powerpc_darwin_mach_dep.lo check_PROGRAMS = gctest$(EXEEXT) gctest_DEPENDENCIES = ./libgcjgc.la -CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) DIST_COMMON = ChangeLog Makefile.am Makefile.in acinclude.m4 aclocal.m4 \ @@ -368,7 +406,7 @@ maintainer-clean-recursive: dot_seen=no; \ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ rev="$$subdir $$rev"; \ - test "$$subdir" = "." && dot_seen=yes; \ + test "$$subdir" != "." || dot_seen=yes; \ done; \ test "$$dot_seen" = "no" && rev=". $$rev"; \ target=`echo $@ | sed s/-recursive//`; \ @@ -598,7 +636,9 @@ mostlyclean distclean maintainer-clean test.o: tests/test.c $(COMPILE) -c $(srcdir)/tests/test.c $(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \ -include/private/gc_hdrs.h include/gc.h include/gc_gcj.h include/gc_mark.h +include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \ +include/gc_pthread_redirects.h include/gc_config_macros.h \ +include/gc_mark.h @addincludes@ .s.lo: $(LTCOMPILE) -Wp,-P -x assembler-with-cpp -c $< diff --git a/boehm-gc/allchblk.c b/boehm-gc/allchblk.c index 7d4cbd82f13..2b039397e1c 100644 --- a/boehm-gc/allchblk.c +++ b/boehm-gc/allchblk.c @@ -47,12 +47,16 @@ GC_bool GC_use_entire_heap = 0; struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 }; #ifndef USE_MUNMAP + word GC_free_bytes[N_HBLK_FLS+1] = { 0 }; /* Number of free bytes on each list. */ /* Is bytes + the number of free bytes on lists n .. N_HBLK_FLS */ /* > GC_max_large_allocd_bytes? */ - GC_bool GC_enough_large_bytes_left(bytes,n) +# ifdef __GNUC__ + __inline__ +# endif + static GC_bool GC_enough_large_bytes_left(bytes,n) word bytes; int n; { @@ -583,11 +587,11 @@ int n; if (!GC_use_entire_heap && size_avail != size_needed && USED_HEAP_SIZE >= GC_requested_heapsize - && !GC_incremental && GC_should_collect()) { + && !TRUE_INCREMENTAL && GC_should_collect()) { # ifdef USE_MUNMAP continue; # else - /* If we enough large blocks left to cover any */ + /* If we have enough large blocks left to cover any */ /* previous request for large blocks, we go ahead */ /* and split. Assuming a steady state, that should */ /* be safe. It means that we can use the full */ @@ -595,6 +599,12 @@ int n; if (!GC_enough_large_bytes_left(GC_large_allocd_bytes, n)) { continue; } + /* If we are deallocating lots of memory from */ + /* finalizers, fail and collect sooner rather */ + /* than later. */ + if (GC_finalizer_mem_freed > (GC_heapsize >> 4)) { + continue; + } # endif /* !USE_MUNMAP */ } /* If the next heap block is obviously better, go on. */ diff --git a/boehm-gc/alloc.c b/boehm-gc/alloc.c index f2e5af05720..f53061f872d 100644 --- a/boehm-gc/alloc.c +++ b/boehm-gc/alloc.c @@ -72,6 +72,13 @@ int GC_full_freq = 19; /* Every 20th collection is a full */ GC_bool GC_need_full_gc = FALSE; /* Need full GC do to heap growth. */ +#ifdef THREADS + GC_bool GC_world_stopped = FALSE; +# define IF_THREADS(x) x +#else +# define IF_THREADS(x) +#endif + word GC_used_heap_size_after_full = 0; char * GC_copyright[] = @@ -160,7 +167,7 @@ static word min_words_allocd() + (GC_large_free_bytes >> 2) /* use a bit more of large empty heap */ + total_root_size); - if (GC_incremental) { + if (TRUE_INCREMENTAL) { return scan_size / (2 * GC_free_space_divisor); } else { return scan_size / GC_free_space_divisor; @@ -182,7 +189,8 @@ word GC_adj_words_allocd() /* managed object should not alter result, assuming the client */ /* is playing by the rules. */ result = (signed_word)GC_words_allocd - - (signed_word)GC_mem_freed - expl_managed; + - (signed_word)GC_mem_freed + + (signed_word)GC_finalizer_mem_freed - expl_managed; if (result > (signed_word)GC_words_allocd) { result = GC_words_allocd; /* probably client bug or unfortunate scheduling */ @@ -250,7 +258,6 @@ void GC_maybe_gc() if (GC_should_collect()) { if (!GC_incremental) { - GC_notify_full_gc(); GC_gcollect_inner(); n_partial_gcs = 0; return; @@ -302,10 +309,14 @@ void GC_maybe_gc() /* * Stop the world garbage collection. Assumes lock held, signals disabled. * If stop_func is not GC_never_stop_func, then abort if stop_func returns TRUE. + * Return TRUE if we successfully completed the collection. */ GC_bool GC_try_to_collect_inner(stop_func) GC_stop_func stop_func; { +# ifdef CONDPRINT + CLOCK_TYPE start_time, current_time; +# endif if (GC_dont_gc) return FALSE; if (GC_incremental && GC_collection_in_progress()) { # ifdef CONDPRINT @@ -320,8 +331,10 @@ GC_stop_func stop_func; GC_collect_a_little_inner(1); } } + if (stop_func == GC_never_stop_func) GC_notify_full_gc(); # ifdef CONDPRINT if (GC_print_stats) { + if (GC_print_stats) GET_TIME(start_time); GC_printf2( "Initiating full world-stop collection %lu after %ld allocd bytes\n", (unsigned long) GC_gc_no+1, @@ -360,6 +373,13 @@ GC_stop_func stop_func; return(FALSE); } GC_finish_collection(); +# if defined(CONDPRINT) + if (GC_print_stats) { + GET_TIME(current_time); + GC_printf1("Complete collection took %lu msecs\n", + MS_TIME_DIFF(current_time,start_time)); + } +# endif return(TRUE); } @@ -430,6 +450,7 @@ int GC_collect_a_little GC_PROTO(()) result = (int)GC_collection_in_progress(); UNLOCK(); ENABLE_SIGNALS(); + if (!result && GC_debugging_started) GC_print_all_smashed(); return(result); } @@ -448,16 +469,17 @@ GC_stop_func stop_func; CLOCK_TYPE start_time, current_time; # endif -# if defined(REGISTER_LIBRARIES_EARLY) - GC_cond_register_dynamic_libraries(); -# endif - STOP_WORLD(); # ifdef PRINTTIMES GET_TIME(start_time); # endif # if defined(CONDPRINT) && !defined(PRINTTIMES) if (GC_print_stats) GET_TIME(start_time); # endif +# if defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + STOP_WORLD(); + IF_THREADS(GC_world_stopped = TRUE); # ifdef CONDPRINT if (GC_print_stats) { GC_printf1("--> Marking for collection %lu ", @@ -488,6 +510,7 @@ GC_stop_func stop_func; } # endif GC_deficit = i; /* Give the mutator a chance. */ + IF_THREADS(GC_world_stopped = FALSE); START_WORLD(); return(FALSE); } @@ -521,6 +544,8 @@ GC_stop_func stop_func; (*GC_check_heap)(); } + IF_THREADS(GC_world_stopped = FALSE); + START_WORLD(); # ifdef PRINTTIMES GET_TIME(current_time); GC_printf1("World-stopped marking took %lu msecs\n", @@ -534,7 +559,6 @@ GC_stop_func stop_func; } # endif # endif - START_WORLD(); return(TRUE); } @@ -611,6 +635,7 @@ void GC_finish_collection() GC_print_address_map(); } # endif + COND_DUMP; if (GC_find_leak) { /* Mark all objects on the free list. All objects should be */ /* marked when we're done. */ @@ -707,6 +732,7 @@ void GC_finish_collection() GC_words_allocd = 0; GC_words_wasted = 0; GC_mem_freed = 0; + GC_finalizer_mem_freed = 0; # ifdef USE_MUNMAP GC_unmap_old(); @@ -730,6 +756,7 @@ void GC_finish_collection() int result; DCL_LOCK_STATE; + if (GC_debugging_started) GC_print_all_smashed(); GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); @@ -741,14 +768,17 @@ void GC_finish_collection() EXIT_GC(); UNLOCK(); ENABLE_SIGNALS(); - if(result) GC_INVOKE_FINALIZERS(); + if(result) { + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + } return(result); } void GC_gcollect GC_PROTO(()) { - GC_notify_full_gc(); (void)GC_try_to_collect(GC_never_stop_func); + if (GC_have_errors) GC_print_all_errors(); } word GC_n_heap_sects = 0; /* Number of sections currently in heap. */ @@ -950,7 +980,6 @@ GC_bool ignore_off_page; { if (!GC_incremental && !GC_dont_gc && (GC_dont_expand && GC_words_allocd > 0 || GC_should_collect())) { - GC_notify_full_gc(); GC_gcollect_inner(); } else { word blocks_to_get = GC_heapsize/(HBLKSIZE*GC_free_space_divisor) @@ -975,7 +1004,6 @@ GC_bool ignore_off_page; && !GC_expand_hp_inner(needed_blocks)) { if (GC_fail_count++ < GC_max_retries) { WARN("Out of Memory! Trying to continue ...\n", 0); - GC_notify_full_gc(); GC_gcollect_inner(); } else { # if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC) @@ -1005,29 +1033,38 @@ ptr_t GC_allocobj(sz, kind) word sz; int kind; { - register ptr_t * flh = &(GC_obj_kinds[kind].ok_freelist[sz]); + ptr_t * flh = &(GC_obj_kinds[kind].ok_freelist[sz]); + GC_bool tried_minor = FALSE; 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); + if(TRUE_INCREMENTAL) GC_collect_a_little_inner(1); /* Sweep blocks for objects of this size */ - GC_continue_reclaim(sz, kind); + GC_continue_reclaim(sz, kind); EXIT_GC(); if (*flh == 0) { GC_new_hblk(sz, kind); } if (*flh == 0) { ENTER_GC(); - if (!GC_collect_or_expand((word)1,FALSE)) { + if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED + && ! tried_minor ) { + GC_collect_a_little_inner(1); + tried_minor = TRUE; + } else { + if (!GC_collect_or_expand((word)1,FALSE)) { EXIT_GC(); return(0); + } } EXIT_GC(); } } + /* Successful allocation; reset failure count. */ + GC_fail_count = 0; return(*flh); } diff --git a/boehm-gc/alpha_mach_dep.s b/boehm-gc/alpha_mach_dep.s deleted file mode 100644 index a21f77ad54c..00000000000 --- a/boehm-gc/alpha_mach_dep.s +++ /dev/null @@ -1,86 +0,0 @@ - .arch ev6 - - .text - .align 4 - .globl GC_push_regs - .ent GC_push_regs 2 -GC_push_regs: - ldgp $gp, 0($27) - lda $sp, -16($sp) - stq $26, 0($sp) - .mask 0x04000000, 0 - .frame $sp, 16, $26, 0 - - # $0 integer result - # $1-$8 temp regs - not preserved cross calls - # $9-$15 call saved regs - # $16-$21 argument regs - not preserved cross calls - # $22-$28 temp regs - not preserved cross calls - # $29 global pointer - not preserved cross calls - # $30 stack pointer - -# define call_push(x) \ - mov x, $16; \ - jsr $26, GC_push_one; \ - ldgp $gp, 0($26) - - call_push($9) - call_push($10) - call_push($11) - call_push($12) - call_push($13) - call_push($14) - call_push($15) - - # $f0-$f1 floating point results - # $f2-$f9 call saved regs - # $f10-$f30 temp regs - not preserved cross calls - - # Use the most efficient transfer method for this hardware. - # Bit 1 detects the FIX extension, which includes ftoit. - amask 2, $0 - bne $0, $use_stack - -#undef call_push -#define call_push(x) \ - ftoit x, $16; \ - jsr $26, GC_push_one; \ - ldgp $gp, 0($26) - - call_push($f2) - call_push($f3) - call_push($f4) - call_push($f5) - call_push($f6) - call_push($f7) - call_push($f8) - call_push($f9) - - ldq $26, 0($sp) - lda $sp, 16($sp) - ret $31, ($26), 1 - - .align 4 -$use_stack: - -#undef call_push -#define call_push(x) \ - stt x, 8($sp); \ - ldq $16, 8($sp); \ - jsr $26, GC_push_one; \ - ldgp $gp, 0($26) - - call_push($f2) - call_push($f3) - call_push($f4) - call_push($f5) - call_push($f6) - call_push($f7) - call_push($f8) - call_push($f9) - - ldq $26, 0($sp) - lda $sp, 16($sp) - ret $31, ($26), 1 - - .end GC_push_regs diff --git a/boehm-gc/configure b/boehm-gc/configure index ef1a1a76331..e8ca37d9e8a 100755 --- a/boehm-gc/configure +++ b/boehm-gc/configure @@ -41,7 +41,12 @@ ac_help="$ac_help ac_help="$ac_help --with-ecos enable runtime eCos target support" ac_help="$ac_help - --enable-shared[=PKGS] build shared libraries [default=no]" + --enable-shared[=PKGS] build shared libraries [default=yes]" +ac_help="$ac_help + --with-target-subdir=SUBDIR + configuring with a cross compiler" +ac_help="$ac_help + --with-cross-host=HOST configuring with a cross compiler" ac_help="$ac_help --enable-full-debug include full support for pointer backtracing etc." @@ -61,7 +66,6 @@ program_suffix=NONE program_transform_name=s,x,x, silent= site= -sitefile= srcdir= target=NONE verbose= @@ -176,7 +180,6 @@ Configuration: --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages - --site-file=FILE use FILE as the site file --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX @@ -347,11 +350,6 @@ EOF -site=* | --site=* | --sit=*) site="$ac_optarg" ;; - -site-file | --site-file | --site-fil | --site-fi | --site-f) - ac_prev=sitefile ;; - -site-file=* | --site-file=* | --site-fil=* | --site-fi=* | --site-f=*) - sitefile="$ac_optarg" ;; - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) @@ -517,16 +515,12 @@ fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. -if test -z "$sitefile"; then - if test -z "$CONFIG_SITE"; then - if test "x$prefix" != xNONE; then - CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" - else - CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" - fi +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi -else - CONFIG_SITE="$sitefile" fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then @@ -604,7 +598,7 @@ ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:608: checking for a BSD compatible install" >&5 +echo "configure:602: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -657,7 +651,7 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 -echo "configure:661: checking whether build environment is sane" >&5 +echo "configure:655: checking whether build environment is sane" >&5 # Just in case sleep 1 echo timestamp > conftestfile @@ -714,7 +708,7 @@ test "$program_suffix" != NONE && test "$program_transform_name" = "" && program_transform_name="s,x,x," echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -echo "configure:718: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo "configure:712: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -747,12 +741,12 @@ else fi echo $ac_n "checking for Cygwin environment""... $ac_c" 1>&6 -echo "configure:751: checking for Cygwin environment" >&5 +echo "configure:745: checking for Cygwin environment" >&5 if eval "test \"`echo '$''{'ac_cv_cygwin'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 756 "configure" +#line 750 "configure" #include "confdefs.h" int main() { @@ -763,7 +757,7 @@ int main() { return __CYGWIN__; ; return 0; } EOF -if { (eval echo configure:767: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:761: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_cygwin=yes else @@ -780,19 +774,19 @@ echo "$ac_t""$ac_cv_cygwin" 1>&6 CYGWIN= test "$ac_cv_cygwin" = yes && CYGWIN=yes echo $ac_n "checking for mingw32 environment""... $ac_c" 1>&6 -echo "configure:784: checking for mingw32 environment" >&5 +echo "configure:778: checking for mingw32 environment" >&5 if eval "test \"`echo '$''{'ac_cv_mingw32'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <<EOF -#line 789 "configure" +#line 783 "configure" #include "confdefs.h" int main() { return __MINGW32__; ; return 0; } EOF -if { (eval echo configure:796: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:790: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_mingw32=yes else @@ -903,7 +897,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:907: checking host system type" >&5 +echo "configure:901: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -924,7 +918,7 @@ host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$host" 1>&6 echo $ac_n "checking target system type""... $ac_c" 1>&6 -echo "configure:928: checking target system type" >&5 +echo "configure:922: checking target system type" >&5 target_alias=$target case "$target_alias" in @@ -942,7 +936,7 @@ target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$target" 1>&6 echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:946: checking build system type" >&5 +echo "configure:940: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -982,7 +976,7 @@ fi missing_dir=`cd $ac_aux_dir && pwd` echo $ac_n "checking for working aclocal""... $ac_c" 1>&6 -echo "configure:986: checking for working aclocal" >&5 +echo "configure:980: checking for working aclocal" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -995,7 +989,7 @@ else fi echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 -echo "configure:999: checking for working autoconf" >&5 +echo "configure:993: checking for working autoconf" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1008,7 +1002,7 @@ else fi echo $ac_n "checking for working automake""... $ac_c" 1>&6 -echo "configure:1012: checking for working automake" >&5 +echo "configure:1006: checking for working automake" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1021,7 +1015,7 @@ else fi echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 -echo "configure:1025: checking for working autoheader" >&5 +echo "configure:1019: checking for working autoheader" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1034,7 +1028,7 @@ else fi echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 -echo "configure:1038: checking for working makeinfo" >&5 +echo "configure:1032: checking for working makeinfo" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. @@ -1060,7 +1054,7 @@ fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1064: checking for $ac_word" >&5 +echo "configure:1058: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1090,7 +1084,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1094: checking for $ac_word" >&5 +echo "configure:1088: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1139,7 +1133,7 @@ fi fi echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:1143: checking whether we are using GNU C" >&5 +echo "configure:1137: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1148,7 +1142,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1152: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1146: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -1163,7 +1157,7 @@ if test $ac_cv_prog_gcc = yes; then ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:1167: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:1161: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1200,7 +1194,7 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1204: checking for $ac_word" >&5 +echo "configure:1198: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1233,7 +1227,7 @@ test -n "$CXX" || CXX="gcc" test -z "$CXX" && { echo "configure: error: no acceptable c++ found in \$PATH" 1>&2; exit 1; } echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 -echo "configure:1237: checking whether we are using GNU C++" >&5 +echo "configure:1231: checking whether we are using GNU C++" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1242,7 +1236,7 @@ else yes; #endif EOF -if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:1246: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:1240: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gxx=yes else ac_cv_prog_gxx=no @@ -1257,7 +1251,7 @@ if test $ac_cv_prog_gxx = yes; then ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS= echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 -echo "configure:1261: checking whether ${CXX-g++} accepts -g" >&5 +echo "configure:1255: checking whether ${CXX-g++} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1290,7 +1284,7 @@ fi # NEWLIB_CONFIGURE, which doesn't work because that means that it will # be run before AC_CANONICAL_HOST. echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:1294: checking build system type" >&5 +echo "configure:1288: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -1311,7 +1305,7 @@ echo "$ac_t""$build" 1>&6 # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. set dummy ${ac_tool_prefix}as; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1315: checking for $ac_word" >&5 +echo "configure:1309: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AS'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1343,7 +1337,7 @@ fi # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1347: checking for $ac_word" >&5 +echo "configure:1341: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1375,7 +1369,7 @@ fi # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1379: checking for $ac_word" >&5 +echo "configure:1373: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1407,7 +1401,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1411: checking for $ac_word" >&5 +echo "configure:1405: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1452,7 +1446,7 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:1456: checking for a BSD compatible install" >&5 +echo "configure:1450: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1506,7 +1500,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6 -echo "configure:1510: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo "configure:1504: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" @@ -1544,7 +1538,7 @@ if false; then echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 -echo "configure:1548: checking for executable suffix" >&5 +echo "configure:1542: checking for executable suffix" >&5 if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1554,10 +1548,10 @@ else rm -f conftest* echo 'int main () { return 0; }' > conftest.$ac_ext ac_cv_exeext= - if { (eval echo configure:1558: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + if { (eval echo configure:1552: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then for file in conftest.*; do case $file in - *.c | *.o | *.obj | *.ilk | *.pdb) ;; + *.c | *.o | *.obj) ;; *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;; esac done @@ -1676,7 +1670,7 @@ ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. echo $ac_n "checking for ld used by GCC""... $ac_c" 1>&6 -echo "configure:1680: checking for ld used by GCC" >&5 +echo "configure:1674: checking for ld used by GCC" >&5 case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw @@ -1706,10 +1700,10 @@ echo "configure:1680: checking for ld used by GCC" >&5 esac elif test "$with_gnu_ld" = yes; then echo $ac_n "checking for GNU ld""... $ac_c" 1>&6 -echo "configure:1710: checking for GNU ld" >&5 +echo "configure:1704: checking for GNU ld" >&5 else echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6 -echo "configure:1713: checking for non-GNU ld" >&5 +echo "configure:1707: checking for non-GNU ld" >&5 fi if eval "test \"`echo '$''{'lt_cv_path_LD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1744,7 +1738,7 @@ else fi test -z "$LD" && { echo "configure: error: no acceptable ld found in \$PATH" 1>&2; exit 1; } echo $ac_n "checking if the linker ($LD) is GNU ld""... $ac_c" 1>&6 -echo "configure:1748: checking if the linker ($LD) is GNU ld" >&5 +echo "configure:1742: checking if the linker ($LD) is GNU ld" >&5 if eval "test \"`echo '$''{'lt_cv_prog_gnu_ld'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1761,7 +1755,7 @@ with_gnu_ld=$lt_cv_prog_gnu_ld echo $ac_n "checking for $LD option to reload object files""... $ac_c" 1>&6 -echo "configure:1765: checking for $LD option to reload object files" >&5 +echo "configure:1759: checking for $LD option to reload object files" >&5 if eval "test \"`echo '$''{'lt_cv_ld_reload_flag'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1773,7 +1767,7 @@ reload_flag=$lt_cv_ld_reload_flag test -n "$reload_flag" && reload_flag=" $reload_flag" echo $ac_n "checking for BSD-compatible nm""... $ac_c" 1>&6 -echo "configure:1777: checking for BSD-compatible nm" >&5 +echo "configure:1771: checking for BSD-compatible nm" >&5 if eval "test \"`echo '$''{'lt_cv_path_NM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1811,7 +1805,7 @@ NM="$lt_cv_path_NM" echo "$ac_t""$NM" 1>&6 echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:1815: checking whether ln -s works" >&5 +echo "configure:1809: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1832,7 +1826,7 @@ else fi echo $ac_n "checking how to recognise dependant libraries""... $ac_c" 1>&6 -echo "configure:1836: checking how to recognise dependant libraries" >&5 +echo "configure:1830: checking how to recognise dependant libraries" >&5 if eval "test \"`echo '$''{'lt_cv_deplibs_check_method'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2005,13 +1999,13 @@ file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method echo $ac_n "checking for object suffix""... $ac_c" 1>&6 -echo "configure:2009: checking for object suffix" >&5 +echo "configure:2003: checking for object suffix" >&5 if eval "test \"`echo '$''{'ac_cv_objext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else rm -f conftest* echo 'int i = 1;' > conftest.$ac_ext -if { (eval echo configure:2015: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2009: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then for ac_file in conftest.*; do case $ac_file in *.c) ;; @@ -2035,7 +2029,7 @@ case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then echo $ac_n "checking for ${ac_tool_prefix}file""... $ac_c" 1>&6 -echo "configure:2039: checking for ${ac_tool_prefix}file" >&5 +echo "configure:2033: checking for ${ac_tool_prefix}file" >&5 if eval "test \"`echo '$''{'lt_cv_path_MAGIC_CMD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2097,7 +2091,7 @@ fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then echo $ac_n "checking for file""... $ac_c" 1>&6 -echo "configure:2101: checking for file" >&5 +echo "configure:2095: checking for file" >&5 if eval "test \"`echo '$''{'lt_cv_path_MAGIC_CMD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2168,7 +2162,7 @@ esac # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2172: checking for $ac_word" >&5 +echo "configure:2166: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2200,7 +2194,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2204: checking for $ac_word" >&5 +echo "configure:2198: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2235,7 +2229,7 @@ fi # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2239: checking for $ac_word" >&5 +echo "configure:2233: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2267,7 +2261,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:2271: checking for $ac_word" >&5 +echo "configure:2265: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2334,8 +2328,8 @@ test x"$pic_mode" = xno && libtool_flags="$libtool_flags --prefer-non-pic" case $host in *-*-irix6*) # Find out which ABI we are using. - echo '#line 2338 "configure"' > conftest.$ac_ext - if { (eval echo configure:2339: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + echo '#line 2332 "configure"' > conftest.$ac_ext + if { (eval echo configure:2333: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) @@ -2368,7 +2362,7 @@ case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext - if { (eval echo configure:2372: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if { (eval echo configure:2366: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then case "`/usr/bin/file conftest.o`" in *ELF-32*) HPUX_IA64_MODE="32" @@ -2384,7 +2378,7 @@ ia64-*-hpux*) x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext - if { (eval echo configure:2388: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + if { (eval echo configure:2382: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then case "`/usr/bin/file conftest.o`" in *32-bit*) case $host in @@ -2428,7 +2422,7 @@ x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" echo $ac_n "checking whether the C compiler needs -belf""... $ac_c" 1>&6 -echo "configure:2432: checking whether the C compiler needs -belf" >&5 +echo "configure:2426: checking whether the C compiler needs -belf" >&5 if eval "test \"`echo '$''{'lt_cv_cc_needs_belf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2441,14 +2435,14 @@ ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$a cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <<EOF -#line 2445 "configure" +#line 2439 "configure" #include "confdefs.h" int main() { ; return 0; } EOF -if { (eval echo configure:2452: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2446: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* lt_cv_cc_needs_belf=yes else @@ -2478,7 +2472,7 @@ echo "$ac_t""$lt_cv_cc_needs_belf" 1>&6 esac echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6 -echo "configure:2482: checking how to run the C++ preprocessor" >&5 +echo "configure:2476: checking how to run the C++ preprocessor" >&5 if test -z "$CXXCPP"; then if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2491,12 +2485,12 @@ ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftes cross_compiling=$ac_cv_prog_cxx_cross CXXCPP="${CXX-g++} -E" cat > conftest.$ac_ext <<EOF -#line 2495 "configure" +#line 2489 "configure" #include "confdefs.h" #include <stdlib.h> EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:2500: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:2494: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -2643,7 +2637,7 @@ fi echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6 -echo "configure:2647: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo "configure:2641: checking whether to enable maintainer-specific portions of Makefiles" >&5 # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. if test "${enable_maintainer_mode+set}" = set; then enableval="$enable_maintainer_mode" @@ -2676,7 +2670,7 @@ if false; then echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 -echo "configure:2680: checking for executable suffix" >&5 +echo "configure:2674: checking for executable suffix" >&5 if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2686,10 +2680,10 @@ else rm -f conftest* echo 'int main () { return 0; }' > conftest.$ac_ext ac_cv_exeext= - if { (eval echo configure:2690: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + if { (eval echo configure:2684: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then for file in conftest.*; do case $file in - *.c | *.o | *.obj | *.ilk | *.pdb) ;; + *.c | *.o | *.obj) ;; *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;; esac done @@ -2709,7 +2703,7 @@ ac_exeext=$EXEEXT fi echo $ac_n "checking for thread model used by GCC""... $ac_c" 1>&6 -echo "configure:2713: checking for thread model used by GCC" >&5 +echo "configure:2707: checking for thread model used by GCC" >&5 THREADS=`$CC -v 2>&1 | sed -n 's/^Thread model: //p'` if test -z "$THREADS"; then THREADS=no @@ -2738,7 +2732,7 @@ case "$THREADS" in THREADS=posix THREADLIBS=-lpthread case "$host" in - x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* ) + x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* | alpha-*-linux*) cat >> confdefs.h <<\EOF #define GC_LINUX_THREADS 1 EOF @@ -2747,7 +2741,7 @@ EOF #define _REENTRANT 1 EOF - if test "${enable_parallel_mark}"; then + if test "${enable_parallel_mark}" = yes; then cat >> confdefs.h <<\EOF #define PARALLEL_MARK 1 EOF @@ -2768,6 +2762,16 @@ EOF EOF ;; + *-*-aix*) + cat >> confdefs.h <<\EOF +#define GC_AIX_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; *-*-hpux*) echo "configure: warning: "Only HP/UX 11 threads are supported."" 1>&2 cat >> confdefs.h <<\EOF @@ -2816,7 +2820,46 @@ EOF ;; *-*-cygwin*) - THREADLIBS= + cat >> confdefs.h <<\EOF +#define GC_WIN32_THREADS 1 +EOF + + ;; + *-*-darwin*) + cat >> confdefs.h <<\EOF +#define GC_DARWIN_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define THREAD_LOCAL_ALLOC 1 +EOF + + if test "${enable_parallel_mark}" = yes; then + cat >> confdefs.h <<\EOF +#define PARALLEL_MARK 1 +EOF + + fi + ;; + *-*-osf*) + cat >> confdefs.h <<\EOF +#define GC_OSF1_THREADS 1 +EOF + + if test "${enable_parallel_mark}" = yes; then + cat >> confdefs.h <<\EOF +#define PARALLEL_MARK 1 +EOF + + cat >> confdefs.h <<\EOF +#define THREAD_LOCAL_ALLOC 1 +EOF + + # May want to enable it in other cases, too. + # Measurements havent yet been done. + fi + INCLUDES="$INCLUDES -pthread" + THREADLIBS="-lpthread -lrt" ;; esac ;; @@ -2825,16 +2868,48 @@ EOF #define GC_WIN32_THREADS 1 EOF - cat >> confdefs.h <<\EOF + cat >> confdefs.h <<\EOF #define NO_GETENV 1 EOF - if test $enable_shared = yes; then - cat >> confdefs.h <<\EOF -#define GC_DLL 1 + ;; + dgux386) + THREADS=dgux386 +echo "$ac_t""$THREADLIBS" 1>&6 + # Use pthread GCC switch + THREADLIBS=-pthread + if test "${enable_parallel_mark}" = yes; then + cat >> confdefs.h <<\EOF +#define PARALLEL_MARK 1 EOF fi + cat >> confdefs.h <<\EOF +#define THREAD_LOCAL_ALLOC 1 +EOF + + cat >> confdefs.h <<\EOF +#define GC_DGUX386_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define DGUX_THREADS 1 +EOF + + # Enable _POSIX4A_DRAFT10_SOURCE with flag -pthread + INCLUDES="-pthread $INCLUDES" + ;; + aix) + THREADS=posix + THREADLIBS=-lpthread + cat >> confdefs.h <<\EOF +#define GC_AIX_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + ;; decosf1 | irix | mach | os2 | solaris | dce | vxworks) { echo "configure: error: thread package $THREADS not yet supported" 1>&2; exit 1; } @@ -2845,8 +2920,28 @@ EOF esac -echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 -echo "configure:2850: checking for dlopen in -ldl" >&5 +case "$host" in + powerpc-*-darwin*) + powerpc_darwin=true + ;; +esac + + +if test x$powerpc_darwin = xtrue; then + POWERPC_DARWIN_TRUE= + POWERPC_DARWIN_FALSE='#' +else + POWERPC_DARWIN_TRUE='#' + POWERPC_DARWIN_FALSE= +fi + +# We never want libdl on darwin. It is a fake libdl that just ends up making +# dyld calls anyway +case "$host" in + *-*-darwin*) ;; + *) + echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:2945: checking for dlopen in -ldl" >&5 ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2854,7 +2949,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldl $LIBS" cat > conftest.$ac_ext <<EOF -#line 2858 "configure" +#line 2953 "configure" #include "confdefs.h" /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 @@ -2865,7 +2960,7 @@ int main() { dlopen() ; return 0; } EOF -if { (eval echo configure:2869: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:2964: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2885,6 +2980,9 @@ else echo "$ac_t""no" 1>&6 fi + ;; +esac + target_all=libgcjgc.la @@ -2901,6 +2999,9 @@ fi addobjs= +addlibs= +addincludes= +addtests= CXXINCLUDES= case "$TARGET_ECOS" in no) @@ -2915,17 +3016,31 @@ EOF ;; esac +if test "${enable_cplusplus}" = yes; then + addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h" + addtests="$addtests test_cpp" +fi +if test "${enable_cplusplus}" = yes; then + CPLUSPLUS_TRUE= + CPLUSPLUS_FALSE='#' +else + CPLUSPLUS_TRUE='#' + CPLUSPLUS_FALSE= +fi -machdep= -case "$host" in - alpha*-*-openbsd*) - machdep="alpha_mach_dep.lo" - if test x"${ac_cv_lib_dl_dlopen}" != xyes ; then - echo "configure: warning: OpenBSD/Alpha without dlopen(). Shared library support is disabled" 1>&2 - # Check whether --enable-shared or --disable-shared was given. + + + + + +# Configuration of shared libraries +# +echo $ac_n "checking whether to build shared libraries""... $ac_c" 1>&6 +echo "configure:3043: checking whether to build shared libraries" >&5 +# Check whether --enable-shared or --disable-shared was given. if test "${enable_shared+set}" = set; then enableval="$enable_shared" p=${PACKAGE-default} @@ -2945,12 +3060,33 @@ no) enable_shared=no ;; ;; esac else - enable_shared=no + enable_shared=yes fi + +case "$host" in + alpha-*-openbsd*) + enable_shared=no + echo "$ac_t""no" 1>&6 + ;; + *) + echo "$ac_t""yes" 1>&6 + ;; +esac + +# Configuration of machine-dependent code +# +echo $ac_n "checking which machine-dependent code should be used""... $ac_c" 1>&6 +echo "configure:3081: checking which machine-dependent code should be used" >&5 +machdep= +case "$host" in + alpha*-*-openbsd*) + machdep="alpha_mach_dep.lo" + if test x"${ac_cv_lib_dl_dlopen}" != xyes ; then + echo "configure: warning: OpenBSD/Alpha without dlopen(). Shared library support is disabled" 1>&2 fi ;; - alpha*-*-*) + alpha*-*-linux*) machdep="alpha_mach_dep.lo" ;; i?86-*-solaris2.[89]*) @@ -2973,8 +3109,10 @@ EOF mips-dec-ultrix*) machdep="mips_ultrix_mach-dep.lo" ;; - mips*-*-linux*) + mips-nec-sysv*|mips-unknown-sysv*) ;; + mips*-*-linux*) + ;; mips-*-*) machdep="mips_sgi_mach_dep.lo" cat >> confdefs.h <<\EOF @@ -2982,6 +3120,9 @@ EOF EOF ;; + sparc-*-netbsd*) + machdep="sparc_netbsd_mach_dep.lo" + ;; sparc-sun-solaris2.3*) machdep="sparc_mach_dep.lo" cat >> confdefs.h <<\EOF @@ -2997,14 +3138,217 @@ EOF ;; esac if test x"$machdep" = x; then +echo "$ac_t""$machdep" 1>&6 machdep="mach_dep.lo" fi addobjs="$addobjs $machdep" + + + + + + + + + +# +# Check for AViiON Machines running DGUX +# +echo $ac_n "checking if host is AViiON running DGUX""... $ac_c" 1>&6 +echo "configure:3161: checking if host is AViiON running DGUX" >&5 +ac_is_dgux=no +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:3164: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 3179 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3185: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 3196 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3202: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 3213 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3219: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +ac_safe=`echo "sys/dg_sys_info.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/dg_sys_info.h""... $ac_c" 1>&6 +echo "configure:3245: checking for sys/dg_sys_info.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3250 "configure" +#include "confdefs.h" +#include <sys/dg_sys_info.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3255: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_is_dgux=yes; +else + echo "$ac_t""no" 1>&6 +fi + + +echo "$ac_t""$ac_is_dgux" 1>&6 + ## :GOTCHA: we do not check anything but sys/dg_sys_info.h +if test $ac_is_dgux = yes; then + if test "$enable_full_debug" = "yes"; then + CFLAGS="-g -mstandard -DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CXXFLAGS="-g -mstandard -DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + else + CFLAGS="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CXXFLAGS="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + fi + + +fi + +# Check whether --with-target-subdir or --without-target-subdir was given. +if test "${with_target_subdir+set}" = set; then + withval="$with_target_subdir" + : +fi + +# Check whether --with-cross-host or --without-cross-host was given. +if test "${with_cross_host+set}" = set; then + withval="$with_cross_host" + : +fi + + +# automake wants to see AC_EXEEXT. But we don't need it. And having +# it is actually a problem, because the compiler we're passed can't +# necessarily do a full link. So we fool automake here. +if false; then + # autoconf 2.50 runs AC_EXEEXT by default, and the macro expands + # to nothing, so nothing would remain between `then' and `fi' if it + # were not for the `:' below. + : + + +echo $ac_n "checking for executable suffix""... $ac_c" 1>&6 +echo "configure:3315: checking for executable suffix" >&5 +if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$CYGWIN" = yes || test "$MINGW32" = yes; then + ac_cv_exeext=.exe +else + rm -f conftest* + echo 'int main () { return 0; }' > conftest.$ac_ext + ac_cv_exeext= + if { (eval echo configure:3325: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then + for file in conftest.*; do + case $file in + *.c | *.o | *.obj) ;; + *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;; + esac + done + else + { echo "configure: error: installation or configuration problem: compiler cannot create executables." 1>&2; exit 1; } + fi + rm -f conftest* + test x"${ac_cv_exeext}" = x && ac_cv_exeext=no +fi +fi + +EXEEXT="" +test x"${ac_cv_exeext}" != xno && EXEEXT=${ac_cv_exeext} +echo "$ac_t""${ac_cv_exeext}" 1>&6 +ac_exeext=$EXEEXT + +fi + +echo $ac_n "checking whether Solaris gcc optimization fix is necessary""... $ac_c" 1>&6 +echo "configure:3348: checking whether Solaris gcc optimization fix is necessary" >&5 case "$host" in - sparc-sun-solaris2*) + sparc-sun-solaris2*|*aix*) if test "$GCC" = yes; then + echo "$ac_t""yes" 1>&6 new_CFLAGS= for i in $CFLAGS; do case "$i" in @@ -3016,8 +3360,11 @@ case "$host" in esac done CFLAGS="$new_CFLAGS" + else + echo "$ac_t""no" 1>&6 fi ;; + *) echo "$ac_t""no" 1>&6 ;; esac MY_CFLAGS="$CFLAGS" @@ -3095,6 +3442,12 @@ EOF EOF ;; + i345686-*-dgux*) + cat >> confdefs.h <<\EOF +#define MAKE_BACK_GRAPH 1 +EOF + + ;; esac fi fi @@ -3311,11 +3664,19 @@ s%@STRIP@%$STRIP%g s%@LIBTOOL@%$LIBTOOL%g s%@CXXCPP@%$CXXCPP%g s%@THREADLIBS@%$THREADLIBS%g +s%@POWERPC_DARWIN_TRUE@%$POWERPC_DARWIN_TRUE%g +s%@POWERPC_DARWIN_FALSE@%$POWERPC_DARWIN_FALSE%g s%@EXTRA_TEST_LIBS@%$EXTRA_TEST_LIBS%g s%@target_all@%$target_all%g +s%@CPLUSPLUS_TRUE@%$CPLUSPLUS_TRUE%g +s%@CPLUSPLUS_FALSE@%$CPLUSPLUS_FALSE%g s%@INCLUDES@%$INCLUDES%g s%@CXXINCLUDES@%$CXXINCLUDES%g s%@addobjs@%$addobjs%g +s%@addincludes@%$addincludes%g +s%@addlibs@%$addlibs%g +s%@addtests@%$addtests%g +s%@CPP@%$CPP%g s%@MY_CFLAGS@%$MY_CFLAGS%g s%@toolexecdir@%$toolexecdir%g s%@toolexeclibdir@%$toolexeclibdir%g @@ -3327,7 +3688,7 @@ cat >> $CONFIG_STATUS <<\EOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. -ac_max_sed_cmds=60 # Maximum number of lines to put in a sed script. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. ac_file=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_cmds # Line after last line for current file. diff --git a/boehm-gc/configure.host b/boehm-gc/configure.host index 82294cf6ef1..a98a0a7cb30 100644 --- a/boehm-gc/configure.host +++ b/boehm-gc/configure.host @@ -14,22 +14,28 @@ # host The configuration host # host_cpu The configuration host CPU # target_optspace --enable-target-optspace ("yes", "no", "") +# GCC should be "yes" if using gcc # It sets the following shell variables: # gc_cflags Special CFLAGS to use when building +gc_cflags="" + # We should set -fexceptions if we are using gcc and might be used # inside something like gcj. This is the zeroth approximation: -case "$host" in - *-*-linux* ) - gc_cflags=-fexceptions - ;; - hppa*-*-hpux* ) - if test $GCC != "yes" ; then - gc_cflags=+ESdbgasm - fi - ;; -esac +if test :"$GCC": = :yes: ; then + gc_cflags="${gc_cflags} -fexceptions" +else + case "$host" in + hppa*-*-hpux* ) + if test :$GCC: != :"yes": ; then + gc_cflags="${gc_flags} +ESdbgasm" + fi + # :TODO: actaully we should check using Autoconf if + # the compiler supports this option. + ;; + esac +fi case "${target_optspace}:${host}" in yes:*) @@ -48,7 +54,7 @@ esac case "${host}" in mips-tx39-*|mipstx39-unknown-*) - boehm_gc_cflags="${boehm_gc_cflags} -G 0" + gc_cflags="${gc_cflags} -G 0" ;; *) ;; diff --git a/boehm-gc/configure.in b/boehm-gc/configure.in index 95e9d7cf06e..99686242214 100644 --- a/boehm-gc/configure.in +++ b/boehm-gc/configure.in @@ -73,10 +73,10 @@ case "$THREADS" in THREADS=posix THREADLIBS=-lpthread case "$host" in - x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* ) + x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* | alpha-*-linux*) AC_DEFINE(GC_LINUX_THREADS) AC_DEFINE(_REENTRANT) - if test "${enable_parallel_mark}"; then + if test "${enable_parallel_mark}" = yes; then AC_DEFINE(PARALLEL_MARK) fi AC_DEFINE(THREAD_LOCAL_ALLOC) @@ -85,6 +85,10 @@ case "$THREADS" in AC_DEFINE(GC_LINUX_THREADS) AC_DEFINE(_REENTRANT) ;; + *-*-aix*) + AC_DEFINE(GC_AIX_THREADS) + AC_DEFINE(_REENTRANT) + ;; *-*-hpux*) AC_MSG_WARN("Only HP/UX 11 threads are supported.") AC_DEFINE(GC_HPUX_THREADS) @@ -109,16 +113,52 @@ case "$THREADS" in AC_DEFINE(GC_IRIX_THREADS) ;; *-*-cygwin*) - THREADLIBS= + AC_DEFINE(GC_WIN32_THREADS) + ;; + *-*-darwin*) + AC_DEFINE(GC_DARWIN_THREADS) + AC_DEFINE(THREAD_LOCAL_ALLOC) + if test "${enable_parallel_mark}" = yes; then + AC_DEFINE(PARALLEL_MARK) + fi + ;; + *-*-osf*) + AC_DEFINE(GC_OSF1_THREADS) + if test "${enable_parallel_mark}" = yes; then + AC_DEFINE(PARALLEL_MARK) + AC_DEFINE(THREAD_LOCAL_ALLOC) + # May want to enable it in other cases, too. + # Measurements havent yet been done. + fi + INCLUDES="$INCLUDES -pthread" + THREADLIBS="-lpthread -lrt" ;; esac ;; win32) AC_DEFINE(GC_WIN32_THREADS) + dnl Wine getenv may not return NULL for missing entry AC_DEFINE(NO_GETENV) - if test $enable_shared = yes; then - AC_DEFINE(GC_DLL) + ;; + dgux386) + THREADS=dgux386 +AC_MSG_RESULT($THREADLIBS) + # Use pthread GCC switch + THREADLIBS=-pthread + if test "${enable_parallel_mark}" = yes; then + AC_DEFINE(PARALLEL_MARK) fi + AC_DEFINE(THREAD_LOCAL_ALLOC) + AC_DEFINE(GC_DGUX386_THREADS) + AC_DEFINE(DGUX_THREADS) + # Enable _POSIX4A_DRAFT10_SOURCE with flag -pthread + INCLUDES="-pthread $INCLUDES" + ;; + aix) + THREADS=posix + THREADLIBS=-lpthread + AC_DEFINE(GC_AIX_THREADS) + AC_DEFINE(_REENTRANT) ;; decosf1 | irix | mach | os2 | solaris | dce | vxworks) AC_MSG_ERROR(thread package $THREADS not yet supported) @@ -129,7 +169,22 @@ case "$THREADS" in esac AC_SUBST(THREADLIBS) -AC_CHECK_LIB(dl, dlopen, EXTRA_TEST_LIBS="$EXTRA_TEST_LIBS -ldl") +case "$host" in + powerpc-*-darwin*) + powerpc_darwin=true + ;; +esac +AM_CONDITIONAL(POWERPC_DARWIN,test x$powerpc_darwin = xtrue) + +# We never want libdl on darwin. It is a fake libdl that just ends up making +# dyld calls anyway +case "$host" in + *-*-darwin*) ;; + *) + AC_CHECK_LIB(dl, dlopen, EXTRA_TEST_LIBS="$EXTRA_TEST_LIBS -ldl") + ;; +esac + AC_SUBST(EXTRA_TEST_LIBS) target_all=libgcjgc.la @@ -147,6 +202,9 @@ TARGET_ECOS="$with_ecos" ) addobjs= +addlibs= +addincludes= +addtests= CXXINCLUDES= case "$TARGET_ECOS" in no) @@ -157,21 +215,46 @@ case "$TARGET_ECOS" in addobjs="$addobjs ecos.lo" ;; esac + +if test "${enable_cplusplus}" = yes; then + addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h" + addtests="$addtests test_cpp" +fi + +AM_CONDITIONAL(CPLUSPLUS, test "${enable_cplusplus}" = yes) + AC_SUBST(CXX) AC_SUBST(INCLUDES) AC_SUBST(CXXINCLUDES) +# Configuration of shared libraries +# +AC_MSG_CHECKING(whether to build shared libraries) +AC_ENABLE_SHARED + +case "$host" in + alpha-*-openbsd*) + enable_shared=no + AC_MSG_RESULT(no) + ;; + *) + AC_MSG_RESULT(yes) + ;; +esac + +# Configuration of machine-dependent code +# +AC_MSG_CHECKING(which machine-dependent code should be used) machdep= case "$host" in alpha*-*-openbsd*) machdep="alpha_mach_dep.lo" if test x"${ac_cv_lib_dl_dlopen}" != xyes ; then AC_MSG_WARN(OpenBSD/Alpha without dlopen(). Shared library support is disabled) - AM_DISABLE_SHARED fi ;; - alpha*-*-*) + alpha*-*-linux*) machdep="alpha_mach_dep.lo" ;; i?86-*-solaris2.[[89]]*) @@ -185,12 +268,17 @@ case "$host" in mips-dec-ultrix*) machdep="mips_ultrix_mach-dep.lo" ;; - mips*-*-linux*) + mips-nec-sysv*|mips-unknown-sysv*) ;; + mips*-*-linux*) + ;; mips-*-*) machdep="mips_sgi_mach_dep.lo" AC_DEFINE(NO_EXECUTE_PERMISSION) ;; + sparc-*-netbsd*) + machdep="sparc_netbsd_mach_dep.lo" + ;; sparc-sun-solaris2.3*) machdep="sparc_mach_dep.lo" AC_DEFINE(SUNOS53_SHARED_LIB) @@ -203,16 +291,65 @@ case "$host" in ;; esac if test x"$machdep" = x; then +AC_MSG_RESULT($machdep) machdep="mach_dep.lo" fi addobjs="$addobjs $machdep" AC_SUBST(addobjs) +AC_SUBST(addincludes) +AC_SUBST(addlibs) +AC_SUBST(addtests) + +AC_PROG_LIBTOOL + +# +# Check for AViiON Machines running DGUX +# +AC_MSG_CHECKING(if host is AViiON running DGUX) +ac_is_dgux=no +AC_CHECK_HEADER(sys/dg_sys_info.h, +[ac_is_dgux=yes;]) + +AC_MSG_RESULT($ac_is_dgux) + ## :GOTCHA: we do not check anything but sys/dg_sys_info.h +if test $ac_is_dgux = yes; then + if test "$enable_full_debug" = "yes"; then + CFLAGS="-g -mstandard -DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CXXFLAGS="-g -mstandard -DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + else + CFLAGS="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + CXXFLAGS="-DDGUX -D_DGUX_SOURCE -Di386 -mno-legend -O2" + fi + AC_SUBST(CFLAGS) + AC_SUBST(CXXFLAGS) +fi + +dnl We use these options to decide which functions to include. +AC_ARG_WITH(target-subdir, +[ --with-target-subdir=SUBDIR + configuring with a cross compiler]) +AC_ARG_WITH(cross-host, +[ --with-cross-host=HOST configuring with a cross compiler]) + +# automake wants to see AC_EXEEXT. But we don't need it. And having +# it is actually a problem, because the compiler we're passed can't +# necessarily do a full link. So we fool automake here. +if false; then + # autoconf 2.50 runs AC_EXEEXT by default, and the macro expands + # to nothing, so nothing would remain between `then' and `fi' if it + # were not for the `:' below. + : + AC_EXEEXT +fi dnl As of 4.13a2, the collector will not properly work on Solaris when dnl built with gcc and -O. So we remove -O in the appropriate case. +dnl +AC_MSG_CHECKING(whether Solaris gcc optimization fix is necessary) case "$host" in - sparc-sun-solaris2*) + sparc-sun-solaris2*|*aix*) if test "$GCC" = yes; then + AC_MSG_RESULT(yes) new_CFLAGS= for i in $CFLAGS; do case "$i" in @@ -224,8 +361,11 @@ case "$host" in esac done CFLAGS="$new_CFLAGS" + else + AC_MSG_RESULT(no) fi ;; + *) AC_MSG_RESULT(no) ;; esac dnl We need to override the top-level CFLAGS. This is how we do it. @@ -267,6 +407,9 @@ AC_ARG_ENABLE(full-debug, AC_MSG_WARN("Client must not use -fomit-frame-pointer.") AC_DEFINE(SAVE_CALL_COUNT, 8) ;; + i[3456]86-*-dgux*) + AC_DEFINE(MAKE_BACK_GRAPH) + ;; esac ] fi) diff --git a/boehm-gc/cord/cordbscs.c b/boehm-gc/cord/cordbscs.c index 9fc894d4946..d83f4067de7 100644 --- a/boehm-gc/cord/cordbscs.c +++ b/boehm-gc/cord/cordbscs.c @@ -219,7 +219,7 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) result->len = result_len; result->left = x; result->right = y; - if (depth > MAX_DEPTH) { + if (depth >= MAX_DEPTH) { return(CORD_balance((CORD)result)); } else { return((CORD) result); @@ -260,7 +260,11 @@ CORD CORD_cat(CORD x, CORD y) result->len = result_len; result->left = x; result->right = y; - return((CORD) result); + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } } } diff --git a/boehm-gc/cord/cordprnt.c b/boehm-gc/cord/cordprnt.c index 8d57f0467fb..6ecc00e8410 100644 --- a/boehm-gc/cord/cordprnt.c +++ b/boehm-gc/cord/cordprnt.c @@ -233,7 +233,7 @@ int CORD_vsprintf(CORD * out, CORD format, va_list args) if (width == NONE && prec == NONE) { register char c; - c = va_arg(args, int); + c = (char)va_arg(args, int); CORD_ec_append(result, c); goto done; } @@ -255,12 +255,18 @@ int CORD_vsprintf(CORD * out, CORD format, va_list args) /* Use standard sprintf to perform conversion */ { register char * buf; - va_list vsprintf_args = args; - /* The above does not appear to be sanctioned */ - /* by the ANSI C standard. */ + va_list vsprintf_args; int max_size = 0; int res; - +# ifdef __va_copy + __va_copy(vsprintf_args, args); +# else +# if defined(__GNUC__) /* and probably in other cases */ + va_copy(vsprintf_args, args); +# else + vsprintf_args = args; +# endif +# endif if (width == VARIABLE) width = va_arg(args, int); if (prec == VARIABLE) prec = va_arg(args, int); if (width != NONE) max_size = width; diff --git a/boehm-gc/cord/de_win.c b/boehm-gc/cord/de_win.c index fedbfbe67c2..0bbd676a335 100644 --- a/boehm-gc/cord/de_win.c +++ b/boehm-gc/cord/de_win.c @@ -249,7 +249,7 @@ LRESULT CALLBACK WndProc (HWND hwnd, UINT message, case IDM_HELPABOUT: if( DialogBox( hInstance, "ABOUTBOX", - hwnd, lpfnAboutBox ) ); + hwnd, lpfnAboutBox ) ) InvalidateRect( hwnd, NULL, TRUE ); return( 0 ); case IDM_HELPCONTENTS: diff --git a/boehm-gc/dbg_mlc.c b/boehm-gc/dbg_mlc.c index 57de3dab28c..f640930d901 100644 --- a/boehm-gc/dbg_mlc.c +++ b/boehm-gc/dbg_mlc.c @@ -60,7 +60,7 @@ ptr_t p; # include <stdlib.h> # if defined(LINUX) || defined(SUNOS4) || defined(SUNOS5) \ - || defined(HPUX) || defined(IRIX) || defined(OSF1) + || defined(HPUX) || defined(IRIX5) || defined(OSF1) # define RANDOM() random() # else # define RANDOM() (long)rand() @@ -228,6 +228,8 @@ ptr_t p; #endif /* KEEP_BACK_PTRS */ +# define CROSSES_HBLK(p, sz) \ + (((word)(p + sizeof(oh) + sz - 1) ^ (word)p) >= HBLKSIZE) /* Store debugging info into p. Return displaced pointer. */ /* Assumes we don't hold allocation lock. */ ptr_t GC_store_debug_info(p, sz, string, integer) @@ -243,6 +245,8 @@ word integer; /* But that's expensive. And this way things should only appear */ /* inconsistent while we're in the handler. */ LOCK(); + GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); + GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); # ifdef KEEP_BACK_PTRS ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); # endif @@ -275,6 +279,8 @@ word integer; /* There is some argument that we should disable signals here. */ /* But that's expensive. And this way things should only appear */ /* inconsistent while we're in the handler. */ + GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); + GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); # ifdef KEEP_BACK_PTRS ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); # endif @@ -324,10 +330,11 @@ ptr_t p; { register oh * ohdr = (oh *)GC_base(p); + GC_ASSERT(!I_HOLD_LOCK()); GC_err_printf1("0x%lx (", ((unsigned long)ohdr + sizeof(oh))); GC_err_puts(ohdr -> oh_string); # ifdef SHORT_DBG_HDRS - GC_err_printf1(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int)); + GC_err_printf1(":%ld)\n", (unsigned long)(ohdr -> oh_int)); # else GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int), (unsigned long)(ohdr -> oh_sz)); @@ -342,6 +349,7 @@ ptr_t p; ptr_t p; # endif { + GC_ASSERT(!I_HOLD_LOCK()); if (GC_HAS_DEBUG_INFO(p)) { GC_print_obj(p); } else { @@ -355,6 +363,7 @@ ptr_t p, clobbered_addr; { register oh * ohdr = (oh *)GC_base(p); + GC_ASSERT(!I_HOLD_LOCK()); GC_err_printf2("0x%lx in object at 0x%lx(", (unsigned long)clobbered_addr, (unsigned long)p); if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz)) @@ -376,14 +385,18 @@ ptr_t p, clobbered_addr; void GC_check_heap_proc GC_PROTO((void)); +void GC_print_all_smashed_proc GC_PROTO((void)); + void GC_do_nothing() {} void GC_start_debugging() { # ifndef SHORT_DBG_HDRS GC_check_heap = GC_check_heap_proc; + GC_print_all_smashed = GC_print_all_smashed_proc; # else GC_check_heap = GC_do_nothing; + GC_print_all_smashed = GC_do_nothing; # endif GC_print_heap_obj = GC_debug_print_heap_obj_proc; GC_debugging_started = TRUE; @@ -429,6 +442,62 @@ void GC_start_debugging() return (GC_store_debug_info(result, (word)lb, s, (word)i)); } +# ifdef __STDC__ + GC_PTR GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +# else + GC_PTR GC_debug_malloc_ignore_off_page(lb, s, i) + size_t lb; + char * s; + int i; +# ifdef GC_ADD_CALLER + --> GC_ADD_CALLER not implemented for K&R C +# endif +# endif +{ + GC_PTR result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc_ignore_off_page(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + +# ifdef __STDC__ + GC_PTR GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +# else + GC_PTR GC_debug_malloc_atomic_ignore_off_page(lb, s, i) + size_t lb; + char * s; + int i; +# ifdef GC_ADD_CALLER + --> GC_ADD_CALLER not implemented for K&R C +# endif +# endif +{ + GC_PTR result = GC_malloc_atomic_ignore_off_page(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc_atomic_ignore_off_page(%ld)" + " returning NIL (", (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + # ifdef DBG_HDRS_ALL /* * An allocation function for internal use. @@ -447,7 +516,7 @@ void GC_start_debugging() (unsigned long) lb); return(0); } - ADD_CALL_CHAIN(result, ra); + ADD_CALL_CHAIN(result, GC_RETURN_ADDR); return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); } @@ -461,7 +530,7 @@ void GC_start_debugging() (unsigned long) lb); return(0); } - ADD_CALL_CHAIN(result, ra); + ADD_CALL_CHAIN(result, GC_RETURN_ADDR); return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); } # endif @@ -592,7 +661,7 @@ GC_PTR p; int i; # endif { - GC_PTR result = GC_malloc_uncollectable(lb + DEBUG_BYTES); + GC_PTR result = GC_malloc_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES); if (result == 0) { GC_err_printf1("GC_debug_malloc_uncollectable(%ld) returning NIL (", @@ -618,7 +687,8 @@ GC_PTR p; int i; # endif { - GC_PTR result = GC_malloc_atomic_uncollectable(lb + DEBUG_BYTES); + GC_PTR result = + GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES); if (result == 0) { GC_err_printf1( @@ -774,6 +844,45 @@ void GC_debug_free_inner(GC_PTR p) } #ifndef SHORT_DBG_HDRS + +/* List of smashed objects. We defer printing these, since we can't */ +/* always print them nicely with the allocation lock held. */ +/* We put them here instead of in GC_arrays, since it may be useful to */ +/* be able to look at them with the debugger. */ +#define MAX_SMASHED 20 +ptr_t GC_smashed[MAX_SMASHED]; +unsigned GC_n_smashed = 0; + +# if defined(__STDC__) || defined(__cplusplus) + void GC_add_smashed(ptr_t smashed) +# else + void GC_add_smashed(smashed) + ptr_t smashed; +#endif +{ + GC_ASSERT(GC_is_marked(GC_base(smashed))); + GC_smashed[GC_n_smashed] = smashed; + if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; + /* In case of overflow, we keep the first MAX_SMASHED-1 */ + /* entries plus the last one. */ + GC_have_errors = TRUE; +} + +/* Print all objects on the list. Clear the list. */ +void GC_print_all_smashed_proc () +{ + unsigned i; + + GC_ASSERT(!I_HOLD_LOCK()); + if (GC_n_smashed == 0) return; + GC_err_printf0("GC_check_heap_block: found smashed heap objects:\n"); + for (i = 0; i < GC_n_smashed; ++i) { + GC_print_smashed_obj(GC_base(GC_smashed[i]), GC_smashed[i]); + GC_smashed[i] = 0; + } + GC_n_smashed = 0; +} + /* Check all marked objects in the given block for validity */ /*ARGSUSED*/ # if defined(__STDC__) || defined(__cplusplus) @@ -802,11 +911,7 @@ void GC_debug_free_inner(GC_PTR p) && GC_HAS_DEBUG_INFO((ptr_t)p)) { ptr_t clobbered = GC_check_annotated_obj((oh *)p); - if (clobbered != 0) { - GC_err_printf0( - "GC_check_heap_block: found smashed location at "); - GC_print_smashed_obj((ptr_t)p, clobbered); - } + if (clobbered != 0) GC_add_smashed(clobbered); } word_no += sz; p += sz; @@ -819,9 +924,11 @@ void GC_debug_free_inner(GC_PTR p) void GC_check_heap_proc() { # ifndef SMALL_CONFIG - if (sizeof(oh) & (2 * sizeof(word) - 1) != 0) { - ABORT("Alignment problem: object header has inappropriate size\n"); - } +# ifdef ALIGN_DOUBLE + GC_STATIC_ASSERT((sizeof(oh) & (2 * sizeof(word) - 1)) == 0); +# else + GC_STATIC_ASSERT((sizeof(oh) & (sizeof(word) - 1)) == 0); +# endif # endif GC_apply_to_all_blocks(GC_check_heap_block, (word)0); } @@ -842,12 +949,12 @@ struct closure { # endif { struct closure * result = -# ifdef DBG_HDRS_ALL - (struct closure *) GC_debug_malloc(sizeof (struct closure), - GC_EXTRAS); -# else - (struct closure *) GC_malloc(sizeof (struct closure)); -# endif +# ifdef DBG_HDRS_ALL + (struct closure *) GC_debug_malloc(sizeof (struct closure), + GC_EXTRAS); +# else + (struct closure *) GC_malloc(sizeof (struct closure)); +# endif result -> cl_fn = fn; result -> cl_data = data; @@ -908,7 +1015,7 @@ GC_PTR *ocd; 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", + "GC_debug_register_finalizer called with non-base-pointer 0x%lx\n", obj); } if (0 == fn) { @@ -940,7 +1047,7 @@ GC_PTR *ocd; ptr_t base = GC_base(obj); if (0 == base || (ptr_t)obj - base != sizeof(oh)) { GC_err_printf1( - "GC_register_finalizer_no_order called with non-base-pointer 0x%lx\n", + "GC_debug_register_finalizer_no_order called with non-base-pointer 0x%lx\n", obj); } if (0 == fn) { @@ -973,7 +1080,7 @@ GC_PTR *ocd; 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", + "GC_debug_register_finalizer_ignore_self called with non-base-pointer 0x%lx\n", obj); } if (0 == fn) { diff --git a/boehm-gc/doc/Makefile.am b/boehm-gc/doc/Makefile.am deleted file mode 100644 index 91446305581..00000000000 --- a/boehm-gc/doc/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -# -# -# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED -# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. -# -# Permission is hereby granted to use or copy this program -# for any purpose, provided the above notices are retained on all copies. -# Permission to modify the code and to distribute modified code is granted, -# provided the above notices are retained, and a notice that the code was -# modified is included with the above copyright notice. -# -# Modified by: Grzegorz Jakacki <jakacki at acm dot org> - -## Process this file with automake to produce Makefile.in. - -# installed documentation -# -dist_pkgdata_DATA = barrett_diagram debugging.html gc.man \ - gcdescr.html README README.amiga README.arm.cross \ - README.autoconf README.changes README.contributors \ - README.cords README.DGUX386 README.dj README.environment \ - README.ews4800 README.hp README.linux README.Mac \ - README.MacOSX README.macros README.OS2 README.rs6000 \ - README.sgi README.solaris2 README.uts README.win32 \ - tree.html leak.html gcinterface.html scale.html \ - README.darwin - diff --git a/boehm-gc/doc/Makefile.in b/boehm-gc/doc/Makefile.in deleted file mode 100644 index 9bf1ff5fead..00000000000 --- a/boehm-gc/doc/Makefile.in +++ /dev/null @@ -1,282 +0,0 @@ -# Makefile.in generated by automake 1.6.3 from Makefile.am. -# @configure_input@ - -# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 -# Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# -# -# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED -# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. -# -# Permission is hereby granted to use or copy this program -# for any purpose, provided the above notices are retained on all copies. -# Permission to modify the code and to distribute modified code is granted, -# provided the above notices are retained, and a notice that the code was -# modified is included with the above copyright notice. -# -# Modified by: Grzegorz Jakacki <jakacki at acm dot org> -SHELL = @SHELL@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -sbindir = @sbindir@ -libexecdir = @libexecdir@ -datadir = @datadir@ -sysconfdir = @sysconfdir@ -sharedstatedir = @sharedstatedir@ -localstatedir = @localstatedir@ -libdir = @libdir@ -infodir = @infodir@ -mandir = @mandir@ -includedir = @includedir@ -oldincludedir = /usr/include -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -top_builddir = .. - -ACLOCAL = @ACLOCAL@ -AUTOCONF = @AUTOCONF@ -AUTOMAKE = @AUTOMAKE@ -AUTOHEADER = @AUTOHEADER@ - -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_DATA = @INSTALL_DATA@ -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_HEADER = $(INSTALL_DATA) -transform = @program_transform_name@ -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -host_alias = @host_alias@ -host_triplet = @host@ - -EXEEXT = @EXEEXT@ -OBJEXT = @OBJEXT@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -AMTAR = @AMTAR@ -AR = @AR@ -AS = @AS@ -AWK = @AWK@ -CC = @CC@ -CCAS = @CCAS@ -CCASFLAGS = @CCASFLAGS@ -CFLAGS = @CFLAGS@ -CXX = @CXX@ -CXXFLAGS = @CXXFLAGS@ -CXXINCLUDES = @CXXINCLUDES@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -ECHO = @ECHO@ -EXTRA_TEST_LIBS = @EXTRA_TEST_LIBS@ -GC_CFLAGS = @GC_CFLAGS@ -GC_VERSION = @GC_VERSION@ -INCLUDES = @INCLUDES@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LIBTOOL = @LIBTOOL@ -LN_S = @LN_S@ -MAINT = @MAINT@ -MY_CFLAGS = @MY_CFLAGS@ -OBJDUMP = @OBJDUMP@ -PACKAGE = @PACKAGE@ -RANLIB = @RANLIB@ -STRIP = @STRIP@ -THREADLIBS = @THREADLIBS@ -VERSION = @VERSION@ -addincludes = @addincludes@ -addlibs = @addlibs@ -addobjs = @addobjs@ -addtests = @addtests@ -am__include = @am__include@ -am__quote = @am__quote@ -install_sh = @install_sh@ -target_all = @target_all@ - -# installed documentation -# -dist_pkgdata_DATA = barrett_diagram debugging.html gc.man \ - gcdescr.html README README.amiga README.arm.cross \ - README.autoconf README.changes README.contributors \ - README.cords README.DGUX386 README.dj README.environment \ - README.ews4800 README.hp README.linux README.Mac \ - README.MacOSX README.macros README.OS2 README.rs6000 \ - README.sgi README.solaris2 README.uts README.win32 \ - tree.html leak.html gcinterface.html scale.html \ - README.darwin - -subdir = doc -mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs -CONFIG_CLEAN_FILES = -DIST_SOURCES = -DATA = $(dist_pkgdata_DATA) - -DIST_COMMON = README $(dist_pkgdata_DATA) Makefile.am Makefile.in -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu doc/Makefile -Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -distclean-libtool: - -rm -f libtool -uninstall-info-am: -dist_pkgdataDATA_INSTALL = $(INSTALL_DATA) -install-dist_pkgdataDATA: $(dist_pkgdata_DATA) - @$(NORMAL_INSTALL) - $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) - @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - f="`echo $$p | sed -e 's|^.*/||'`"; \ - echo " $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \ - $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \ - done - -uninstall-dist_pkgdataDATA: - @$(NORMAL_UNINSTALL) - @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ - f="`echo $$p | sed -e 's|^.*/||'`"; \ - echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \ - rm -f $(DESTDIR)$(pkgdatadir)/$$f; \ - done -tags: TAGS -TAGS: - -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) - -top_distdir = .. -distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) - -distdir: $(DISTFILES) - @list='$(DISTFILES)'; for file in $$list; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkinstalldirs) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ - if test -d $$d/$$file; then \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(DATA) - -installdirs: - $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) - -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -rm -f Makefile $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -distclean-am: clean-am distclean-generic distclean-libtool - -dvi: dvi-am - -dvi-am: - -info: info-am - -info-am: - -install-data-am: install-dist_pkgdataDATA - -install-exec-am: - -install-info: install-info-am - -install-man: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -uninstall-am: uninstall-dist_pkgdataDATA uninstall-info-am - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am info info-am install install-am install-data \ - install-data-am install-dist_pkgdataDATA install-exec \ - install-exec-am install-info install-info-am install-man \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool uninstall uninstall-am \ - uninstall-dist_pkgdataDATA uninstall-info-am - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/boehm-gc/doc/README b/boehm-gc/doc/README index 6ac37c6c8ce..29d954f023e 100644 --- a/boehm-gc/doc/README +++ b/boehm-gc/doc/README @@ -1,7 +1,7 @@ Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. -Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. +Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved. The file linux_threads.c is also Copyright (c) 1998 by Fergus Henderson. All rights reserved. @@ -9,8 +9,9 @@ Copyright (c) 1998 by Fergus Henderson. All rights reserved. The files Makefile.am, and configure.in are Copyright (c) 2001 by Red Hat Inc. All rights reserved. -The files config.guess and a few others are copyrighted by the Free -Software Foundation. +Several files supporting GNU-style builds are copyrighted by the Free +Software Foundation, and carry a different license from that given +below. THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. @@ -27,7 +28,7 @@ are GPL'ed, but with an exception that should cover all uses in the collector. (If you are concerned about such things, I recommend you look at the notice in config.guess or ltmain.sh.) -This is version 6.1alpha3 of a conservative garbage collector for C and C++. +This is version 6.3alpha1 of a conservative garbage collector for C and C++. You might find a more recent version of this at @@ -228,10 +229,12 @@ and several of those are compatible with the collector. or equivalent is supplied. Many of these have separate README.system files. - Dynamic libraries are completely supported only under SunOS + Dynamic libraries are completely supported only under SunOS/Solaris, (and even that support is not functional on the last Sun 3 release), -Linux, 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: +Linux, FreeBSD, NetBSD, IRIX 5&6, HP/UX, Win32 (not Win32S) and OSF/1 +on DEC AXP machines plus perhaps a few others listed near the top +of dyn_load.c. On other machines we recommend that you do one of +the following: 1) Add dynamic library support (and send us the code). 2) Use static versions of the libraries. @@ -245,6 +248,8 @@ On other machines we recommend that you do one of the following: In all cases we assume that pointer alignment is consistent with that enforced by the standard C compilers. If you use a nonstandard compiler you may have to adjust the alignment parameters defined in gc_priv.h. +Note that this may also be an issue with packed records/structs, if those +enforce less alignment for pointers. 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 plain MSDOS diff --git a/boehm-gc/doc/README.MacOSX b/boehm-gc/doc/README.MacOSX index 2abf0b400f7..f5333d51ad6 100644 --- a/boehm-gc/doc/README.MacOSX +++ b/boehm-gc/doc/README.MacOSX @@ -1,27 +1 @@ -While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested -it on MacOS X Server. -I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is -no longer necessary. Incremental collection is supported via mprotect/signal. -The current solution isn't really optimal because the signal handler must decode -the faulting PPC machine instruction in order to find the correct heap address. -Further, it must poke around in the register state which the kernel saved away -in some obscure register state structure before it calls the signal handler - -needless to say the layout of this structure is no where documented. -Threads and dynamic libraries are not yet supported (adding dynamic library -support via the low-level dyld API shouldn't be that hard). - -The original MacOS X port was brought to you by Andrew Stone. - - -June, 1 2000 - -Dietmar Planitzer -dave.pl@ping.at - -Note from Andrew Begel: - -One more fix to enable gc.a to link successfully into a shared library for -MacOS X. You have to add -fno-common to the CFLAGS in the Makefile. MacOSX -disallows common symbols in anything that eventually finds its way into a -shared library. (I don't completely understand why, but -fno-common seems to -work and doesn't mess up the garbage collector's functionality). +See README.darwin for the latest Darwin/MacOSX information. diff --git a/boehm-gc/doc/README.changes b/boehm-gc/doc/README.changes index 232938b4375..619ea2e4a49 100644 --- a/boehm-gc/doc/README.changes +++ b/boehm-gc/doc/README.changes @@ -1469,8 +1469,439 @@ Since 6.1 alpha2: - Caused the Solaris and Irix thread creation primitives to call GC_init_inner(). +Since 6.1alpha3: + - Fixed typo in sparc_mach_dep.S, preventing the 64-bit version from + building. Increased 64-bit heap size limit in test.c slightly, since + a functional SPARC collector seems to slightly exceed the old limits. + (Thanks again to Jeff Sturm.) + - Use NPRGREG in solaris_threads.c, thus printing all registers if things + go wrong. + - Added GC_MARKERS environment variable to allow use of a single marker + thread on an MP without confusing the lock implementation. + - Collect much less aggressively in incremental mode with GC_TIME_UNLIMITED. + This is really a purely generational mode, and we can afford to + postpone the collection until the heap is (nearly) full. + - Remove read() wrapper for MPROTECT_VDB. It was causing more harm than + good. It is often no longer needed if system calls avoid writing to + pointerfull heap objects. + - Fix MACOSX test in gcconfig.h. (Thanks to John Clements.) + - Change GC_test_and_set so that it consistently has one argument. + Add spaces to ::: in powerpc assembly code in gc_locks.h. + (Thanks to Ryan Murray.) + - Fixed a formatting error in dbg_mlc.c. Added prototype to GC_abort() + declaration. (Thanks to Michael Smith.) + - Removed "source" argument to GC_find_start(). Eliminate GC_FIND_START(). + - Added win32 recognition code in configure.in. Changed some of the + dllimport/export defines in gc.h. (Thanks to Adam Megacz.) + - GC_malloc_many didn't set hb_last_reclaimed when it called + GC_reclaim_generic. (I'm not sure this matters much, but ...) + - Allocating uncollectable objects with debug information sometimes + allocated objects that were one byte too small, since uncollectable + objects don't have the extra byte added at the end. (Thanks to + Wink Saville for pointing this out.) + - Added a bit more assertion checking to make sure that gcj objects + on free lists never have a nonzero second word. + - Replaced BCC_MAKEFILE with an up-to-date one. (Thanks to + Andre Leiradella.) + - Upgraded libtool, cinfigure.in and some related files to hopefully + support NetBSD/SPARC. (Thanks to Adrian Bunk.) Unfortunately, + libtool 1.4.2 seemed to be buggy due to missing quotes in several + "test" invocations. Fixed those in the ltmain.sh script. + - Some win32-specific patches, including the introduction of + GC_CreateThread. (Thanks to Adam Megacz.) + - Merged in gcj changes from Anthony Green to support embedded systems. + - Tried to consistently rename preprocessed assembly files with a capital + .S extension. + - Use alpha_mach_dep.S on ALPHA again. It doesn't really matter, but this + makes our distribution consistent with the gcc one, avoiding future merge + problems. + - Move GET_MEM definition into gcconfig.h. Include gcconfig.h slightly + later in gc_priv.h to avoid forward references to ptr_t. + - Add some testing of local allocation to test.c. + - Change definition of INVALID_QTID in specific.h. The -1 value was used + inconsistently, and too likely to collide with a valid stack address. + Some general clean-up of specific.[ch]. Added assertions. (Thanks + to Michael Smith for tracking down an intermittent bug to this + general area. I'm not sure it has been squashed yet, however.) + - On Pthread systems it was not safe to call GC_malloc() between fork() + and exec(). According to the applicable standards, it doesn't appear + to be safe to call malloc() or many other libc functions either, thus + it's not clear this is fixable. Added experimental support for + -DHANDLE_FORK in linux_threads.c which tries to support it. It may + succeed if libc does the right thing. I'm not sure whether it does. + (Thanks to Kenneth Schalk for pointing out this issue.) + - Documented thread local allocation primitives to require an + explicit GC_init call. GC_init_parallel is no longer declared to + be a constructor function, since that isn't portable and often + seems to lead to initialization order problems. + - Changed gc_cpp.cc and gc_cpp.h in one more attempt to make them + compatible with Visual C++ 6. (Thanks to Wink Saville for the + patch.) + - Some more patches for Linux on HP PA-RISC. + - Added include/gc_allocator.h. It implements (hopefully) standard + conforming (as opposed to SGI-style) allocators that allocate + collectable (gc_allocator) or GC-traceable, but not collectable + (traceable_allocator) objects. This borrows heavily from libstc++, + which borrows heavily from the SGI implementation, this part of + which was written by Matt Austern. Changed test_cpp.cc to very + minimally test this. + - On Linux/X86, retry mmap with a different start argument. That should + allow the collector to use more (closer to 3GB) of the address space. + (Thanks to Jeffrey Mark Siskind for tracking this down.) + - Force 64 bit alignment with GCJ support. (Reflects Bryce McKinley's + patch to the gcc tree.) + - Refined the choice of sa_handler vs. sa_sigaction in GC_dirty_init + to accomodate some glibc5 systems. (Thanks to Dan Fandrich for the + patch.) + - Compensated for the fact that current versions of glibc set + __libc_stack_end incorrectly on Linux/IA64 while initialization code + is running. This could cause the collector to miss 16 bytes of + the memory stack if GC_malloc or friends where called before main(). + - Mostly integrated Takis Psarogiannakopoulos' port to DG/UX Inix 86. + This will probably take another iteration to work, since his + patch conflicted with the libtool upgrade. + - Added README.arm.cross containing some information about cross- + compiling to an ARM processor from Margaret Fleck. + +Since 6.1alpha4: + - Added GC_finalizer_mem_freed, and changed some of the code that + decided on heap expansion to look at it. Memory explicitly + deallocated by finalizers essentially needs to be counted as reclaimed + by the GC. Otherwise there are cases in which the heap can grow + unboundedly. (Thanks to Mark Reichert for the test case.) + - Integrated Adam Megacz patches to not scan dynamic libraries if + we are compiling with gcc on win32. Otherwise we need structured + exception handling to deal with asynchronously unmapped root + segments, and gcc doesn't directly support that. + - Integrated Anthony Green's patch to support Wine. + - GC_OPERATOR_NEW_ARRAY was misspelled OPERATOR_NEW_ARRAY in several + places, including gc_cpp.cc. (Thanks to Wink Saville for pointing + this out.) + - Integrated Loren James Rittle's Alpha FreeBSD patches. In + response to Richard Henderson's suggestion, these also + changed the declarations of symbols like _end on many platforms to + that they wouldn't mistakenly be declared as short data symbols. + - Integrated changes from the Debian distribution. (Thanks to Ryan Murray + for pointing these out.) Fix C++ comments in POWERPC port. Add ARM32 + incremental GC support. Get rid of USE_GENERIC_PUSH_REGS for alpha/Linux, + this time for real. Use va_copy to get rid of cord printf problems + (finally). + - Close file descriptor used to count cpus. Thanks to Jeff Sturm for + pointing out the omission. + - Don't just drop gcj free lists in GC_start_reclaim, since that can + eventually cause the marker to see a bogus mark descriptor in the + dropped objects. The usual symptom was a very intermittent segmentation + fault in the marker. This mattered only if one of the GC_gcj_malloc + variants was used. (Thanks to Michael Smith, Jeff Sturm, Bryce + McKinley and Tom Tromey for helping to track this down.) + - Fixed Linux and Solaris/64 SPARC configuration. (Thanks to David Miller, + Jeff Sturm, Tom Tromey, and Christian Joensson.) + - Fixed a typo in strdup definition. (Thanks to Gerard A Allan.) + - Changed Makefile.direct to invoke $(CC) to assemble alpha_mach_dep.S. + This is needed on Linux. I'm not sure whether it's better or worse + on Tru64. + - Changed gc_cpp.h once more to declare operator new and friends only in + a Microsoft environment. This may need further fine tuning. (Thanks to + Johannes Schmidt for pointing out that the older code breaks on gcc3.0.4.) + - Don't ever override strdup if it's already macro defined. (Thanks to + Adnan Ali for pointing out the problem.) + - Changed gc_cpp.h yet again to also overload placement new. Due to the + C++ overloading rules, the other overloaded new operations otherwise hide + placement new, which causes many STL uses to break. (Thanks to Reza + Shahidi for reporting this, and to Matt Austern for proposing a fix.) + - Integrated cygwin pthreads support from Dan Bonachea. + - Turn on DYNAMIC_LOADING for NetBSD. (Thanks to Krister Walfridsson.) + - Changed printing code to print more complete GC times. + - Applied Mark Mitchell's Irix patch to correct some bitrot. + - Clarified which object-printing routines in dbg_mlc.c should hold + the allocation lock. Restructured the code to allow reasonable object + printing with -DREDIRECT_MALLOC. + - Fix the Linux mmap code to always start with 0x1000 as the initial hint. + Minor patches for 64-bit AIX, particularly to STACKBOTTOM. + (Thanks again to Jeffrey Mark Siskind.) + - Renamed "SUSPENDED" flag for Solaris threads support to avoid a conflict + with a system header. (Thanks to Philp Brown.) + - Cause win32_threads.c to handle an out of range stack pointer correctly, + though currently with a warning. (Thanks to Jonathan Clark for + observing that win32 applications may temporarily use the stack + pointer for other purposes, and suggesting a fix. Unfortunately, it's + not clear that there is a complete solution to this problem.) + +Since 6.1alpha5: + - Added GC_MAXIMUM_HEAP_SIZE environment variable. + - Fix configure.in for MIPS/LINUX. (Thanks to H.J. Lu.) + - Double page hash table size for -DLARGE_CONFIG. + - Integrated Bo Thorsen's X86-64 support. + - STACKBOTTOM definition for LINUX/MIPS was partially changed back. + (Thanks to H.J. Lu and Hiroshi Kawashima for resolving this.) + - Replaced all occurrences of LINUX_DATA_START in gcconfig.h with + SEARCH_FOR_DATA_START. It doesn't hurt to falll back to a search. + And __data_start doesn't seem to get defined correctly of the GC + library is loaded with LD_PRELOAD, e.g. for leak detection. + - If the GC_find_leak environment variable is set, do a + atexit(GC_gcollect) to give us at least one chance to detect leaks. + This may report some very benign leaks, but ... + - Addeded REDIRECT_FREE. It's necessary if we want leak detection with + LD_PRELOAD. + - Defer printing of leaked objects, as for smashed objects. + - Fixed process and descriptor leak in GC_print_callers. Try for + line number even if we got function name.) + - Ported parallel GC support and thread local allocation to Alpha. + Not yet well-tested. + - Added GC_DUMP_REGULARLY and added finalization statistics to GC_dump(). + - Fixed Makefile.am to mention alpha_mach_dep.S instead of the defunct + alpha_mach_dep.s. (Thanks to Fergus Henderson.) + - Incorporated a change to new_gc_alloc.h, suggested by Johannes Schmidt, + which should make it work with gcc3.1. (I would still like to encourage + use of gc_allocator.h instead.) + - Use alpha_mach_dep.S only on Linux. (It's not clear that this is + optimal, but it otherwise didn't build on Tru64. Thanks to Fergus + Henderson.) + - Added ifdef to guard free() in os_dep.c. Otherwise we get a + compilation error on Irix. (Thanks to Dai Sato.) + - Added an experimental version of GC_memalign to mallocx.c. This can't + always work, since we don't handle alignment requests in the hblk-level + allocator, and we can't handle arbitrary pointer displacements unless + GC_all_interior_pointers is enabled. But it should work for alignment + requests up to HBLKSIZE. This is not yet documented in the standard + places. + - Finally debugged the OSF1/Tru64 thread support. This needs more testing, + since I needed to add a somewhat unconvincing workaround for signal + delivery issues that I don't yet completely understand. But it does + pass my tests, even in parallel GC mode. Incremental GC support is + disabled if thread support is enabled, due to the signal issues. + - Eliminated name-space-incorrect definition of _cdecl from gc_cpp.h. + - Added GC_debug_malloc_replacement and GC_debug_realloc_replacement + declarations to gc.h. On IA64, this is required for REDIRECT_MALLOC + to work correctly with these. + - Fixed Linux USE_PROC_FOR_LIBRARIES to work with a 64-bit /proc format. + +Since 6.1: + - Guard the test for GC_DUMP_REGULARLY in misc.c with + "#ifndef NO_DEBUGGING". Otherwise it fails to build with NO_DEBUGGING + defined. (Thanks to Manuel Serrano.) + - Message about retrying suspend signals was incorrectly generated even when + flag was not set. + - Cleaned up MACOSX/NEXT root registration code. There was apparently a + separate ifdef case in GC_register_data_segments() for no reason. + - Removed MPROTECT_VDB for MACOSX port, based on one negative report. + - Arrange for gc.h and friends to be correctly installed with GNU-style + "make install". + - Enable the GNU-style build facility include C++ support in the library + with --enable-cplusplus. (Thanks to Thomas Maier for some of the patch.) + - Mark from GC_thread_key in linux_threads.c, in case that's allocated + from the garbage collected heap, as it is with our own thread-specific + storage implementation. (Thanks to Jeff Sturm.) + - Mark all free list header blocks if they are heap allocated. This avoids + some unnecessary tracing. And it remains correct if we clear the + root set. (Thanks to Jeff Sturm for identifying the bug.) + - Improved S390/Linux support. Add S390/Linux 64-bit support. (Thanks + to Ulrich Weigand.) + - Corrected the spelling of GC_{M,C}ALLOC_EXPLICTLY_TYPED to + GC_{M,C}ALLOC_EXPLICITLY_TYPED in gc_typed.h. This is technically + an interface change. Based on the fact that nobody reported this, + I suspect/hope there were no clients. + - Cleaned up gc_typed.h so that (1) it adds an extern "C" declaration + when appropriate, (2) doesn't generate references to undefined internal + macros, and (3) allows easier manual construction of descriptors. + - Close the file descriptor used by GC_print_address_map(). + - Set the "close-on-exec" bit for various file descriptors maintained + for the collector's internal use. + - Added a hack to find memory segments owned by the system allocator + under win32. Based on my tests, this tends to eventually find all + segments, though it may take a while. There appear to be cleaner, + but slower solutions under NT/XP. But they rely on an API that's + unsupported under 9X. + - Changed Linux PowerPC stack finding to LINUX_STACKBOTTOM. (Thanks + to Akira Tagoh for pointing out that HEURISTIC1 doesn't work on + 64-bit kernels.) + - Added GC_set_free_space_divisor to avoid some Windows dll issues. + - Added FIXUP_POINTER, POINTER_SHIFT, POINTER_MASK to allow preprocessing + of candidate pointers for tagging, etc. + - Always lock around GC_notify_full_gc(). Simplified code for + invoking GC_notify_full_gc(). + - Changed the way DATASTART is defined on FreeBSD to be robust against + an unmapped page after etext. (Thanks to Hironori Sakamoto for + tracking down the intermittent failure.) + - Made GC_enable() and GC_disable() official. Deprecated direct update + of GC_dont_gc. Changed GC_gcollect to be a noop when garbage collection + is disabled. + - Call GC_register_dynamic_libraries before stopping the world on Linux, + in order to avoid a potential deadlock due to the dl_iterate_phdr lock. + - Introduced a more general mechanism for platform-dependent code to + decide whether the main data segment should be handled separately + from dynamic libraries, or registered by GC_register_dynamic_libraries. + The latter is more reliable and easier on Linux with dl_iterate_phdr. + +Since 6.2alpha1: + - Fixed the completely broken FreeBSD code in 6.2alpha1. (Thanks to + Hironori Sakamoto for the patch.) + - Changed IRIX reference in dbg_mlc.c to IRIX5. (Thanks to Marcus Herbert.) + - Attempted to work around the problems with .S filenames and the SGI + compiler. (Reported by several people. Untested.) + - Worked around an HP/UX make issue with the GNU-style build process. + - Fixed the --enable-cplusplus build machinery to allow builds without + a C++ compiler. (That was always the intent ...) + - Changed the debugging allocation macros to explicitly pass the return + address for Linux and XXXBSD on hardware for which we can't get stack + traces. Use __builtin_return_address(0) to generate it when possible. + Some of the configuration work was cleaned up (good) and moved to gc.h + (bad, but necessary). This should make leak detection more useful + on a number of platforms. (Thanks to Fabian Thylman for the suggestion.) + - Fixed compilation problems in dbg_mlc.c with GC_ADD_CALLER. + - Bumped revision number for dynamic library. + +Since 6.2alpha2: + - Don't include execinfo.h in os_dep.c when it's not needed, and may not exist. + +Since 6.2alpha3: + - Use LINUX_STACKBOTTOM for >= glibc2.2 on Linux/MIPS. (See Debian bug + # 177204) + - Integrated Jeff Sturm and Jesse Rosenstock's MACOSX threads patches. + - Integrated Grzegorz Jakacki's substantial GNU build patch. "Make dist" + should now work for the GNU build process. Documentation files + are installed under share/gc. + - Tweaked gc_cpp.h to again support the Borland compiler. (Thanks to + Rene Girard for pointing out the problems.) + - Updated BCC_MAKEFILE (thanks to Rene Girard). + - Added GC_ASSERT check for minimum thread stack size. + - Added --enable-gc-assertions. + - Added some web documentation to the distribution. Updated it in the + process. + - Separate gc_conf_macros.h from gc.h. + - Added generic GC_THREADS client-defined macro to set the appropriate + GC_XXX_THREADS internal macro. (gc_config_macros.h.) + - Add debugging versions of _ignore_off_page allocation primitves. + - Moved declarations of GC_make_closure and GC_debug_invoke_finalizer + from gc.h to gc_priv.h. + - Reset GC_fail_count even if only a small allocation succeeds. + - Integrated Brian Alliet's patch for dynamic library support on Darwin. + - gc_cpp.h's gc_cleanup destructor called GC_REGISTER_FINALIZER_IGNORE_SELF + when it should have called the lower case version, since it was + explicitly computing a base pointer. + +Since 6.2alpha4: + - GC_invoke_finalizers could, under rare conditions, set + GC_finalizer_mem_freed to an essentially random value. This could + possibly cause unbounded heap growth for long-running applications + under some conditions. (The bug was introduced in 6.1alpha5, and + is not in gcc3.3. Thanks to Ben Hutchings for finding it.) + - Attempted to sanitize the various DLL macros. GC_USE_DLL disappeared. + GC_DLL is used instead. All internal tests are now on GC_DLL. + README.macros is now more precise about the intended meaning. + - Include DllMain in the multithreaded win32 version only if the + collector is actually built as a dll. (Thanks to Mohan Embar for + a version of the patch.) + - Hide the cygwin threadAttach/Detach functions. They were violating our + namespace rules. + - Fixed an assertion in GC_check_heap_proc. Added GC_STATIC_ASSERT. + (Thanks again to Ben Hutchings.) + - Removed some obsolete definitions for Linux/PowerPC in gcconfig.h. + - CORD_cat was not rebalancing unbalanced trees in some cases, violating + a CORD invariant. Also tweaked the rebalancing rule for + CORD_cat_char_star. (Thanks to Alexandr Petrosian for the bug report + and patch.) + - Added hand-coded structured exception handling support to mark.c. + This should enable support of dynamic libraries under win32 with + gcc-compiled code. (Thanks to Ranjit Mathew for the patch.) + Turned on dynamic library scanning for win32/gcc. + - Removed some remnants of read wrapping. (Thanks to Kenneth Schalk.) + GC_USE_LD_WRAP ws probably broken in recent versions. + - The build could fail on some platforms since gcconfig.h could include + declarations mentioning ptr_t, which was not defined, e.g. when if_mach + was built. (Thanks to Yann Dirson for pointing this out.) Also + cleaned up tests for GC_PRIVATE_H in gcconfig.h a bit. + - The GC_LOOP_ON_ABORT environment variable interfered with incremental + collection, since the write fault handler was erroneously overridden. + Handlers are now set up in the correct order. + - It used to be possible to call GC_mark_thread_local_free_lists() while + the world was not stopped during an incremental GC. This was not safe. + Fortunately, it was also unnecessary. Added GC_world_stopped flag + to avoid it. (This caused occasional crashes in GC_set_fl_marks + with thread local allocation and incremental GC. This probably happened + primarily on old, slow multiprocessors.) + - Allowed overriding of MAX_THREADS in win32_threads.c from the build + command line. (Patch from Yannis Bres.) + - Taught the IA64/linux code to determine the register backing store base from + /proc/self/maps after checking the __libc symbol, but before guessing. + (__libc symbols are on the endangered list, and the guess is likely to not + always be right for 2.6 kernels.) Restructured the code to read and parse + /proc/self/maps so it only exists in one place (all platforms). + - The -DUSE_PROC_FOR_LIBRARIES code was broken on Linux. It claimed that it + also registered the main data segment, but didn't actually do so. (I don't + think anyone actually uses this configuration, but ...) + - Made another attempt to get --enablecplusplus to do the right thing. + Since there are unavoidable problems with C programs linking against a + dynamic library that includes C++ code, I separated out the c++ code into + libgccpp. + +Since 6.2alpha5: + - There was extra underscore in the name of GC_save_registers_in_stack + for NetBSD/SPARC. (Thanks to Jaap Boender for the patch.) + - Integrated Brian Alliet's patch for Darwin. This restructured the + linuxthreads/pthreads support to separate generic pthreads support + from more the system-dependent thread-stopping code. I believe this + should make it easier to eliminate the code duplication between + pthreads platforms in the future. The patch included some other + code cleanups. + - Integrated Dan Bonachea's patch to support AIX threads. This required + substantial manual integration, mostly due to conflicts with other + recent threads changes. It may take another iteration to + get it to work. + - Removed HPUX/PA-RISC support from aix_irix_threads.c. It wasn't used + anyway and it cluttered up the code. And anything we can do to migrate + towards generic pthreads support is a good thing. + - Added a more explicit test for tracing of function arguments to test.c. + (Thanks to Dan Grayson.) + - Added Akira Tagoh's PowerPC64 patch. + - Fixed some bit rot in the Cygwin port. (Thanks to Dan Bonachea for + pointing it out.) Gc.h now includes just windows.h, not winbase.h. + - Declared GC_save_regs_in_stack() in gc_priv.h. Remove other declarations. + - Changed --enable-cplusplus to use automake consitionals. The old way + confused libtool. "Make install" didn't work correctly for the old version. + Previously --enable-cplusplus was broken on cygwin. + - Changed the C version of GC_push_regs to fail at compile time if it is + generated with an empty body. This seems to have been the cause of one + or two subtle failures on unusual platforms. Those failures should + now occur at build time and be easily fixable. + +Since 6.2alpha6: + - Integrated a second round of Irix/AIX patches from Dan Bonachea. + Renamed mips_sgi_mach_dep.S back to mips_sgi_mach_dep.s, since it requires + the Irix assembler to do the C preprocessing; gcc -E doesn't work. + - Fixed Makefile.direct for DARWIN. (Thanks to Manuel Serrano.) + - There was a race between GC_pthread_detach and thread exit that could + result in a thread structure being deallocated by GC_pthread_detach + eventhough it was still needed by the thread exit code. (Thanks to + Dick Porter for the small test case that allowed this to be debugged.) + - Fixed version parsing for non-alpha versions in acinclude.m4 and + version checking in version.h. + +Since 6.2: + - Integrated some NetBSD patches forwarded to me by Marc Recht. These + were already in the NetBSD package. + - GC_pthread_create waited for the semaphore even if pthread_create failed. + Thanks to Dick Porter for the pthread_support.c patch. Applied the + analogous fix for aix_irix_threads.c. + - Added Rainer Orth's Tru64 fixes. + - The check for exceeding the thread table size in win32 threadDetach + was incorrect. (Thanks to Alexandr Petrosian for the patch.) + - Applied Andrew Begel's patch to correct some reentrancy issues + with dynamic loading on Darwin. + - GC_CreateThread() was neglecting to duplicate the thread handle in + the table. (Thanks to Tum Nguyen for the patch.) + - Pass +ESdbgasm only on PA-RISC machines with vendor compiler. + (Thanks to Roger Sayle for the patch.) + - Applied more AIX threads patches from Scott Ananian. To do: + - A dynamic libgc.so references dlopen unconditionally, but doesn't link + against libdl. + - GC_proc_fd for Solaris is not correctly updated in response to a + fork() call. Thus incremental collection in the child won't work + correctly. (Thanks to Ben Cottrell for pointing this out.) + - --enable-redirect-malloc is mostly untested and known not to work + on some platforms. - There seem to be outstanding issues on Solaris/X86, possibly with finding the data segment starting address. Information/patches would be appreciated. @@ -1488,7 +1919,4 @@ To do: - Incremental collector should handle large objects better. Currently, it looks like the whole object is treated as dirty if any part of it is. - - Cord/cordprnt.c doesn't build on a few platforms (notably PowerPC), since - we make some unwarranted assumptions about how varargs are handled. This - currently makes the cord-aware versions of printf unusable on some platforms. - Fixing this is unfortunately not trivial. + diff --git a/boehm-gc/doc/README.environment b/boehm-gc/doc/README.environment index c7daddb0aca..d1f3b5c053b 100644 --- a/boehm-gc/doc/README.environment +++ b/boehm-gc/doc/README.environment @@ -5,6 +5,8 @@ platforms. GC_INITIAL_HEAP_SIZE=<bytes> - Initial heap size in bytes. May speed up process start-up. +GC_MAXIMUM_HEAP_SIZE=<bytes> - Maximum collected heap size. + GC_LOOP_ON_ABORT - Causes the collector abort routine to enter a tight loop. This may make it easier to debug, such a process, especially for multithreaded platforms that don't produce usable core @@ -19,6 +21,11 @@ GC_PRINT_STATS - Turn on as much logging as is easily feasible without by setting GC_quiet. On by default if the collector was built without -DSILENT. +GC_DUMP_REGULARLY - Generate a GC debugging dump GC_dump() on startup + and during every collection. Very verbose. Useful + if you have a bug to report, but please include only the + last complete dump. + GC_PRINT_ADDRESS_MAP - Linux only. Dump /proc/self/maps, i.e. various address maps for the process, to stderr on every GC. Useful for mapping root addresses to source for deciphering leak @@ -27,7 +34,14 @@ GC_PRINT_ADDRESS_MAP - Linux only. Dump /proc/self/maps, i.e. various address GC_NPROCS=<n> - Linux w/threads only. Explicitly sets the number of processors that the GC should expect to use. Note that setting this to 1 when multiple processors are available will preserve - correctness, but may lead to really horrible performance. + correctness, but may lead to really horrible performance, + since the lock implementation will immediately yield without + first spinning. + +GC_MARKERS=<n> - Linux w/threads and parallel marker only. Set the number + of marker threads. This is normaly set to the number of + processors. It is safer to adjust GC_MARKERS than GC_NPROCS, + since GC_MARKERS has no impact on the lock implementation. GC_NO_BLACKLIST_WARNING - Prevents the collector from issuing warnings about allocations of very large blocks. @@ -62,6 +76,20 @@ GC_PRINT_BACK_HEIGHT - Print max length of chain through unreachable objects of Conservative Garbage Collectors", POPL 2001, or http://lib.hpl.hp.com/techpubs/2001/HPL-2001-251.html . +GC_RETRY_SIGNALS, GC_NO_RETRY_SIGNALS - Try to compensate for lost + thread suspend signals in linux_threads.c. On by + default for GC_OSF1_THREADS, off otherwise. Note + that this does not work around a possible loss of + thread restart signals. This seems to be necessary for + some versions of Tru64. Since we've previously seen + similar issues on some other operating systems, it + was turned into a runtime flag to enable last-minute + work-arounds. + +The following turn on runtime flags that are also program settable. Checked +only during initialization. We expect that they will usually be set through +other means, but this may help with debugging and testing: + GC_ENABLE_INCREMENTAL - Turn on incremental collection at startup. Note that, depending on platform and collector configuration, this may involve write protecting pieces of the heap to @@ -71,22 +99,20 @@ GC_ENABLE_INCREMENTAL - Turn on incremental collection at startup. Note that, Use with caution. GC_PAUSE_TIME_TARGET - Set the desired garbage collector pause time in msecs. - This only has an effect if incremental collection is enabled. - If a collection requires appreciably more time than this, - the client will be restarted, and the collector will need - to do additional work to compensate. The special value - "999999" indicates that pause time is unlimited, and the - incremental collector will behave completely like a - simple generational collector. If the collector is - configured for parallel marking, and run on a multiprocessor, - incremental collection should only be used with unlimited - pause time. - -The following turn on runtime flags that are also program settable. Checked -only during initialization. We expect that they will usually be set through -other means, but this may help with debugging and testing: + This only has an effect if incremental collection is + enabled. If a collection requires appreciably more time + than this, the client will be restarted, and the collector + will need to do additional work to compensate. The + special value "999999" indicates that pause time is + unlimited, and the incremental collector will behave + completely like a simple generational collector. If + the collector is configured for parallel marking, and + run on a multiprocessor, incremental collection should + only be used with unlimited pause time. -GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection. +GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection. Forces a + collection at program termination to detect leaks that would + otherwise occur after the last GC. GC_ALL_INTERIOR_POINTERS - Turns on GC_all_interior_pointers and thus interior pointer recognition. diff --git a/boehm-gc/doc/README.linux b/boehm-gc/doc/README.linux index efd0a26bfb8..1d0fd4c3fb6 100644 --- a/boehm-gc/doc/README.linux +++ b/boehm-gc/doc/README.linux @@ -1,21 +1,18 @@ See README.alpha for Linux on DEC AXP info. -This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K -and PowerPC are also integrated. They should behave similarly, except that -the PowerPC port lacks incremental GC support, and it is unknown to what -extent the Linux threads code is functional. See below for M68K specific -notes. +This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K, IA64, +SPARC, MIPS, Alpha and PowerPC are also integrated. They should behave +similarly, except that the PowerPC port lacks incremental GC support, and +it is unknown to what extent the Linux threads code is functional. +See below for M68K specific notes. -Incremental GC is supported on Intel IA32 and M68K. +Incremental GC is generally supported. Dynamic libraries are supported on an ELF system. A static executable should be linked with the gcc option "-Wl,-defsym,_DYNAMIC=0". -The collector appears to work with Linux threads. We have seen -intermittent hangs in sem_wait. So far we have been unable to reproduce -these unless the process was being debugged or traced. Thus it's -possible that the only real issue is that the debugger loses -signals on rare occasions. +The collector appears to work reliably with Linux threads, but beware +of older versions of glibc and gdb. The garbage collector uses SIGPWR and SIGXCPU if it is used with Linux threads. These should not be touched by the client program. diff --git a/boehm-gc/doc/README.win32 b/boehm-gc/doc/README.win32 index b1a6ec59664..a40b375f550 100644 --- a/boehm-gc/doc/README.win32 +++ b/boehm-gc/doc/README.win32 @@ -21,6 +21,13 @@ registrations are ignored, but not terribly quickly.) pointers. And the VirtualQuery call has different semantics under the two systems, and under different versions of win32s.) +Win32 applications compiled with some flavor of gcc currently behave +like win32s applications, in that dynamic library data segments are +not scanned. (Gcc does not directly support Microsoft's "structured +exception handling". It turns out that use of this feature is +unavoidable if you scan arbitrary memory segments obtained from +VirtualQuery.) + The collector test program "gctest" is linked as a GUI application, but does not open any windows. Its output appears in the file "gc.log". It may be started from the file manager. The hour glass @@ -50,13 +57,20 @@ This appears to cause problems under Windows NT and Windows 2000 (but not Windows 95/98) if the memory is later passed to CreateDIBitmap. To work around this problem, build the collector with -DUSE_GLOBAL_ALLOC. This is currently incompatible with -DUSE_MUNMAP. (Thanks to Jonathan -Clark for tracking this down.) +Clark for tracking this down. There's some chance this may be fixed +in 6.1alpha4, since we now separate heap sections with an unused page.) For Microsoft development tools, rename NT_MAKEFILE as MAKEFILE. (Make sure that the CPU environment variable is defined to be i386.) In order to use the gc_cpp.h C++ interface, all client code should include gc_cpp.h. +If you would prefer a VC++.NET project file, ask boehm@acm.org. One has +been contributed, but it seems to contain some absolute paths etc., so +it can presumably only be a starting point, and is not in the standard +distribution. It is unclear (to me, Hans Boehm) whether it is feasible to +change that. + Clients may need to define GC_NOT_DLL before including gc.h, if the collector was built as a static library (as it normally is in the absence of thread support). @@ -64,7 +78,7 @@ absence of thread support). For GNU-win32, use the regular makefile, possibly after uncommenting the line "include Makefile.DLLs". The latter should be necessary only if you want to package the collector as a DLL. The GNU-win32 port is -believed to work only for b18, not b19, probably dues to linker changes +believed to work only for b18, not b19, probably due to linker changes in b19. This is probably fixable with a different definition of DATASTART and DATAEND in gcconfig.h. @@ -147,7 +161,7 @@ To compile the collector and testing programs use the command: All programs using gc should be compiled with 4-byte alignment. For further explanations on this see comments about Borland. -If gc compiled as dll, the macro ``GC_DLL'' should be defined before +If the gc is compiled as dll, the macro ``GC_DLL'' should be defined before including "gc.h" (for example, with -DGC_DLL compiler option). It's important, otherwise resulting programs will not run. diff --git a/boehm-gc/doc/debugging.html b/boehm-gc/doc/debugging.html index 04773fa66a3..22273fed4bc 100644 --- a/boehm-gc/doc/debugging.html +++ b/boehm-gc/doc/debugging.html @@ -248,8 +248,12 @@ The <TT>hb_last_reclaimed</tt> field will identify the collection number during which its block was last swept. <LI> Verify that the offending object still has its correct contents at this point. -The call <TT>GC_is_marked(p)</tt> from the debugger to verify that the -object has not been marked, and is about to be reclaimed. +Then call <TT>GC_is_marked(p)</tt> from the debugger to verify that the +object has not been marked, and is about to be reclaimed. Note that +<TT>GC_is_marked(p)</tt> expects the real address of an object (the +address of the debug header if there is one), and thus it may +be more appropriate to call <TT>GC_is_marked(GC_base(p))</tt> +instead. <LI> Determine a path from a root, i.e. static variable, stack, or register variable, to the reclaimed object. Call <TT>GC_is_marked(q)</tt> for each object diff --git a/boehm-gc/dyn_load.c b/boehm-gc/dyn_load.c index 71ad5fb1d78..5ae0e0dcccd 100644 --- a/boehm-gc/dyn_load.c +++ b/boehm-gc/dyn_load.c @@ -55,9 +55,10 @@ !defined(MSWIN32) && !defined(MSWINCE) && \ !(defined(ALPHA) && defined(OSF1)) && \ !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \ - !defined(RS6000) && !defined(SCO_ELF) && \ + !defined(RS6000) && !defined(SCO_ELF) && !defined(DGUX) && \ !(defined(FREEBSD) && defined(__ELF__)) && \ - !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) + !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \ + !defined(DARWIN) --> We only know how to find data segments of dynamic libraries for the --> above. Additional SVR4 variants might not be too --> hard to add. @@ -80,7 +81,7 @@ #endif #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \ - (defined(FREEBSD) && defined(__ELF__)) || \ + (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \ (defined(NETBSD) && defined(__ELF__)) || defined(HURD) # include <stddef.h> # include <elf.h> @@ -264,7 +265,7 @@ void GC_register_dynamic_libraries() # endif /* SUNOS */ #if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \ - (defined(FREEBSD) && defined(__ELF__)) || \ + (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \ (defined(NETBSD) && defined(__ELF__)) || defined(HURD) @@ -282,56 +283,23 @@ extern ssize_t GC_repeat_read(int fd, char *buf, size_t count); /* Repeatedly read until buffer is filled, or EOF is encountered */ /* Defined in os_dep.c. */ -static char *parse_map_entry(char *buf_ptr, word *start, word *end, - char *prot_buf, unsigned int *maj_dev); +char *GC_parse_map_entry(char *buf_ptr, word *start, word *end, + char *prot_buf, unsigned int *maj_dev); +word GC_apply_to_maps(word (*fn)(char *)); + /* From os_dep.c */ -void GC_register_dynamic_libraries() +word GC_register_map_entries(char *maps) { - int f; - int result; char prot_buf[5]; - int maps_size; - char maps_temp[32768]; - char *maps_buf; - char *buf_ptr; + char *buf_ptr = maps; int count; word start, end; - unsigned int maj_dev, min_dev; + unsigned int maj_dev; word least_ha, greatest_ha; unsigned i; word datastart = (word)(DATASTART); - /* Read /proc/self/maps */ - /* Note that we may not allocate, and thus can't use stdio. */ - f = open("/proc/self/maps", O_RDONLY); - if (-1 == f) ABORT("Couldn't open /proc/self/maps"); - /* stat() doesn't work for /proc/self/maps, so we have to - read it to find out how large it is... */ - maps_size = 0; - do { - result = GC_repeat_read(f, maps_temp, sizeof(maps_temp)); - if (result <= 0) ABORT("Couldn't read /proc/self/maps"); - maps_size += result; - } while (result == sizeof(maps_temp)); - - if (maps_size > sizeof(maps_temp)) { - /* If larger than our buffer, close and re-read it. */ - close(f); - f = open("/proc/self/maps", O_RDONLY); - if (-1 == f) ABORT("Couldn't open /proc/self/maps"); - maps_buf = alloca(maps_size); - if (NULL == maps_buf) ABORT("/proc/self/maps alloca failed"); - result = GC_repeat_read(f, maps_buf, maps_size); - if (result <= 0) ABORT("Couldn't read /proc/self/maps"); - } else { - /* Otherwise use the fixed size buffer */ - maps_buf = maps_temp; - } - - close(f); - maps_buf[result] = '\0'; - buf_ptr = maps_buf; - /* Compute heap bounds. Should be done by add_to_heap? */ + /* Compute heap bounds. FIXME: Should be done by add_to_heap? */ least_ha = (word)(-1); greatest_ha = 0; for (i = 0; i < GC_n_heap_sects; ++i) { @@ -342,11 +310,10 @@ void GC_register_dynamic_libraries() } if (greatest_ha < (word)GC_scratch_last_end_ptr) greatest_ha = (word)GC_scratch_last_end_ptr; - for (;;) { - - buf_ptr = parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev); - if (buf_ptr == NULL) return; + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev); + if (buf_ptr == NULL) return 1; if (prot_buf[1] == 'w') { /* This is a writable mapping. Add it to */ /* the root set unless it is already otherwise */ @@ -358,16 +325,7 @@ void GC_register_dynamic_libraries() # ifdef THREADS if (GC_segment_is_thread_stack(start, end)) continue; # endif - /* The rest of this assumes that there is no mapping */ - /* spanning the beginning of the data segment, or extending */ - /* beyond the entire heap at both ends. */ - /* Empirically these assumptions hold. */ - - if (start < (word)DATAEND && end > (word)DATAEND) { - /* Rld may use space at the end of the main data */ - /* segment. Thus we add that in. */ - start = (word)DATAEND; - } + /* We no longer exclude the main data segment. */ if (start < least_ha && end > least_ha) { end = least_ha; } @@ -377,7 +335,14 @@ void GC_register_dynamic_libraries() if (start >= least_ha && end <= greatest_ha) continue; GC_add_roots_inner((char *)start, (char *)end, TRUE); } - } + } + return 1; +} + +void GC_register_dynamic_libraries() +{ + if (!GC_apply_to_maps(GC_register_map_entries)) + ABORT("Failed to read /proc for library registration."); } /* We now take care of the main data segment ourselves: */ @@ -387,60 +352,6 @@ GC_bool GC_register_main_static_data() } # define HAVE_REGISTER_MAIN_STATIC_DATA -// -// parse_map_entry parses an entry from /proc/self/maps so we can -// locate all writable data segments that belong to shared libraries. -// The format of one of these entries and the fields we care about -// is as follows: -// XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n -// ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ -// start end prot maj_dev -// 0 9 18 32 -// -// The parser is called with a pointer to the entry and the return value -// is either NULL or is advanced to the next entry(the byte after the -// trailing '\n'.) -// -#define OFFSET_MAP_START 0 -#define OFFSET_MAP_END 9 -#define OFFSET_MAP_PROT 18 -#define OFFSET_MAP_MAJDEV 32 - -static char *parse_map_entry(char *buf_ptr, word *start, word *end, - char *prot_buf, unsigned int *maj_dev) -{ - int i; - unsigned int val; - char *tok; - - if (buf_ptr == NULL || *buf_ptr == '\0') { - return NULL; - } - - memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4); // do the protections first - prot_buf[4] = '\0'; - - if (prot_buf[1] == 'w') { // we can skip all of this if it's not writable - - tok = buf_ptr; - buf_ptr[OFFSET_MAP_START+8] = '\0'; - *start = strtoul(tok, NULL, 16); - - tok = buf_ptr+OFFSET_MAP_END; - buf_ptr[OFFSET_MAP_END+8] = '\0'; - *end = strtoul(tok, NULL, 16); - - buf_ptr += OFFSET_MAP_MAJDEV; - tok = buf_ptr; - while (*buf_ptr != ':') buf_ptr++; - *buf_ptr++ = '\0'; - *maj_dev = strtoul(tok, NULL, 16); - } - - while (*buf_ptr && *buf_ptr++ != '\n'); - - return buf_ptr; -} #endif /* USE_PROC_FOR_LIBRARIES */ @@ -508,6 +419,7 @@ GC_bool GC_register_dynamic_libraries_dl_iterate_phdr() GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE); # endif } + return TRUE; } else { return FALSE; @@ -534,6 +446,16 @@ GC_bool GC_register_main_static_data() #if defined(NETBSD) # include <sys/exec_elf.h> +/* for compatibility with 1.4.x */ +# ifndef DT_DEBUG +# define DT_DEBUG 21 +# endif +# ifndef PT_LOAD +# define PT_LOAD 1 +# endif +# ifndef PF_W +# define PF_W 2 +# endif #else # include <elf.h> #endif @@ -1048,7 +970,7 @@ void GC_register_dynamic_libraries() len = ldi->ldinfo_next; GC_add_roots_inner( ldi->ldinfo_dataorg, - (unsigned long)ldi->ldinfo_dataorg + (ptr_t)(unsigned long)ldi->ldinfo_dataorg + ldi->ldinfo_datasize, TRUE); ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; @@ -1056,7 +978,133 @@ void GC_register_dynamic_libraries() } #endif /* RS6000 */ +#ifdef DARWIN + +#include <mach-o/dyld.h> +#include <mach-o/getsect.h> + +/*#define DARWIN_DEBUG*/ + +const static struct { + const char *seg; + const char *sect; +} GC_dyld_sections[] = { + { SEG_DATA, SECT_DATA }, + { SEG_DATA, SECT_BSS }, + { SEG_DATA, SECT_COMMON } +}; + +#ifdef DARWIN_DEBUG +static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) { + unsigned long i,c; + c = _dyld_image_count(); + for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr) + return _dyld_get_image_name(i); + return NULL; +} +#endif + +/* This should never be called by a thread holding the lock */ +static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) { + unsigned long start,end,i; + const struct section *sec; + for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) { + sec = getsectbynamefromheader( + hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect); + if(sec == NULL || sec->size == 0) continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n", + start,end,sec->size,GC_dyld_name_for_hdr(hdr)); +# endif + GC_add_roots((char*)start,(char*)end); + } +# ifdef DARWIN_DEBUG + GC_print_static_roots(); +# endif +} + +/* This should never be called by a thread holding the lock */ +static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) { + unsigned long start,end,i; + const struct section *sec; + for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) { + sec = getsectbynamefromheader( + hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect); + if(sec == NULL || sec->size == 0) continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n", + start,end,sec->size,GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start,(char*)end); + } +# ifdef DARWIN_DEBUG + GC_print_static_roots(); +# endif +} + +void GC_register_dynamic_libraries() { + /* Currently does nothing. The callbacks are setup by GC_init_dyld() + The dyld library takes it from there. */ +} + +/* The _dyld_* functions have an internal lock so no _dyld functions + can be called while the world is stopped without the risk of a deadlock. + Because of this we MUST setup callbacks BEFORE we ever stop the world. + This should be called BEFORE any thread in created and WITHOUT the + allocation lock held. */ + +void GC_init_dyld() { + static GC_bool initialized = FALSE; + char *bind_fully_env = NULL; + + if(initialized) return; + +# ifdef DARWIN_DEBUG + GC_printf0("Registering dyld callbacks...\n"); +# endif + + /* Apple's Documentation: + When you call _dyld_register_func_for_add_image, the dynamic linker runtime + calls the specified callback (func) once for each of the images that is + currently loaded into the program. When a new image is added to the program, + your callback is called again with the mach_header for the new image, and the + virtual memory slide amount of the new image. + + This WILL properly register already linked libraries and libraries + linked in the future + */ + + _dyld_register_func_for_add_image(GC_dyld_image_add); + _dyld_register_func_for_remove_image(GC_dyld_image_remove); + + /* Set this early to avoid reentrancy issues. */ + initialized = TRUE; + + bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH"); + + if (bind_fully_env == NULL) { +# ifdef DARWIN_DEBUG + GC_printf0("Forcing full bind of GC code...\n"); +# endif + + if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc)) + GC_abort("_dyld_bind_fully_image_containing_address failed"); + } + +} + +#define HAVE_REGISTER_MAIN_STATIC_DATA +GC_bool GC_register_main_static_data() +{ + /* Already done through dyld callbacks */ + return FALSE; +} +#endif /* DARWIN */ #else /* !DYNAMIC_LOADING */ diff --git a/boehm-gc/finalize.c b/boehm-gc/finalize.c index a316010a680..3b9d9f5ea1e 100644 --- a/boehm-gc/finalize.c +++ b/boehm-gc/finalize.c @@ -207,7 +207,8 @@ signed_word * log_size_ptr; UNLOCK(); ENABLE_SIGNALS(); # endif - new_dl = GC_oom_fn(sizeof(struct disappearing_link)); + new_dl = (struct disappearing_link *) + GC_oom_fn(sizeof(struct disappearing_link)); if (0 == new_dl) { GC_finalization_failures++; return(0); @@ -433,7 +434,8 @@ finalization_mark_proc * mp; UNLOCK(); ENABLE_SIGNALS(); # endif - new_fo = GC_oom_fn(sizeof(struct finalizable_object)); + new_fo = (struct finalizable_object *) + GC_oom_fn(sizeof(struct finalizable_object)); if (0 == new_fo) { GC_finalization_failures++; return; @@ -759,8 +761,9 @@ int GC_should_invoke_finalizers GC_PROTO((void)) /* Should be called without allocation lock. */ int GC_invoke_finalizers() { - register struct finalizable_object * curr_fo; - register int count = 0; + struct finalizable_object * curr_fo; + int count = 0; + word mem_freed_before; DCL_LOCK_STATE; while (GC_finalize_now != 0) { @@ -768,6 +771,9 @@ int GC_invoke_finalizers() DISABLE_SIGNALS(); LOCK(); # endif + if (count == 0) { + mem_freed_before = GC_mem_freed; + } curr_fo = GC_finalize_now; # ifdef THREADS if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo); @@ -789,6 +795,11 @@ int GC_invoke_finalizers() GC_free((GC_PTR)curr_fo); # endif } + if (count != 0 && mem_freed_before != GC_mem_freed) { + LOCK(); + GC_finalizer_mem_freed += (GC_mem_freed - mem_freed_before); + UNLOCK(); + } return count; } @@ -801,7 +812,9 @@ void GC_notify_or_invoke_finalizers GC_PROTO((void)) if (GC_finalize_now == 0) return; if (!GC_finalize_on_demand) { (void) GC_invoke_finalizers(); - GC_ASSERT(GC_finalize_now == 0); +# ifndef THREADS + GC_ASSERT(GC_finalize_now == 0); +# endif /* Otherwise GC can run concurrently and add more */ return; } if (GC_finalizer_notifier != (void (*) GC_PROTO((void)))0 @@ -839,3 +852,17 @@ void GC_notify_or_invoke_finalizers GC_PROTO((void)) return(result); } +#if !defined(NO_DEBUGGING) + +void GC_print_finalization_stats() +{ + struct finalizable_object *fo = GC_finalize_now; + size_t ready = 0; + + GC_printf2("%lu finalization table entries; %lu disappearing links\n", + GC_fo_entries, GC_dl_entries); + for (; 0 != fo; fo = fo_next(fo)) ++ready; + GC_printf1("%lu objects are eligible for immediate finalization\n", ready); +} + +#endif /* NO_DEBUGGING */ diff --git a/boehm-gc/gc_cpp.cc b/boehm-gc/gc_cpp.cc index a97091c817c..f8b803a8baa 100644 --- a/boehm-gc/gc_cpp.cc +++ b/boehm-gc/gc_cpp.cc @@ -26,15 +26,13 @@ Authors: John R. Ellis and Jesse Hull #include "gc_cpp.h" -#ifndef _MSC_VER -/* In the Visual C++ case, we moved this into the header. */ void* operator new( size_t size ) { return GC_MALLOC_UNCOLLECTABLE( size );} void operator delete( void* obj ) { GC_FREE( obj );} -#ifdef OPERATOR_NEW_ARRAY +#ifdef GC_OPERATOR_NEW_ARRAY void* operator new[]( size_t size ) { return GC_MALLOC_UNCOLLECTABLE( size );} @@ -42,8 +40,22 @@ void* operator new[]( size_t size ) { void operator delete[]( void* obj ) { GC_FREE( obj );} -#endif /* OPERATOR_NEW_ARRAY */ +#endif /* GC_OPERATOR_NEW_ARRAY */ -#endif /* _MSC_VER */ +#ifdef _MSC_VER + +// This new operator is used by VC++ in case of Debug builds ! +void* operator new( size_t size, + int ,//nBlockUse, + const char * szFileName, + int nLine ) +{ +#ifndef GC_DEBUG + return GC_malloc_uncollectable( size ); +#else + return GC_debug_malloc_uncollectable(size, szFileName, nLine); +#endif +} +#endif /* _MSC_VER */ diff --git a/boehm-gc/gc_dlopen.c b/boehm-gc/gc_dlopen.c index eafaa2b1b46..4c690edcfe4 100644 --- a/boehm-gc/gc_dlopen.c +++ b/boehm-gc/gc_dlopen.c @@ -19,12 +19,14 @@ /* * This used to be in dyn_load.c. It was extracted into a separate file * to avoid having to link against libdl.{a,so} if the client doesn't call - * dlopen. -HB + * dlopen. Of course this fails if the collector is in a dynamic + * library. -HB */ #include "private/gc_priv.h" -# if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) +# if (defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS)) \ + || defined(GC_SOLARIS_THREADS) # if defined(dlopen) && !defined(GC_USE_LD_WRAP) /* To support various threads pkgs, gc.h interposes on dlopen by */ @@ -44,19 +46,14 @@ /* calls in either a multithreaded environment, or if the library */ /* initialization code allocates substantial amounts of GC'ed memory. */ /* But I don't know of a better solution. */ - /* This can still deadlock if the client explicitly starts a GC */ - /* during the dlopen. He shouldn't do that. */ - static GC_bool disable_gc_for_dlopen() + static void disable_gc_for_dlopen() { - GC_bool result; LOCK(); - result = GC_dont_gc; while (GC_incremental && GC_collection_in_progress()) { GC_collect_a_little_inner(1000); } - GC_dont_gc = TRUE; + ++GC_dont_gc; UNLOCK(); - return(result); } /* Redefine dlopen to guarantee mutual exclusion with */ @@ -74,10 +71,9 @@ #endif { void * result; - GC_bool dont_gc_save; # ifndef USE_PROC_FOR_LIBRARIES - dont_gc_save = disable_gc_for_dlopen(); + disable_gc_for_dlopen(); # endif # ifdef GC_USE_LD_WRAP result = (void *)__real_dlopen(path, mode); @@ -85,7 +81,7 @@ result = dlopen(path, mode); # endif # ifndef USE_PROC_FOR_LIBRARIES - GC_dont_gc = dont_gc_save; + GC_enable(); /* undoes disable_gc_for_dlopen */ # endif return(result); } diff --git a/boehm-gc/gcj_mlc.c b/boehm-gc/gcj_mlc.c index 89f0d728a57..a10a66f926e 100644 --- a/boehm-gc/gcj_mlc.c +++ b/boehm-gc/gcj_mlc.c @@ -157,6 +157,7 @@ DCL_LOCK_STATE; GC_words_allocd += lw; } *(void **)op = ptr_to_struct_containing_descr; + GC_ASSERT(((void **)op)[1] == 0); UNLOCK(); } else { LOCK(); diff --git a/boehm-gc/if_mach.c b/boehm-gc/if_mach.c index fd6009e1c35..3dcccf21f5a 100644 --- a/boehm-gc/if_mach.c +++ b/boehm-gc/if_mach.c @@ -14,7 +14,7 @@ char ** envp; if (strcmp(MACH_TYPE, argv[1]) != 0) return(0); if (strcmp(OS_TYPE, "") != 0 && strcmp(argv[2], "") != 0 && strcmp(OS_TYPE, argv[2]) != 0) return(0); - printf("^^^^Starting command^^^^\n"); + fprintf(stderr, "^^^^Starting command^^^^\n"); fflush(stdout); execvp(argv[3], argv+3); perror("Couldn't execute"); diff --git a/boehm-gc/include/Makefile.in b/boehm-gc/include/Makefile.in index 65bd34e2e33..94ece1a258a 100644 --- a/boehm-gc/include/Makefile.in +++ b/boehm-gc/include/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -66,9 +66,11 @@ target_triplet = @target@ AR = @AR@ AS = @AS@ CC = @CC@ +CFLAGS = @CFLAGS@ CPP = @CPP@ CXX = @CXX@ CXXCPP = @CXXCPP@ +CXXFLAGS = @CXXFLAGS@ CXXINCLUDES = @CXXINCLUDES@ DLLTOOL = @DLLTOOL@ EXEEXT = @EXEEXT@ @@ -89,10 +91,15 @@ RANLIB = @RANLIB@ STRIP = @STRIP@ THREADLIBS = @THREADLIBS@ VERSION = @VERSION@ +addincludes = @addincludes@ +addlibs = @addlibs@ addobjs = @addobjs@ +addtests = @addtests@ gc_basedir = @gc_basedir@ mkinstalldirs = @mkinstalldirs@ target_all = @target_all@ +toolexecdir = @toolexecdir@ +toolexeclibdir = @toolexeclibdir@ AUTOMAKE_OPTIONS = foreign diff --git a/boehm-gc/include/gc.h b/boehm-gc/include/gc.h index 69075b0669e..d49cd9d60b1 100644 --- a/boehm-gc/include/gc.h +++ b/boehm-gc/include/gc.h @@ -30,91 +30,7 @@ # define _GC_H -/* - * Some tests for old macros. These violate our namespace rules and will - * disappear shortly. Use the GC_ names. - */ -#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS) -# define GC_SOLARIS_THREADS -#endif -#if defined(_SOLARIS_PTHREADS) -# define GC_SOLARIS_PTHREADS -#endif -#if defined(IRIX_THREADS) -# define GC_IRIX_THREADS -#endif -#if defined(HPUX_THREADS) -# define GC_HPUX_THREADS -#endif -#if defined(OSF1_THREADS) -# define GC_OSF1_THREADS -#endif -#if defined(LINUX_THREADS) -# define GC_LINUX_THREADS -#endif -#if defined(WIN32_THREADS) -# define GC_WIN32_THREADS -#endif -#if defined(USE_LD_WRAP) -# define GC_USE_LD_WRAP -#endif - -#if !defined(_REENTRANT) && (defined(GC_SOLARIS_THREADS) \ - || defined(GC_SOLARIS_PTHREADS) \ - || defined(GC_HPUX_THREADS) \ - || defined(GC_LINUX_THREADS)) -# define _REENTRANT - /* Better late than never. This fails if system headers that */ - /* depend on this were previously included. */ -#endif - -#if defined(GC_SOLARIS_PTHREADS) && !defined(GC_SOLARIS_THREADS) -# define GC_SOLARIS_THREADS -#endif - -# if defined(GC_SOLARIS_PTHREADS) || defined(GC_FREEBSD_THREADS) || \ - defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) || \ - defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) -# define GC_PTHREADS -# endif - -# define __GC -# include <stddef.h> -# ifdef _WIN32_WCE -/* Yet more kluges for WinCE */ -# include <stdlib.h> /* size_t is defined here */ - typedef long ptrdiff_t; /* ptrdiff_t is not defined */ -# endif - -#if defined(__MINGW32__) &&defined(_DLL) && !defined(GC_NOT_DLL) -# ifdef GC_BUILD -# define GC_API __declspec(dllexport) -# else -# define GC_API __declspec(dllimport) -# endif -#endif - -#if (defined(__DMC__) || defined(_MSC_VER)) \ - && (defined(_DLL) && !defined(GC_NOT_DLL) \ - || defined(GC_DLL)) -# ifdef GC_BUILD -# define GC_API extern __declspec(dllexport) -# else -# define GC_API __declspec(dllimport) -# endif -#endif - -#if defined(__WATCOMC__) && defined(GC_DLL) -# ifdef GC_BUILD -# define GC_API extern __declspec(dllexport) -# else -# define GC_API extern __declspec(dllimport) -# endif -#endif - -#ifndef GC_API -#define GC_API extern -#endif +# include "gc_config_macros.h" # if defined(__STDC__) || defined(__cplusplus) # define GC_PROTO(args) args @@ -154,7 +70,7 @@ GC_API int GC_parallel; /* GC is parallelized for performance on */ /* Env variable GC_NPROC is set to > 1, or */ /* GC_NPROC is not set and this is an MP. */ /* If GC_parallel is set, incremental */ - /* collection is aonly partially functional, */ + /* collection is only partially functional, */ /* and may not be desirable. */ @@ -215,8 +131,14 @@ GC_API void (* GC_finalizer_notifier)(); /* thread, which will call GC_invoke_finalizers */ /* in response. */ -GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */ - /* because it's not safe. */ +GC_API int GC_dont_gc; /* != 0 ==> Dont collect. In versions 6.2a1+, */ + /* this overrides explicit GC_gcollect() calls. */ + /* Used as a counter, so that nested enabling */ + /* and disabling work correctly. Should */ + /* normally be updated with GC_enable() and */ + /* GC_disable() calls. */ + /* Direct assignment to GC_dont_gc is */ + /* deprecated. */ GC_API int GC_dont_expand; /* Dont expand heap unless explicitly requested */ @@ -316,9 +238,18 @@ GC_API unsigned long GC_time_limit; /* enabled. */ # define GC_TIME_UNLIMITED 999999 /* Setting GC_time_limit to this value */ - /* will disable the "pause time exceeded */ + /* will disable the "pause time exceeded"*/ /* tests. */ +/* Public procedures */ + +/* Initialize the collector. This is only required when using thread-local + * allocation, since unlike the regular allocation routines, GC_local_malloc + * is not self-initializing. If you use GC_local_malloc you should arrange + * to call this somehow (e.g. from a constructor) before doing any allocation. + */ +GC_API void GC_init GC_PROTO((void)); + /* * general purpose allocation routines, with roughly malloc calling conv. * The atomic versions promise that no relevant pointers are contained @@ -419,17 +350,21 @@ GC_API void GC_clear_roots GC_PROTO((void)); GC_API void GC_add_roots GC_PROTO((char * low_address, char * high_address_plus_1)); +/* Remove a root segment. Wizards only. */ +GC_API void GC_remove_roots GC_PROTO((char * low_address, + char * high_address_plus_1)); + /* Add a displacement to the set of those considered valid by the */ /* collector. GC_register_displacement(n) means that if p was returned */ /* by GC_malloc, then (char *)p + n will be considered to be a valid */ -/* pointer to n. N must be small and less than the size of p. */ +/* pointer to p. N must be small and less than the size of p. */ /* (All pointers to the interior of objects from the stack are */ /* considered valid in any case. This applies to heap objects and */ /* static data.) */ /* Preferably, this should be called before any other GC procedures. */ /* Calling it later adds to the probability of excess memory */ /* retention. */ -/* This is a no-op if the collector was compiled with recognition of */ +/* This is a no-op if the collector has recognition of */ /* arbitrary interior pointers enabled, which is now the default. */ GC_API void GC_register_displacement GC_PROTO((GC_word n)); @@ -464,9 +399,18 @@ GC_API size_t GC_get_free_bytes GC_PROTO((void)); GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); /* Return the total number of bytes allocated in this process. */ -/* Never decreases. */ +/* Never decreases, except due to wrapping. */ GC_API size_t GC_get_total_bytes GC_PROTO((void)); +/* Disable garbage collection. Even GC_gcollect calls will be */ +/* ineffective. */ +GC_API void GC_disable GC_PROTO((void)); + +/* Reenable garbage collection. GC_disable() and GC_enable() calls */ +/* nest. Garbage collection is enabled if the number of calls to both */ +/* both functions is equal. */ +GC_API void GC_enable GC_PROTO((void)); + /* Enable incremental/generational collection. */ /* Not advisable unless dirty bits are */ /* available or most heap objects are */ @@ -474,7 +418,11 @@ GC_API size_t GC_get_total_bytes GC_PROTO((void)); /* Don't use in leak finding mode. */ /* Ignored if GC_dont_gc is true. */ /* Only the generational piece of this is */ -/* functional if GC_parallel is TRUE. */ +/* functional if GC_parallel is TRUE */ +/* or if GC_time_limit is GC_TIME_UNLIMITED. */ +/* Causes GC_local_gcj_malloc() to revert to */ +/* locked allocation. Must be called */ +/* before any GC_local_gcj_malloc() calls. */ GC_API void GC_enable_incremental GC_PROTO((void)); /* Does incremental mode write-protect pages? Returns zero or */ @@ -518,6 +466,42 @@ GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); # define GC_RETURN_ADDR (GC_word)__return_address #endif +#ifdef __linux__ +# include <features.h> +# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \ + && !defined(__ia64__) +# define GC_HAVE_BUILTIN_BACKTRACE +# define GC_CAN_SAVE_CALL_STACKS +# endif +# if defined(__i386__) || defined(__x86_64__) +# define GC_CAN_SAVE_CALL_STACKS +# endif +#endif + +#if defined(__sparc__) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +/* If we're on an a platform on which we can't save call stacks, but */ +/* gcc is normally used, we go ahead and define GC_ADD_CALLER. */ +/* We make this decision independent of whether gcc is actually being */ +/* used, in order to keep the interface consistent, and allow mixing */ +/* of compilers. */ +/* This may also be desirable if it is possible but expensive to */ +/* retrieve the call chain. */ +#if (defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__FreeBSD__)) & !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_ADD_CALLER +# if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) + /* gcc knows how to retrieve return address, but we don't know */ + /* how to generate call stacks. */ +# define GC_RETURN_ADDR (GC_word)__builtin_return_address(0) +# else + /* Just pass 0 for gcc compatibility. */ +# define GC_RETURN_ADDR 0 +# endif +#endif + #ifdef GC_ADD_CALLER # define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ # define GC_EXTRA_PARAMS GC_word ra, GC_CONST char * s, int i @@ -536,18 +520,42 @@ GC_API GC_PTR GC_debug_malloc_uncollectable GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API GC_PTR GC_debug_malloc_stubborn GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_ignore_off_page + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_atomic_ignore_off_page + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); GC_API GC_PTR GC_debug_realloc GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, GC_EXTRA_PARAMS)); - GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); + +/* Routines that allocate objects with debug information (like the */ +/* above), but just fill in dummy file and line number information. */ +/* Thus they can serve as drop-in malloc/realloc replacements. This */ +/* can be useful for two reasons: */ +/* 1) It allows the collector to be built with DBG_HDRS_ALL defined */ +/* even if some allocation calls come from 3rd party libraries */ +/* that can't be recompiled. */ +/* 2) On some platforms, the file and line information is redundant, */ +/* since it can be reconstructed from a stack trace. On such */ +/* platforms it may be more convenient not to recompile, e.g. for */ +/* leak detection. This can be accomplished by instructing the */ +/* linker to replace malloc/realloc with these. */ +GC_API GC_PTR GC_debug_malloc_replacement GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_debug_realloc_replacement + GC_PROTO((GC_PTR object_addr, size_t size_in_bytes)); + # ifdef GC_DEBUG # define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) # define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) -# define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ - GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_ignore_off_page(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_atomic_ignore_off_page(sz, GC_EXTRAS) # define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) # define GC_FREE(p) GC_debug_free(p) # define GC_REGISTER_FINALIZER(p, f, d, of, od) \ @@ -566,6 +574,10 @@ GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); # define GC_MALLOC(sz) GC_malloc(sz) # define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) # define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_ignore_off_page(sz) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_atomic_ignore_off_page(sz) # define GC_REALLOC(old, sz) GC_realloc(old, sz) # define GC_FREE(p) GC_free(p) # define GC_REGISTER_FINALIZER(p, f, d, of, od) \ @@ -644,7 +656,8 @@ GC_API void GC_debug_register_finalizer /* itself. There is a stylistic argument that this is wrong, */ /* but it's unavoidable for C++, since the compiler may */ /* silently introduce these. It's also benign in that specific */ -/* case. */ +/* case. And it helps if finalizable objects are split to */ +/* avoid cycles. */ /* Note that cd will still be viewed as accessible, even if it */ /* refers to the object itself. */ GC_API void GC_register_finalizer_ignore_self @@ -717,11 +730,6 @@ GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); /* 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. */ -GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); -GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); - /* Returns !=0 if GC_invoke_finalizers has something to do. */ GC_API int GC_should_invoke_finalizers GC_PROTO((void)); @@ -738,6 +746,10 @@ GC_API int GC_invoke_finalizers GC_PROTO((void)); typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); /* Returns old warning procedure. */ + +GC_API GC_word GC_set_free_space_divisor GC_PROTO((GC_word value)); + /* Set free_space_divisor. See above for definition. */ + /* Returns old value. */ /* The following is intended to be used by a higher level */ /* (e.g. Java-like) finalization facility. It is expected */ @@ -873,14 +885,17 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ #endif /* THREADS && !SRC_M3 */ -#if defined(GC_WIN32_THREADS) +#if defined(GC_WIN32_THREADS) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) # include <windows.h> /* * All threads must be created using GC_CreateThread, so that they will be - * recorded in the thread table. + * recorded in the thread table. For backwards compatibility, this is not + * technically true if the GC is built as a dynamic library, since it can + * and does then use DllMain to keep track of thread creations. But new code + * should be built to call GC_CreateThread. */ - HANDLE WINAPI GC_CreateThread( + GC_API HANDLE GC_CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); @@ -902,7 +917,7 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif # endif /* defined(_WIN32_WCE) */ -#endif /* defined(GC_WIN32_THREADS) */ +#endif /* defined(GC_WIN32_THREADS) && !cygwin */ /* * If you are planning on putting @@ -914,13 +929,18 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # define GC_INIT() { extern end, etext; \ GC_noop(&end, &etext); } #else -# if (defined(__CYGWIN32__) && defined(GC_USE_DLL)) || defined (_AIX) +# if defined(__CYGWIN32__) && defined(GC_DLL) || defined (_AIX) /* - * Similarly gnu-win32 DLLs need explicit initialization + * Similarly gnu-win32 DLLs need explicit initialization from + * the main program, as does AIX. */ # define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } # else +# if defined(__APPLE__) && defined(__MACH__) +# define GC_INIT() { GC_init(); } +# else # define GC_INIT() +# endif # endif #endif diff --git a/boehm-gc/include/gc_cpp.h b/boehm-gc/include/gc_cpp.h index ceb73f50a65..d789a3731e3 100644 --- a/boehm-gc/include/gc_cpp.h +++ b/boehm-gc/include/gc_cpp.h @@ -134,7 +134,9 @@ by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. #include "gc.h" #ifndef THINK_CPLUS -#define _cdecl +# define GC_cdecl +#else +# define GC_cdecl _cdecl #endif #if ! defined( GC_NO_OPERATOR_NEW_ARRAY ) \ @@ -159,12 +161,22 @@ enum GCPlacement {UseGC, class gc {public: inline void* operator new( size_t size ); inline void* operator new( size_t size, GCPlacement gcp ); + inline void* operator new( size_t size, void *p ); + /* Must be redefined here, since the other overloadings */ + /* hide the global definition. */ inline void operator delete( void* obj ); +# ifndef __BORLANDC__ /* Confuses the Borland compiler. */ + inline void operator delete( void*, void* ); +# endif #ifdef GC_OPERATOR_NEW_ARRAY inline void* operator new[]( size_t size ); inline void* operator new[]( size_t size, GCPlacement gcp ); + inline void* operator new[]( size_t size, void *p ); inline void operator delete[]( void* obj ); +# ifndef __BORLANDC__ + inline void gc::operator delete[]( void*, void* ); +# endif #endif /* GC_OPERATOR_NEW_ARRAY */ }; /* @@ -176,7 +188,7 @@ class gc_cleanup: virtual public gc {public: inline gc_cleanup(); inline virtual ~gc_cleanup(); private: - inline static void _cdecl cleanup( void* obj, void* clientData );}; + inline static void GC_cdecl cleanup( void* obj, void* clientData );}; /* Instances of classes derived from "gc_cleanup" will be allocated in the collected heap by default. When the collector discovers an @@ -211,7 +223,6 @@ inline void* operator new( classes derived from "gc_cleanup" or containing members derived from "gc_cleanup". */ -#ifdef GC_OPERATOR_NEW_ARRAY #ifdef _MSC_VER /** This ensures that the system default operator new[] doesn't get @@ -220,42 +231,24 @@ inline void* operator new( * There seems to be really redirect new in this environment without * including this everywhere. */ - inline void *operator new[]( size_t size ) - { - return GC_MALLOC_UNCOLLECTABLE( size ); - } - - inline void operator delete[](void* obj) - { - GC_FREE(obj); - }; - - inline void* operator new( size_t size) - { - return GC_MALLOC_UNCOLLECTABLE( size); - }; + void *operator new[]( size_t size ); + + void operator delete[](void* obj); - inline void operator delete(void* obj) - { - GC_FREE(obj); - }; + void* operator new( size_t size); + void operator delete(void* obj); -// This new operator is used by VC++ in case of Debug builds ! - inline void* operator new( size_t size, + // This new operator is used by VC++ in case of Debug builds ! + void* operator new( size_t size, int ,//nBlockUse, const char * szFileName, - int nLine - ) { -# ifndef GC_DEBUG - return GC_malloc_uncollectable( size ); -# else - return GC_debug_malloc_uncollectable(size, szFileName, nLine); -# endif - } - + int nLine ); #endif /* _MSC_VER */ + +#ifdef GC_OPERATOR_NEW_ARRAY + inline void* operator new[]( size_t size, GCPlacement gcp, @@ -283,9 +276,15 @@ inline void* gc::operator new( size_t size, GCPlacement gcp ) { else return GC_MALLOC_UNCOLLECTABLE( size );} +inline void* gc::operator new( size_t size, void *p ) { + return p;} + inline void gc::operator delete( void* obj ) { GC_FREE( obj );} +#ifndef __BORLANDC__ + inline void gc::operator delete( void*, void* ) {} +#endif #ifdef GC_OPERATOR_NEW_ARRAY @@ -295,14 +294,21 @@ inline void* gc::operator new[]( size_t size ) { inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { return gc::operator new( size, gcp );} +inline void* gc::operator new[]( size_t size, void *p ) { + return p;} + inline void gc::operator delete[]( void* obj ) { gc::operator delete( obj );} + +#ifndef __BORLANDC__ + inline void gc::operator delete[]( void*, void* ) {} +#endif #endif /* GC_OPERATOR_NEW_ARRAY */ inline gc_cleanup::~gc_cleanup() { - GC_REGISTER_FINALIZER_IGNORE_SELF( GC_base(this), 0, 0, 0, 0 );} + GC_register_finalizer_ignore_self( GC_base(this), 0, 0, 0, 0 );} inline void gc_cleanup::cleanup( void* obj, void* displ ) { ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup();} diff --git a/boehm-gc/include/gc_local_alloc.h b/boehm-gc/include/gc_local_alloc.h index 1e58730cfe8..88e29e9a11f 100644 --- a/boehm-gc/include/gc_local_alloc.h +++ b/boehm-gc/include/gc_local_alloc.h @@ -33,6 +33,9 @@ * -DTHREAD_LOCAL_ALLOC, which is currently supported only on Linux. * * The debugging allocators use standard, not thread-local allocation. + * + * These routines normally require an explicit call to GC_init(), though + * that may be done from a constructor function. */ #ifndef GC_LOCAL_ALLOC_H diff --git a/boehm-gc/include/gc_mark.h b/boehm-gc/include/gc_mark.h index 0856d16fdae..9ddba2ca527 100644 --- a/boehm-gc/include/gc_mark.h +++ b/boehm-gc/include/gc_mark.h @@ -129,7 +129,9 @@ extern GC_PTR GC_greatest_plausible_heap_addr; /* be reserved for exceptional cases. That will ensure that */ /* performance of this call is not extremely performance critical. */ /* (Otherwise we would need to inline GC_mark_and_push completely, */ -/* which would tie the client code to a fixed colllector version.) */ +/* which would tie the client code to a fixed collector version.) */ +/* Note that mark procedures should explicitly call FIXUP_POINTER() */ +/* if required. */ struct GC_ms_entry *GC_mark_and_push GC_PROTO((GC_PTR obj, struct GC_ms_entry * mark_stack_ptr, diff --git a/boehm-gc/include/gc_pthread_redirects.h b/boehm-gc/include/gc_pthread_redirects.h index 47284fbc97a..842518cfcc4 100644 --- a/boehm-gc/include/gc_pthread_redirects.h +++ b/boehm-gc/include/gc_pthread_redirects.h @@ -52,15 +52,30 @@ int GC_pthread_create(pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); +#ifndef GC_DARWIN_THREADS int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); +#endif int GC_pthread_join(pthread_t thread, void **retval); int GC_pthread_detach(pthread_t thread); +#if defined(GC_OSF1_THREADS) \ + && defined(_PTHREAD_USE_MANGLED_NAMES_) && !defined(_PTHREAD_USE_PTDNAM_) +/* Unless the compiler supports #pragma extern_prefix, the Tru64 UNIX + <pthread.h> redefines some POSIX thread functions to use mangled names. + If so, undef them before redefining. */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach +#endif + # define pthread_create GC_pthread_create -# define pthread_sigmask GC_pthread_sigmask # define pthread_join GC_pthread_join # define pthread_detach GC_pthread_detach + +#ifndef GC_DARWIN_THREADS +# define pthread_sigmask GC_pthread_sigmask # define dlopen GC_dlopen +#endif #endif /* GC_xxxxx_THREADS */ diff --git a/boehm-gc/include/gc_typed.h b/boehm-gc/include/gc_typed.h index 2e0598f204c..905734b8da0 100644 --- a/boehm-gc/include/gc_typed.h +++ b/boehm-gc/include/gc_typed.h @@ -29,14 +29,21 @@ # include "gc.h" # endif +#ifdef __cplusplus + extern "C" { +#endif typedef GC_word * GC_bitmap; /* The least significant bit of the first word is one if */ /* the first word in the object may be a pointer. */ +# define GC_WORDSZ (8*sizeof(GC_word)) # define GC_get_bit(bm, index) \ - (((bm)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) + (((bm)[index/GC_WORDSZ] >> (index%GC_WORDSZ)) & 1) # define GC_set_bit(bm, index) \ - (bm)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) + (bm)[index/GC_WORDSZ] |= ((GC_word)1 << (index%GC_WORDSZ)) +# define GC_WORD_OFFSET(t, f) (offsetof(t,f)/sizeof(GC_word)) +# define GC_WORD_LEN(t) (sizeof(t)/ sizeof(GC_word)) +# define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ-1)/GC_WORDSZ) typedef GC_word GC_descr; @@ -57,6 +64,16 @@ GC_API GC_descr GC_make_descriptor GC_PROTO((GC_bitmap bm, size_t len)); /* is intended to be called once per type, not once */ /* per allocation. */ +/* It is possible to generate a descriptor for a C type T with */ +/* word aligned pointer fields f1, f2, ... as follows: */ +/* */ +/* GC_descr T_descr; */ +/* GC_word T_bitmap[GC_BITMAP_SIZE(T)] = {0}; */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f1)); */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f2)); */ +/* ... */ +/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T)); */ + GC_API GC_PTR GC_malloc_explicitly_typed GC_PROTO((size_t size_in_bytes, GC_descr d)); /* Allocate an object whose layout is described by d. */ @@ -79,15 +96,18 @@ GC_API GC_PTR GC_calloc_explicitly_typed /* Returned object is cleared. */ #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) +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) GC_MALLOC(bytes) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) GC_MALLOC(n*bytes) #else -# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) \ +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \ GC_malloc_explicitly_typed(bytes, d) -# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) \ +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ GC_calloc_explicitly_typed(n, bytes, d) #endif /* !GC_DEBUG */ +#ifdef __cplusplus + } /* matches extern "C" */ +#endif #endif /* _GC_TYPED_H */ diff --git a/boehm-gc/include/new_gc_alloc.h b/boehm-gc/include/new_gc_alloc.h index aad9446543e..20a2fabf83c 100644 --- a/boehm-gc/include/new_gc_alloc.h +++ b/boehm-gc/include/new_gc_alloc.h @@ -64,6 +64,14 @@ #endif #endif +/* A hack to deal with gcc 3.1. If you are using gcc3.1 and later, */ +/* you should probably really use gc_allocator.h instead. */ +#if defined (__GNUC__) && \ + (__GNUC > 3 || (__GNUC__ == 3 && (__GNUC_MINOR__ >= 1))) +# define simple_alloc __simple_alloc +#endif + + #define GC_ALLOC_H diff --git a/boehm-gc/include/private/dbg_mlc.h b/boehm-gc/include/private/dbg_mlc.h index 5378835811c..e2003e6c44f 100644 --- a/boehm-gc/include/private/dbg_mlc.h +++ b/boehm-gc/include/private/dbg_mlc.h @@ -115,16 +115,24 @@ typedef struct { #ifdef SHORT_DBG_HDRS # define DEBUG_BYTES (sizeof (oh)) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES #else /* Add space for END_FLAG, but use any extra space that was already */ /* added to catch off-the-end pointers. */ -# define DEBUG_BYTES (sizeof (oh) + sizeof (word) - EXTRA_BYTES) + /* For uncollectable objects, the extra byte is not added. */ +# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word)) +# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) #endif #define USR_PTR_FROM_BASE(p) ((ptr_t)(p) + sizeof(oh)) /* Round bytes to words without adding extra byte at end. */ #define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) +/* ADD_CALL_CHAIN stores a (partial) call chain into an object */ +/* header. It may be called with or without the allocation */ +/* lock. */ +/* PRINT_CALL_CHAIN prints the call chain stored in an object */ +/* to stderr. It requires that we do not hold the lock. */ #ifdef SAVE_CALL_CHAIN # define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) # define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) diff --git a/boehm-gc/include/private/gc_hdrs.h b/boehm-gc/include/private/gc_hdrs.h index dd615455f3b..96749ab1bf0 100644 --- a/boehm-gc/include/private/gc_hdrs.h +++ b/boehm-gc/include/private/gc_hdrs.h @@ -70,7 +70,7 @@ extern hdr * GC_invalid_header; /* header for an imaginary block */ #define ADVANCE(p, hhdr, source) \ { \ hdr * new_hdr = GC_invalid_header; \ - p = GC_FIND_START(p, hhdr, &new_hdr, (word)source); \ + p = GC_find_start(p, hhdr, &new_hdr); \ hhdr = new_hdr; \ } diff --git a/boehm-gc/include/private/gc_locks.h b/boehm-gc/include/private/gc_locks.h index 9b91ada917a..775176b3151 100644 --- a/boehm-gc/include/private/gc_locks.h +++ b/boehm-gc/include/private/gc_locks.h @@ -141,23 +141,24 @@ # if defined(POWERPC) inline static int GC_test_and_set(volatile unsigned int *addr) { int oldval; - int temp = 1; // locked value + int temp = 1; /* locked value */ __asm__ __volatile__( - "1:\tlwarx %0,0,%3\n" // load and reserve - "\tcmpwi %0, 0\n" // if load is - "\tbne 2f\n" // non-zero, return already set - "\tstwcx. %2,0,%1\n" // else store conditional - "\tbne- 1b\n" // retry if lost reservation - "2:\t\n" // oldval is zero if we set + "1:\tlwarx %0,0,%3\n" /* load and reserve */ + "\tcmpwi %0, 0\n" /* if load is */ + "\tbne 2f\n" /* non-zero, return already set */ + "\tstwcx. %2,0,%1\n" /* else store conditional */ + "\tbne- 1b\n" /* retry if lost reservation */ + "\tsync\n" /* import barrier */ + "2:\t\n" /* oldval is zero if we set */ : "=&r"(oldval), "=p"(addr) : "r"(temp), "1"(addr) - : "memory"); - return (int)oldval; + : "cr0","memory"); + return oldval; } # define GC_TEST_AND_SET_DEFINED inline static void GC_clear(volatile unsigned int *addr) { - __asm__ __volatile__("eieio" ::: "memory"); + __asm__ __volatile__("eieio" : : : "memory"); *(addr) = 0; } # define GC_CLEAR_DEFINED @@ -174,12 +175,18 @@ " bne %2,2f\n" " xor %0,%3,%0\n" " stl_c %0,%1\n" +# ifdef __ELF__ " beq %0,3f\n" +# else + " beq %0,1b\n" +# endif " mb\n" "2:\n" +# ifdef __ELF__ ".section .text2,\"ax\"\n" "3: br 1b\n" ".previous" +# endif :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue) :"Ir" (1), "m" (*addr) :"memory"); @@ -187,8 +194,11 @@ return oldvalue; } # define GC_TEST_AND_SET_DEFINED - /* Should probably also define GC_clear, since it needs */ - /* a memory barrier ?? */ + inline static void GC_clear(volatile unsigned int *addr) { + __asm__ __volatile__("mb" : : : "memory"); + *(addr) = 0; + } +# define GC_CLEAR_DEFINED # endif /* ALPHA */ # ifdef ARM32 inline static int GC_test_and_set(volatile unsigned int *addr) { @@ -206,22 +216,30 @@ # define GC_TEST_AND_SET_DEFINED # endif /* ARM32 */ # ifdef S390 - inline static int GC_test_and_set(volatile unsigned int *addr) { - int ret; - __asm__ __volatile__ ( - " l %0,0(%2)\n" - "0: cs %0,%1,0(%2)\n" - " jl 0b" - : "=&d" (ret) - : "d" (1), "a" (addr) - : "cc", "memory"); - return ret; - } + inline static int GC_test_and_set(volatile unsigned int *addr) { + int ret; + __asm__ __volatile__ ( + " l %0,0(%2)\n" + "0: cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (ret) + : "d" (1), "a" (addr) + : "cc", "memory"); + return ret; + } # endif # endif /* __GNUC__ */ # if (defined(ALPHA) && !defined(__GNUC__)) -# define GC_test_and_set(addr) __cxx_test_and_set_atomic(addr, 1) +# ifndef OSF1 + --> We currently assume that if gcc is not used, we are + --> running under Tru64. +# endif +# include <machine/builtins.h> +# include <c_asm.h> +# define GC_test_and_set(addr) __ATOMIC_EXCH_LONG(addr, 1) # define GC_TEST_AND_SET_DEFINED +# define GC_clear(addr) { asm("mb"); *(volatile unsigned *)addr = 0; } +# define GC_CLEAR_DEFINED # endif # if defined(MSWIN32) # define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1) @@ -234,14 +252,51 @@ # define GC_TEST_AND_SET_DEFINED # elif __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 -# define GC_test_and_set(addr) test_and_set(addr, 1) +# ifdef __GNUC__ +# define GC_test_and_set(addr) _test_and_set((void *)addr,1) +# else +# define GC_test_and_set(addr) test_and_set((void *)addr,1) +# endif # else -# define GC_test_and_set(addr) __test_and_set(addr,1) +# define GC_test_and_set(addr) __test_and_set32((void *)addr,1) # define GC_clear(addr) __lock_release(addr); # define GC_CLEAR_DEFINED # endif # define GC_TEST_AND_SET_DEFINED # endif /* MIPS */ +# if defined(_AIX) +# include <sys/atomic_op.h> +# if (defined(_POWER) || defined(_POWERPC)) +# if defined(__GNUC__) + inline static void GC_memsync() { + __asm__ __volatile__ ("sync" : : : "memory"); + } +# else +# ifndef inline +# define inline __inline +# endif +# pragma mc_func GC_memsync { \ + "7c0004ac" /* sync (same opcode used for dcs)*/ \ + } +# endif +# else +# error dont know how to memsync +# endif + inline static int GC_test_and_set(volatile unsigned int * addr) { + int oldvalue = 0; + if (compare_and_swap((void *)addr, &oldvalue, 1)) { + GC_memsync(); + return 0; + } else return 1; + } +# define GC_TEST_AND_SET_DEFINED + inline static void GC_clear(volatile unsigned int *addr) { + GC_memsync(); + *(addr) = 0; + } +# define GC_CLEAR_DEFINED + +# endif # if 0 /* defined(HP_PA) */ /* The official recommendation seems to be to not use ldcw from */ /* user mode. Since multithreaded incremental collection doesn't */ @@ -275,7 +330,7 @@ # endif # if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ - && !defined(GC_IRIX_THREADS) + && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) # define NO_THREAD (pthread_t)(-1) # include <pthread.h> # if defined(PARALLEL_MARK) @@ -306,12 +361,12 @@ { char result; __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1" - : "=m"(*(addr)), "=r"(result) - : "r" (new_val), "0"(*(addr)), "a"(old) : "memory"); + : "+m"(*(addr)), "=r"(result) + : "r" (new_val), "a"(old) : "memory"); return (GC_bool) result; } # endif /* !GENERIC_COMPARE_AND_SWAP */ - inline static void GC_memory_write_barrier() + inline static void GC_memory_barrier() { /* We believe the processor ensures at least processor */ /* consistent ordering. Thus a compiler barrier */ @@ -319,6 +374,37 @@ __asm__ __volatile__("" : : : "memory"); } # endif /* I386 */ + +# if defined(POWERPC) +# if !defined(GENERIC_COMPARE_AND_SWAP) + /* Returns TRUE if the comparison succeeded. */ + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + int result, dummy; + __asm__ __volatile__( + "1:\tlwarx %0,0,%5\n" + "\tcmpw %0,%4\n" + "\tbne 2f\n" + "\tstwcx. %3,0,%2\n" + "\tbne- 1b\n" + "\tsync\n" + "\tli %1, 1\n" + "\tb 3f\n" + "2:\tli %1, 0\n" + "3:\t\n" + : "=&r" (dummy), "=r" (result), "=p" (addr) + : "r" (new_val), "r" (old), "2"(addr) + : "cr0","memory"); + return (GC_bool) result; + } +# endif /* !GENERIC_COMPARE_AND_SWAP */ + inline static void GC_memory_barrier() + { + __asm__ __volatile__("sync" : : : "memory"); + } +# endif /* POWERPC */ + # if defined(IA64) # if !defined(GENERIC_COMPARE_AND_SWAP) inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, @@ -330,31 +416,71 @@ # endif /* !GENERIC_COMPARE_AND_SWAP */ # if 0 /* Shouldn't be needed; we use volatile stores instead. */ - inline static void GC_memory_write_barrier() + inline static void GC_memory_barrier() { __sync_synchronize (); } # endif /* 0 */ # endif /* IA64 */ +# if defined(ALPHA) +# if !defined(GENERIC_COMPARE_AND_SWAP) +# if defined(__GNUC__) + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + unsigned long was_equal; + unsigned long temp; + + __asm__ __volatile__( + "1: ldq_l %0,%1\n" + " cmpeq %0,%4,%2\n" + " mov %3,%0\n" + " beq %2,2f\n" + " stq_c %0,%1\n" + " beq %0,1b\n" + "2:\n" + " mb\n" + :"=&r" (temp), "=m" (*addr), "=&r" (was_equal) + : "r" (new_val), "Ir" (old) + :"memory"); + return was_equal; + } +# else /* !__GNUC__ */ + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + return __CMP_STORE_QUAD(addr, old, new_val, addr); + } +# endif /* !__GNUC__ */ +# endif /* !GENERIC_COMPARE_AND_SWAP */ +# ifdef __GNUC__ + inline static void GC_memory_barrier() + { + __asm__ __volatile__("mb" : : : "memory"); + } +# else +# define GC_memory_barrier() asm("mb") +# endif /* !__GNUC__ */ +# endif /* ALPHA */ # if defined(S390) # if !defined(GENERIC_COMPARE_AND_SWAP) - inline static GC_bool GC_compare_and_exchange(volatile C_word *addr, - GC_word old, GC_word new_val) - { - int retval; - __asm__ __volatile__ ( -# ifndef __s390x__ - " cs %1,%2,0(%3)\n" -# else - " csg %1,%2,0(%3)\n" -# endif - " ipm %0\n" - " srl %0,28\n" - : "=&d" (retval), "+d" (old) - : "d" (new_val), "a" (addr) - : "cc", "memory"); - return retval == 0; - } + inline static GC_bool GC_compare_and_exchange(volatile C_word *addr, + GC_word old, GC_word new_val) + { + int retval; + __asm__ __volatile__ ( +# ifndef __s390x__ + " cs %1,%2,0(%3)\n" +# else + " csg %1,%2,0(%3)\n" +# endif + " ipm %0\n" + " srl %0,28\n" + : "=&d" (retval), "+d" (old) + : "d" (new_val), "a" (addr) + : "cc", "memory"); + return retval == 0; + } # endif # endif # if !defined(GENERIC_COMPARE_AND_SWAP) @@ -427,8 +553,12 @@ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ pthread_mutex_unlock(&GC_allocate_ml); } # else /* !GC_ASSERTIONS */ +# if defined(NO_PTHREAD_TRYLOCK) +# define LOCK() GC_lock(); +# else /* !defined(NO_PTHREAD_TRYLOCK) */ # define LOCK() \ { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); } +# endif # define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) # endif /* !GC_ASSERTIONS */ # endif /* USE_PTHREAD_LOCKS */ @@ -450,7 +580,7 @@ /* on Irix anymore. */ # include <mutex.h> - extern unsigned long GC_allocate_lock; + extern volatile unsigned int GC_allocate_lock; /* This is not a mutex because mutexes that obey the (optional) */ /* POSIX scheduling rules are subject to convoys in high contention */ /* applications. This is basically a spin lock. */ @@ -471,11 +601,18 @@ } # define EXIT_GC() GC_collecting = 0; # endif /* GC_IRIX_THREADS */ -# ifdef GC_WIN32_THREADS -# include <windows.h> - GC_API CRITICAL_SECTION GC_allocate_ml; -# define LOCK() EnterCriticalSection(&GC_allocate_ml); -# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# if defined(GC_WIN32_THREADS) +# if defined(GC_PTHREADS) +# include <pthread.h> + extern pthread_mutex_t GC_allocate_ml; +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# include <windows.h> + GC_API CRITICAL_SECTION GC_allocate_ml; +# define LOCK() EnterCriticalSection(&GC_allocate_ml); +# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# endif # endif # ifndef SET_LOCK_HOLDER # define SET_LOCK_HOLDER() diff --git a/boehm-gc/include/private/gc_pmark.h b/boehm-gc/include/private/gc_pmark.h index 872065702f0..c109738203a 100644 --- a/boehm-gc/include/private/gc_pmark.h +++ b/boehm-gc/include/private/gc_pmark.h @@ -137,7 +137,7 @@ extern mse * GC_mark_stack; #ifdef __STDC__ # ifdef PRINT_BLACK_LIST ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p, - ptr_t source); + word source); # else ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p); # endif @@ -145,7 +145,7 @@ extern mse * GC_mark_stack; ptr_t GC_find_start(); #endif -mse *GC_signal_mark_stack_overflow(mse *msp); +mse * GC_signal_mark_stack_overflow GC_PROTO((mse *msp)); # ifdef GATHERSTATS # define ADD_TO_ATOMIC(sz) GC_atomic_in_use += (sz) @@ -174,14 +174,6 @@ mse *GC_signal_mark_stack_overflow(mse *msp); } \ } -#ifdef PRINT_BLACK_LIST -# define GC_FIND_START(current, hhdr, new_hdr_p, source) \ - GC_find_start(current, hhdr, new_hdr_p, source) -#else -# define GC_FIND_START(current, hhdr, new_hdr_p, source) \ - GC_find_start(current, hhdr, new_hdr_p) -#endif - /* Push the contents of current onto the mark stack if it is a valid */ /* ptr to a currently unmarked object. Mark it. */ /* If we assumed a standard-conforming compiler, we could probably */ @@ -195,8 +187,7 @@ mse *GC_signal_mark_stack_overflow(mse *msp); GET_HDR(my_current, my_hhdr); \ if (IS_FORWARDING_ADDR_OR_NIL(my_hhdr)) { \ hdr * new_hdr = GC_invalid_header; \ - my_current = GC_FIND_START(my_current, my_hhdr, \ - &new_hdr, (word)source); \ + my_current = GC_find_start(my_current, my_hhdr, &new_hdr); \ my_hhdr = new_hdr; \ } \ PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \ @@ -290,21 +281,39 @@ exit_label: ; \ /* * Push a single value onto mark stack. Mark from the object pointed to by p. + * Invoke FIXUP_POINTER(p) before any further processing. * P is considered valid even if it is an interior pointer. * Previously marked objects are not pushed. Hence we make progress even * if the mark stack overflows. */ -# define GC_PUSH_ONE_STACK(p, source) \ - if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + +# if NEED_FIXUP_POINTER + /* Try both the raw version and the fixed up one. */ +# define GC_PUSH_ONE_STACK(p, source) \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ PUSH_ONE_CHECKED_STACK(p, source); \ - } + } \ + FIXUP_POINTER(p); \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } +# else /* !NEED_FIXUP_POINTER */ +# define GC_PUSH_ONE_STACK(p, source) \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } +# endif + /* * As above, but interior pointer recognition as for * normal for heap pointers. */ # define GC_PUSH_ONE_HEAP(p,source) \ + FIXUP_POINTER(p); \ if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ GC_mark_stack_top = GC_mark_and_push( \ diff --git a/boehm-gc/include/private/gc_priv.h b/boehm-gc/include/private/gc_priv.h index b09c4840945..ffa398b308b 100644 --- a/boehm-gc/include/private/gc_priv.h +++ b/boehm-gc/include/private/gc_priv.h @@ -30,14 +30,20 @@ # define BSD_TIME #endif +#ifdef DGUX +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif /* DGUX */ + #ifdef BSD_TIME # include <sys/types.h> # include <sys/time.h> # include <sys/resource.h> #endif /* BSD_TIME */ -# ifndef GC_H -# include "gc.h" +# ifndef _GC_H +# include "../gc.h" # endif # ifndef GC_MARK_H @@ -206,11 +212,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ #endif #if defined(GC_GCJ_SUPPORT) && ALIGNMENT < 8 && !defined(ALIGN_DOUBLE) - /* GCJ's Hashtable synchronization code requires 64-bit alignment. */ + /* GCJ's Hashtable synchronization code requires 64-bit alignment. */ # define ALIGN_DOUBLE #endif - /* ALIGN_DOUBLE requires MERGE_SIZES at present. */ # if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) # define MERGE_SIZES @@ -347,7 +352,8 @@ void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES])); # include <string.h> # define BCOPY_EXISTS # endif -# if defined(MACOSX) +# if defined(DARWIN) +# include <string.h> # define BCOPY_EXISTS # endif @@ -360,68 +366,6 @@ void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES])); # define BZERO(x,n) bzero((char *)(x),(int)(n)) # endif -/* HBLKSIZE aligned allocation. 0 is taken to mean failure */ -/* space is assumed to be cleared. */ -/* In the case os USE_MMAP, the argument must also be a */ -/* physical page size. */ -/* GET_MEM is currently not assumed to retrieve 0 filled space, */ -/* though we should perhaps take advantage of the case in which */ -/* does. */ -struct hblk; /* See below. */ -# ifdef PCR - char * real_malloc(); -# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ - + GC_page_size-1) -# else -# ifdef OS2 - void * os2_alloc(size_t bytes); -# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ - + GC_page_size) \ - + GC_page_size-1) -# else -# if defined(NEXT) || defined(MACOSX) || defined(DOS4GW) || \ - (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) || \ - (defined(SUNOS5) && !defined(USE_MMAP)) -# define GET_MEM(bytes) HBLKPTR((size_t) \ - calloc(1, (size_t)bytes + GC_page_size) \ - + GC_page_size-1) -# else -# ifdef MSWIN32 - extern ptr_t GC_win32_get_mem(); -# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) -# else -# ifdef MACOS -# if defined(USE_TEMPORARY_MEMORY) - extern Ptr GC_MacTemporaryNewPtr(size_t size, - Boolean clearMemory); -# define GET_MEM(bytes) HBLKPTR( \ - GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ - + GC_page_size-1) -# else -# define GET_MEM(bytes) HBLKPTR( \ - NewPtrClear(bytes + GC_page_size) + GC_page_size-1) -# endif -# else -# ifdef MSWINCE - extern ptr_t GC_wince_get_mem(); -# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) -# else -# if defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) - extern void *GC_amiga_get_mem(size_t size); - define GET_MEM(bytes) HBLKPTR((size_t) \ - GC_amiga_get_mem((size_t)bytes + GC_page_size) \ - + GC_page_size-1) -# else - extern ptr_t GC_unix_get_mem(); -# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) -# endif -# endif -# endif -# endif -# endif -# endif -# endif - /* Delay any interrupts or signals that may abort this thread. Data */ /* structures are in a consistent state outside this pair of calls. */ /* ANSI C allows both to be empty (though the standard isn't very */ @@ -486,7 +430,7 @@ struct hblk; /* See below. */ # ifdef SMALL_CONFIG # define ABORT(msg) abort(); # else - GC_API void GC_abort(); + GC_API void GC_abort GC_PROTO((GC_CONST char * msg)); # define ABORT(msg) GC_abort(msg); # endif # endif @@ -646,9 +590,10 @@ extern GC_warn_proc GC_current_warn_proc; */ # ifdef LARGE_CONFIG -# define LOG_PHT_ENTRIES 19 /* Collisions likely at 512K blocks, */ - /* which is >= 2GB. Each table takes */ - /* 64KB. */ +# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */ + /* which is >= 4GB. Each table takes */ + /* 128KB, some of which may never be */ + /* touched. */ # else # ifdef SMALL_CONFIG # define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */ @@ -656,7 +601,7 @@ extern GC_warn_proc GC_current_warn_proc; /* Each hash table occupies 2K bytes. */ # else /* default "medium" configuration */ # define LOG_PHT_ENTRIES 16 /* Collisions are likely if heap grows */ - /* to more than 16K hblks >= 256MB. */ + /* to more than 64K hblks >= 256MB. */ /* Each hash table occupies 8K bytes. */ # endif # endif @@ -897,6 +842,10 @@ struct _GC_arrays { word _mem_freed; /* Number of explicitly deallocated words of memory */ /* since last collection. */ + word _finalizer_mem_freed; + /* Words of memory explicitly deallocated while */ + /* finalizers were running. Used to approximate mem. */ + /* explicitly deallocated by finalizers. */ ptr_t _scratch_end_ptr; ptr_t _scratch_last_end_ptr; /* Used by headers.c, and can easily appear to point to */ @@ -957,7 +906,7 @@ struct _GC_arrays { /* OFFSET_TOO_BIG if the value j would be too */ /* large to fit in the entry. (Note that the */ /* size of these entries matters, both for */ - /* space consumption and for cache utilization. */ + /* space consumption and for cache utilization.) */ # define OFFSET_TOO_BIG 0xfe # define OBJ_INVALID 0xff # define MAP_ENTRY(map, bytes) (map)[bytes] @@ -1067,6 +1016,7 @@ GC_API GC_FAR struct _GC_arrays GC_arrays; # define GC_words_finalized GC_arrays._words_finalized # define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc # define GC_mem_freed GC_arrays._mem_freed +# define GC_finalizer_mem_freed GC_arrays._finalizer_mem_freed # define GC_scratch_end_ptr GC_arrays._scratch_end_ptr # define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr # define GC_mark_procs GC_arrays._mark_procs @@ -1201,17 +1151,19 @@ extern struct hblk * GC_hblkfreelist[]; /* header structure associated with */ /* block. */ -extern GC_bool GC_is_initialized; /* GC_init() has been run. */ - extern GC_bool GC_objects_are_marked; /* There are marked objects in */ /* the heap. */ #ifndef SMALL_CONFIG extern GC_bool GC_incremental; /* Using incremental/generational collection. */ +# define TRUE_INCREMENTAL \ + (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED) + /* True incremental, not just generational, mode */ #else # define GC_incremental FALSE /* Hopefully allow optimizer to remove some code. */ +# define TRUE_INCREMENTAL FALSE #endif extern GC_bool GC_dirty_maintained; @@ -1229,6 +1181,10 @@ extern long GC_large_alloc_warn_interval; extern long GC_large_alloc_warn_suppressed; /* Number of warnings suppressed so far. */ +#ifdef THREADS + extern GC_bool GC_world_stopped; +#endif + /* Operations */ # ifndef abs # define abs(x) ((x) < 0? (-(x)) : (x)) @@ -1403,6 +1359,11 @@ extern void (*GC_start_call_back) GC_PROTO((void)); # else void GC_push_regs GC_PROTO((void)); # endif +# if defined(SPARC) || defined(IA64) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + word GC_save_regs_in_stack GC_PROTO((void)); +# endif /* Push register contents onto mark stack. */ /* If NURSERY is defined, the default push */ /* action can be overridden with GC_push_proc */ @@ -1452,6 +1413,7 @@ void GC_set_fl_marks GC_PROTO((ptr_t p)); /* Set all mark bits associated with */ /* a free list. */ void GC_add_roots_inner GC_PROTO((char * b, char * e, GC_bool tmp)); +void GC_remove_roots_inner GC_PROTO((char * b, char * e)); GC_bool GC_is_static_root GC_PROTO((ptr_t p)); /* Is the address p in one of the registered static */ /* root sections? */ @@ -1462,11 +1424,10 @@ GC_bool GC_is_tmp_root GC_PROTO((ptr_t p)); # endif void GC_register_dynamic_libraries GC_PROTO((void)); /* Add dynamic library data sections to the root set. */ - GC_bool GC_register_main_static_data GC_PROTO((void)); - /* We need to register the main data segment. Returns */ - /* TRUE unless this is done implicitly as part of */ - /* dynamic library registration. */ + /* We need to register the main data segment. Returns */ + /* TRUE unless this is done implicitly as part of */ + /* dynamic library registration. */ /* Machine dependent startup routines */ ptr_t GC_get_stack_base GC_PROTO((void)); /* Cold end of stack */ @@ -1624,6 +1585,8 @@ GC_bool GC_collect_or_expand GC_PROTO(( \ /* until the blocks are available or */ /* until it fails by returning FALSE. */ +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ + #if defined(MSWIN32) || defined(MSWINCE) void GC_deinit GC_PROTO((void)); /* Free any resources allocated by */ @@ -1665,6 +1628,8 @@ ptr_t GC_allocobj GC_PROTO((word sz, int kind)); /* Make the indicated */ /* free list nonempty, and return its */ /* head. */ + +void GC_free_inner(GC_PTR p); void GC_init_headers GC_PROTO((void)); struct hblkhdr * GC_install_header GC_PROTO((struct hblk *h)); @@ -1694,6 +1659,12 @@ void GC_notify_or_invoke_finalizers GC_PROTO((void)); /* Call *GC_finalizer_notifier if there are */ /* finalizers to be run, and we haven't called */ /* this procedure yet this GC cycle. */ + +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)); + /* Auxiliary fns to make finalization work */ + /* correctly with displaced pointers introduced */ + /* by the debugging allocators. */ void GC_add_to_heap GC_PROTO((struct hblk *p, word bytes)); /* Add a HBLKSIZE aligned chunk to the heap. */ @@ -1704,16 +1675,36 @@ void GC_print_obj GC_PROTO((ptr_t p)); /* description of the object to stderr. */ extern void (*GC_check_heap) GC_PROTO((void)); /* Check that all objects in the heap with */ - /* debugging info are intact. Print */ - /* descriptions of any that are not. */ + /* debugging info are intact. */ + /* Add any that are not to GC_smashed list. */ +extern void (*GC_print_all_smashed) GC_PROTO((void)); + /* Print GC_smashed if it's not empty. */ + /* Clear GC_smashed list. */ +extern void GC_print_all_errors GC_PROTO((void)); + /* Print smashed and leaked objects, if any. */ + /* Clear the lists of such objects. */ extern void (*GC_print_heap_obj) GC_PROTO((ptr_t p)); /* If possible print s followed by a more */ /* detailed description of the object */ /* referred to by p. */ +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + void GC_print_address_map GC_PROTO((void)); + /* Print an address map of the process. */ +#endif +extern GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ + /* Call error printing routine */ + /* occasionally. */ extern GC_bool GC_print_stats; /* Produce at least some logging output */ /* Set from environment variable. */ +#ifndef NO_DEBUGGING + extern GC_bool GC_dump_regularly; /* Generate regular debugging dumps. */ +# define COND_DUMP if (GC_dump_regularly) GC_dump(); +#else +# define COND_DUMP +#endif + /* Macros used for collector internal allocation. */ /* These assume the collector lock is held. */ #ifdef DBG_HDRS_ALL @@ -1785,6 +1776,7 @@ void GC_print_block_list GC_PROTO((void)); void GC_print_hblkfreelist GC_PROTO((void)); void GC_print_heap_sects GC_PROTO((void)); void GC_print_static_roots GC_PROTO((void)); +void GC_print_finalization_stats GC_PROTO((void)); void GC_dump GC_PROTO((void)); #ifdef KEEP_BACK_PTRS @@ -1866,6 +1858,16 @@ void GC_err_puts GC_PROTO((GC_CONST char *s)); # define GC_ASSERT(expr) # endif +/* Check a compile time assertion at compile time. The error */ +/* message for failure is a bit baroque, but ... */ +#if defined(mips) && !defined(__GNUC__) +/* DOB: MIPSPro C gets an internal error taking the sizeof an array type. + This code works correctly (ugliness is to avoid "unused var" warnings) */ +# define GC_STATIC_ASSERT(expr) do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0) +#else +# define GC_STATIC_ASSERT(expr) sizeof(char[(expr)? 1 : -1]) +#endif + # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) /* We need additional synchronization facilities from the thread */ /* support. We believe these are less performance critical */ @@ -1911,7 +1913,7 @@ void GC_err_puts GC_PROTO((GC_CONST char *s)); /* in Linux glibc, but it's not exported.) Thus we continue to use */ /* the same hard-coded signals we've always used. */ # if !defined(SIG_SUSPEND) -# if defined(GC_LINUX_THREADS) +# if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS) # if defined(SPARC) && !defined(SIGPWR) /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>. * It is aliased to SIGLOST in asm/signal.h, though. */ diff --git a/boehm-gc/include/private/gcconfig.h b/boehm-gc/include/private/gcconfig.h index 809887f8219..29dab213815 100644 --- a/boehm-gc/include/private/gcconfig.h +++ b/boehm-gc/include/private/gcconfig.h @@ -13,11 +13,25 @@ * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ + +/* + * This header is private to the gc. It is almost always included from + * gc_priv.h. However it is possible to include it by itself if just the + * configuration macros are needed. In that + * case, a few declarations relying on types declared in gc_priv.h will be + * omitted. + */ #ifndef GCCONFIG_H # define GCCONFIG_H +# ifndef GC_PRIVATE_H + /* Fake ptr_t declaration, just to avoid compilation errors. */ + /* This avoids many instances if "ifndef GC_PRIVATE_H" below. */ + typedef struct GC_undefined_struct * ptr_t; +# endif + /* Machine dependent parameters. Some tuning parameters can be found */ /* near the top of gc_private.h. */ @@ -25,7 +39,9 @@ /* First a unified test for Linux: */ # if defined(linux) || defined(__linux__) +# ifndef LINUX # define LINUX +# endif # endif /* And one for NetBSD: */ @@ -44,9 +60,9 @@ # endif /* Determine the machine type: */ -# if defined(__arm__) || defined(__thumb__) +# if defined(__XSCALE__) # define ARM32 -# if !defined(LINUX) && !defined(NETBSD) +# if !defined(LINUX) # define NOSYS # define mach_type_known # endif @@ -69,7 +85,7 @@ # define SPARC # define mach_type_known # endif -# if defined(NETBSD) && defined(m68k) +# if defined(NETBSD) && (defined(m68k) || defined(__m68k__)) # define M68K # define mach_type_known # endif @@ -77,7 +93,7 @@ # define POWERPC # define mach_type_known # endif -# if defined(NETBSD) && defined(__arm__) +# if defined(NETBSD) && (defined(__arm32__) || defined(__arm__)) # define ARM32 # define mach_type_known # endif @@ -90,6 +106,10 @@ # endif # define mach_type_known # endif +# if defined(__NetBSD__) && defined(__vax__) +# define VAX +# define mach_type_known +# endif # if defined(mips) || defined(__mips) || defined(_mips) # define MIPS # if defined(nec_ews) || defined(_nec_ews) @@ -109,6 +129,13 @@ # endif /* !LINUX */ # define mach_type_known # endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define I386 +# ifndef _USING_DGUX +# define _USING_DGUX +# endif +# define mach_type_known +# endif # if defined(sequent) && (defined(i386) || defined(__i386__)) # define I386 # define SEQUENT @@ -198,7 +225,11 @@ # define IA64 # define mach_type_known # endif -# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__)) +# if defined(LINUX) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) || defined(powerpc64) || defined(__powerpc64__)) # define POWERPC # define mach_type_known # endif @@ -237,19 +268,19 @@ # define MACOS # define mach_type_known # endif -# if defined(__MWERKS__) && defined(__powerc) +# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) # define POWERPC # define MACOS # define mach_type_known # endif # if defined(macosx) || \ defined(__APPLE__) && defined(__MACH__) && defined(__ppc__) -# define MACOSX +# define DARWIN # define POWERPC # define mach_type_known # endif # if defined(__APPLE__) && defined(__MACH__) && defined(__i386__) -# define MACOSX +# define DARWIN # define I386 --> Not really supported, but at least we recognize it. # endif @@ -291,7 +322,7 @@ # define CX_UX # define mach_type_known # endif -# if defined(DGUX) +# if defined(DGUX) && defined(m88k) # define M88K /* DGUX defined */ # define mach_type_known @@ -419,17 +450,18 @@ /* (CX_UX and DGUX) */ /* S370 ==> 370-like machine */ /* running Amdahl UTS4 */ - /* S390 ==> 390-like machine */ - /* running LINUX */ + /* S390 ==> 390-like machine */ + /* running LINUX */ /* ARM32 ==> Intel StrongARM */ /* IA64 ==> Intel IPF */ /* (e.g. Itanium) */ /* (LINUX and HPUX) */ - /* IA64_32 ==> IA64 w/32 bit ABI */ - /* (HPUX) */ /* SH ==> Hitachi SuperH */ /* (LINUX & MSWINCE) */ /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, NOSYS variants) */ /* @@ -450,7 +482,12 @@ * defining it to be 1 will always work, but perform poorly. * * DATASTART is the beginning of the data segment. - * On UNIX systems, the collector will scan the area between DATASTART + * On some platforms SEARCH_FOR_DATA_START is defined. + * SEARCH_FOR_DATASTART will cause GC_data_start to + * be set to an address determined by accessing data backwards from _end + * until an unmapped page is found. DATASTART will be defined to be + * GC_data_start. + * On UNIX-like systems, the collector will scan the area between DATASTART * and DATAEND for root pointers. * * DATAEND, if not `end' where `end' is defined as ``extern int end[];''. @@ -470,8 +507,13 @@ * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and * 2) define exactly one of * STACKBOTTOM (should be defined to be an expression) + * LINUX_STACKBOTTOM * HEURISTIC1 * HEURISTIC2 + * If STACKBOTTOM is defined, then it's value will be used directly as the + * stack base. If LINUX_STACKBOTTOM is defined, then it will be determined + * with a method appropriate for most Linux systems. Currently we look + * first for __libc_stack_end, and if that fails read it from /proc. * If either of the last two macros are defined, then STACKBOTTOM is computed * during collector startup using one of the following two heuristics: * HEURISTIC1: Take an address inside GC_init's frame, and round it up to @@ -536,6 +578,9 @@ * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to * clear the two words at GC_malloc-aligned address x. By default, * word stores of 0 are used instead. + * + * HEAP_START may be defined as the initial address hint for mmap-based + * allocation. */ /* If we are using a recent version of gcc, we can use __builtin_unwind_init() @@ -560,18 +605,25 @@ # ifdef NETBSD # define OS_TYPE "NETBSD" # define HEURISTIC2 - extern char etext[]; -# define DATASTART ((ptr_t)(etext)) +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif # endif # ifdef LINUX # define OS_TYPE "LINUX" # define STACKBOTTOM ((ptr_t)0xf0000000) +# define USE_GENERIC_PUSH_REGS + /* We never got around to the assembly version. */ /* # define MPROTECT_VDB - Reported to not work 9/17/01 */ # ifdef __ELF__ # define DYNAMIC_LOADING # include <features.h> # if defined(__GLIBC__)&& __GLIBC__>=2 -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START # else /* !GLIBC2 */ extern char **__environ; # define DATASTART ((ptr_t)(&__environ)) @@ -666,26 +718,49 @@ # define DATAEND /* not needed */ # endif # ifdef LINUX -# define ALIGNMENT 4 /* Guess. Can someone verify? */ +# if (defined (powerpc64) || defined(__powerpc64__)) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# else +# define ALIGNMENT 4 /* Guess. Can someone verify? */ /* This was 2, but that didn't sound right. */ +# endif # define OS_TYPE "LINUX" -# define DYNAMIC_LOADING + /* HEURISTIC1 has been reliably reported to fail for a 32-bit */ + /* executable on a 64 bit kernel. */ # define LINUX_STACKBOTTOM - /* Stack usually starts at 0x80000000 */ -# define LINUX_DATA_START +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START extern int _end[]; # define DATAEND (_end) # endif -# ifdef MACOSX - /* There are reasons to suspect this may not be reliable. */ +# ifdef DARWIN # define ALIGNMENT 4 -# define OS_TYPE "MACOSX" +# define OS_TYPE "DARWIN" +# define DYNAMIC_LOADING + /* XXX: see get_end(3), get_etext() and get_end() should not be used. + These aren't used when dyld support is enabled (it is by default) */ # define DATASTART ((ptr_t) get_etext()) +# define DATAEND ((ptr_t) get_end()) # define STACKBOTTOM ((ptr_t) 0xc0000000) -# define DATAEND /* not needed */ -# undef MPROTECT_VDB +# define USE_MMAP +# define USE_MMAP_ANON +# define USE_ASM_PUSH_REGS + /* This is potentially buggy. It needs more testing. See the comments in + os_dep.c */ +# define MPROTECT_VDB # include <unistd.h> # define GETPAGESIZE() getpagesize() +# if defined(USE_PPC_PREFETCH) && defined(__GNUC__) + /* The performance impact of prefetches is untested */ +# define PREFETCH(x) \ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x))) +# define PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) +# endif + /* There seems to be some issues with trylock hanging on darwin. This + should be looked into some more */ +# define NO_PTHREAD_TRYLOCK # endif # ifdef NETBSD # define ALIGNMENT 4 @@ -746,8 +821,8 @@ # define OS_TYPE "SUNOS5" extern int _etext[]; extern int _end[]; - extern char * GC_SysVGetDataStart(); -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, _etext) + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) # define DATAEND (_end) # if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) # define USE_MMAP @@ -801,9 +876,9 @@ # endif # ifdef DRSNX # define OS_TYPE "DRSNX" - extern char * GC_SysVGetDataStart(); + extern ptr_t GC_SysVGetDataStart(); extern int etext[]; -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, etext) +# define DATASTART GC_SysVGetDataStart(0x10000, etext) # define MPROTECT_VDB # define STACKBOTTOM ((ptr_t) 0xdfff0000) # define DYNAMIC_LOADING @@ -819,13 +894,14 @@ extern int _etext[]; # define DATAEND (_end) # define SVR4 + extern ptr_t GC_SysVGetDataStart(); # ifdef __arch64__ +# define DATASTART GC_SysVGetDataStart(0x100000, _etext) /* libc_stack_end is not set reliably for sparc64 */ -# define STACKBOTTOM ((ptr_t) 0x80000000000) -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x100000, _etext) +# define STACKBOTTOM ((ptr_t) 0x80000000000ULL) # else -# define LINUX_STACKBOTTOM -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, _etext) +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) +# define LINUX_STACKBOTTOM # endif # endif # ifdef OPENBSD @@ -876,7 +952,7 @@ # ifdef SUNOS5 # define OS_TYPE "SUNOS5" extern int _etext[], _end[]; - extern char * GC_SysVGetDataStart(); + extern ptr_t GC_SysVGetDataStart(); # define DATASTART GC_SysVGetDataStart(0x1000, _etext) # define DATAEND (_end) /* # define STACKBOTTOM ((ptr_t)(_start)) worked through 2.7, */ @@ -921,6 +997,28 @@ # define DYNAMIC_LOADING # define ELF_CLASS ELFCLASS32 # endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern int _etext, _end; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, &_etext) +# define DATAEND (&_end) +# define STACK_GROWS_DOWN +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif /* USE_MMAP */ +# define MAP_FAILED (void *) -1 +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else /* USE_MMAP */ +# define HEAP_START DATAEND +# endif /* USE_MMAP */ +# endif /* DGUX */ + # ifdef LINUX # ifndef __GNUC__ /* The Intel compiler doesn't like inline assembly */ @@ -944,6 +1042,9 @@ /* possibly because Linux threads is itself a malloc client */ /* and can't deal with the signals. */ # endif +# define HEAP_START 0x1000 + /* This encourages mmap to give us low addresses, */ + /* thus allowing the heap to grow to ~3GB */ # ifdef __ELF__ # define DYNAMIC_LOADING # ifdef UNDEFINED /* includes ro data */ @@ -952,7 +1053,7 @@ # endif # include <features.h> # if defined(__GLIBC__) && __GLIBC__ >= 2 -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START # else extern char **__environ; # define DATASTART ((ptr_t)(&__environ)) @@ -1006,8 +1107,12 @@ /* DATAEND = _data_end__ */ /* To get it right for both, we take the */ /* minumum/maximum of the two. */ +# ifndef MAX # define MAX(x,y) ((x) > (y) ? (x) : (y)) +# endif +# ifndef MIN # define MIN(x,y) ((x) < (y) ? (x) : (y)) +# endif # define DATASTART ((ptr_t) MIN(_data_start__, _bss_start__)) # define DATAEND ((ptr_t) MAX(_data_end__, _bss_end__)) # undef STACK_GRAN @@ -1061,16 +1166,9 @@ # ifdef __ELF__ # define DYNAMIC_LOADING # endif -/* Handle unmapped hole i386*-*-freebsd[45]* may put between etext and edata. */ extern char etext[]; - extern char edata[]; - extern char end[]; -# define NEED_FIND_LIMIT -# define DATASTART ((ptr_t)(etext)) -# define MIN(x,y) ((x) < (y) ? (x) : (y)) -# define DATAEND (MIN (GC_find_limit (DATASTART, TRUE), DATASTART2)) -# define DATASTART2 ((ptr_t)(edata)) -# define DATAEND2 ((ptr_t)(end)) + extern char * GC_FreeBSDGetDataStart(); +# define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext) # endif # ifdef NETBSD # define OS_TYPE "NETBSD" @@ -1149,7 +1247,11 @@ # define DATASTART ((ptr_t)(__data_start)) # define ALIGNMENT 4 # define USE_GENERIC_PUSH_REGS -# define LINUX_STACKBOTTOM +# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2 +# define LINUX_STACKBOTTOM +# else +# define STACKBOTTOM 0x80000000 +# endif # endif /* Linux */ # ifdef EWS4800 # define HEURISTIC2 @@ -1203,7 +1305,8 @@ /* heap sections so they're not */ /* considered as roots. */ # define OS_TYPE "IRIX5" -# define MPROTECT_VDB +/*# define MPROTECT_VDB DOB: this should work, but there is evidence */ +/* of recent breakage. */ # ifdef _MIPS_SZPTR # define CPP_WORDSZ _MIPS_SZPTR # define ALIGNMENT (_MIPS_SZPTR/8) @@ -1226,28 +1329,46 @@ # define ALIGNMENT 4 # define HEURISTIC2 # define USE_GENERIC_PUSH_REGS - extern int _fdata[]; -# define DATASTART ((ptr_t)(_fdata)) - extern int _end[]; -# define DATAEND ((ptr_t)(_end)) -# define DYNAMIC_LOADING +# ifdef __ELF__ + extern int etext[]; +# define DATASTART GC_data_start +# define NEED_FIND_LIMIT +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x10000000) +# define STACKBOTTOM ((ptr_t) 0x7ffff000) +# endif /* _ELF_ */ # endif # endif # ifdef RS6000 # define MACH_TYPE "RS6000" +# ifdef ALIGNMENT +# undef ALIGNMENT +# endif +# ifdef IA64 +# undef IA64 /* DOB: some AIX installs stupidly define IA64 in /usr/include/sys/systemcfg.h */ +# endif # ifdef __64BIT__ # define ALIGNMENT 8 # define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x1000000000000000) # else # define ALIGNMENT 4 # define CPP_WORDSZ 32 -# endif +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# endif + /* From AIX linker man page: + _text Specifies the first location of the program. + _etext Specifies the first location after the program. + _data Specifies the first location of the data. + _edata Specifies the first location after the initialized data + _end or end Specifies the first location after all data. + */ extern int _data[], _end[]; # define DATASTART ((ptr_t)((ulong)_data)) # define DATAEND ((ptr_t)((ulong)_end)) extern int errno; -# define STACKBOTTOM ((ptr_t)((ulong)&errno)) # define USE_GENERIC_PUSH_REGS # define DYNAMIC_LOADING /* For really old versions of AIX, this may have to be removed. */ @@ -1311,15 +1432,23 @@ # define OS_TYPE "LINUX" # define LINUX_STACKBOTTOM # define DYNAMIC_LOADING -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START extern int _end[]; -# define DATAEND (_end) +# define DATAEND (&_end) # endif /* LINUX */ # endif /* HP_PA */ # ifdef ALPHA # define MACH_TYPE "ALPHA" # define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef LINUX +# define USE_GENERIC_PUSH_REGS + /* Gcc and probably the DEC/Compaq compiler spill pointers to preserved */ + /* fp registers in some cases when the target is a 21264. The assembly */ + /* code doesn't handle that yet, and version dependencies make that a */ + /* bit tricky. Do the easy thing for now. */ +# endif # ifdef NETBSD # define OS_TYPE "NETBSD" # define HEURISTIC2 @@ -1327,13 +1456,11 @@ # define ELFCLASS32 32 # define ELFCLASS64 64 # define ELF_CLASS ELFCLASS64 -# define CPP_WORDSZ 64 # define DYNAMIC_LOADING # endif # ifdef OPENBSD # define OS_TYPE "OPENBSD" # define HEURISTIC2 -# define CPP_WORDSZ 64 # ifdef __ELF__ /* since OpenBSD/Alpha 2.9 */ # define DATASTART GC_data_start # define ELFCLASS32 32 @@ -1357,17 +1484,16 @@ extern char edata[]; extern char end[]; # define NEED_FIND_LIMIT -# define DATASTART ((ptr_t)(etext)) +# define DATASTART ((ptr_t)(&etext)) # define DATAEND (GC_find_limit (DATASTART, TRUE)) -# define DATASTART2 ((ptr_t)(edata)) -# define DATAEND2 ((ptr_t)(end)) -# define CPP_WORDSZ 64 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) # endif # ifdef OSF1 # define OS_TYPE "OSF1" # define DATASTART ((ptr_t) 0x140000000) extern int _end[]; -# define DATAEND ((ptr_t) _end) +# define DATAEND ((ptr_t) &_end) extern char ** environ; /* round up from the value of environ to the nearest page boundary */ /* Probably breaks if putenv is called before collector */ @@ -1378,19 +1504,19 @@ /* the text segment immediately follows the stack. */ /* Hence we give an upper pound. */ /* This is currently unused, since we disabled HEURISTIC2 */ - extern int __start[]; + extern int __start[]; # define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) & ~(getpagesize()-1))) -# define CPP_WORDSZ 64 -# define MPROTECT_VDB +# ifndef GC_OSF1_THREADS + /* Unresolved signal issues with threads. */ +# define MPROTECT_VDB +# endif # define DYNAMIC_LOADING # endif # ifdef LINUX # define OS_TYPE "LINUX" -# define CPP_WORDSZ 64 # define STACKBOTTOM ((ptr_t) 0x120000000) # ifdef __ELF__ # define SEARCH_FOR_DATA_START -# define DATASTART GC_data_start # define DYNAMIC_LOADING # else # define DATASTART ((ptr_t) 0x140000000) @@ -1468,7 +1594,6 @@ extern char * GC_register_stackbottom; # define BACKING_STORE_BASE ((ptr_t)GC_register_stackbottom) # define SEARCH_FOR_DATA_START -# define DATASTART GC_data_start # ifdef __GNUC__ # define DYNAMIC_LOADING # else @@ -1502,23 +1627,25 @@ # endif # ifdef DGUX # define OS_TYPE "DGUX" - extern char * GC_SysVGetDataStart(); -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, etext) + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, etext) # endif # define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ # endif # ifdef S370 + /* If this still works, and if anyone cares, this should probably */ + /* be moved to the S390 category. */ # define MACH_TYPE "S370" # define ALIGNMENT 4 /* Required by hardware */ # define USE_GENERIC_PUSH_REGS # ifdef UTS4 # define OS_TYPE "UTS4" - extern int etext[]; + extern int etext[]; extern int _etext[]; extern int _end[]; - extern char * GC_SysVGetDataStart(); -# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, _etext) + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) # define DATAEND (_end) # define HEURISTIC2 # endif @@ -1528,23 +1655,23 @@ # define MACH_TYPE "S390" # define USE_GENERIC_PUSH_REGS # ifndef __s390x__ -# define ALIGNMENT 4 -# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 # else -# define ALIGNMENT 8 -# define CPP_WORDSZ 64 -# define HBLKSIZE 4096 +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define HBLKSIZE 4096 # endif # ifdef LINUX # define OS_TYPE "LINUX" # define LINUX_STACKBOTTOM # define DYNAMIC_LOADING - extern int __data_start[]; + extern int __data_start[]; # define DATASTART ((ptr_t)(__data_start)) - extern int _end[]; -# define DATAEND (_end) -# define CACHE_LINE_SIZE 256 -# define GETPAGESIZE() 4096 + extern int _end[]; +# define DATAEND (_end) +# define CACHE_LINE_SIZE 256 +# define GETPAGESIZE() 4096 # endif # endif @@ -1562,13 +1689,8 @@ # ifdef NETBSD # define OS_TYPE "NETBSD" # define HEURISTIC2 -# ifdef __ELF__ -# define DATASTART GC_data_start -# define DYNAMIC_LOADING -# else - extern char etext[]; -# define DATASTART ((ptr_t)(etext)) -# endif + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) # define USE_GENERIC_PUSH_REGS # endif # ifdef LINUX @@ -1581,7 +1703,7 @@ # define DYNAMIC_LOADING # include <features.h> # if defined(__GLIBC__) && __GLIBC__ >= 2 -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START # else extern char **__environ; # define DATASTART ((ptr_t)(&__environ)) @@ -1628,7 +1750,7 @@ # define STACKBOTTOM ((ptr_t) 0x7c000000) # define USE_GENERIC_PUSH_REGS # define DYNAMIC_LOADING -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START extern int _end[]; # define DATAEND (_end) # endif @@ -1645,7 +1767,9 @@ # define MACH_TYPE "X86_64" # define ALIGNMENT 8 # define CPP_WORDSZ 64 -# define HBLKSIZE 4096 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif # define CACHE_LINE_SIZE 64 # define USE_GENERIC_PUSH_REGS # ifdef LINUX @@ -1665,7 +1789,7 @@ # define DATASTART ((ptr_t)((((word) (_etext)) + 0xfff) & ~0xfff)) # endif # include <features.h> -# define LINUX_DATA_START +# define SEARCH_FOR_DATA_START extern int _end[]; # define DATAEND (_end) # else @@ -1679,19 +1803,6 @@ # endif # endif -#ifdef LINUX_DATA_START - /* Some Linux distributions arrange to define __data_start. Some */ - /* define data_start as a weak symbol. The latter is technically */ - /* broken, since the user program may define data_start, in which */ - /* case we lose. Nonetheless, we try both, prefering __data_start. */ - /* We assume gcc. */ -# pragma weak __data_start - extern int __data_start[]; -# pragma weak data_start - extern int data_start[]; -# define DATASTART ((ptr_t)(__data_start != 0? __data_start : data_start)) -#endif - #if defined(LINUX) && defined(REDIRECT_MALLOC) /* Rld appears to allocate some memory with its own allocator, and */ /* some through malloc, which might be redirected. To make this */ @@ -1730,15 +1841,15 @@ # endif # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) - /* OS has SVR4 generic features. Probably others also qualify. */ + /* OS has SVR4 generic features. Probably others also qualify. */ # define SVR4 # endif # if defined(SUNOS5) || defined(DRSNX) - /* OS has SUNOS5 style semi-undocumented interface to dynamic */ - /* loader. */ + /* OS has SUNOS5 style semi-undocumented interface to dynamic */ + /* loader. */ # define SUNOS5DL - /* OS has SUNOS5 style signal handlers. */ + /* OS has SUNOS5 style signal handlers. */ # define SUNOS5SIGS # endif @@ -1747,13 +1858,14 @@ # endif # if defined(SVR4) || defined(LINUX) || defined(IRIX) || defined(HPUX) \ - || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ - || defined(BSD) || defined(_AIX) || defined(MACOSX) || defined(OSF1) + || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ + || defined(DGUX) || defined(BSD) \ + || defined(_AIX) || defined(DARWIN) || defined(OSF1) # define UNIX_LIKE /* Basic Unix-like system calls work. */ # endif # if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 - -> bad word size + -> bad word size # endif # ifdef PCR @@ -1767,13 +1879,13 @@ # endif # ifdef SRC_M3 -/* Postponed for now. */ + /* Postponed for now. */ # undef PROC_VDB # undef MPROTECT_VDB # endif # ifdef SMALL_CONFIG -/* Presumably not worth the space it takes. */ + /* Presumably not worth the space it takes. */ # undef PROC_VDB # undef MPROTECT_VDB # endif @@ -1813,49 +1925,50 @@ /* platforms as well, though it should be avoided in win32. */ # endif /* LINUX */ -# if defined(SEARCH_FOR_DATA_START) && defined(GC_PRIVATE_H) +# if defined(SEARCH_FOR_DATA_START) extern ptr_t GC_data_start; +# define DATASTART GC_data_start # endif # ifndef CLEAR_DOUBLE # define CLEAR_DOUBLE(x) \ - ((word*)x)[0] = 0; \ - ((word*)x)[1] = 0; + ((word*)x)[0] = 0; \ + ((word*)x)[1] = 0; # endif /* CLEAR_DOUBLE */ -/* Internally we use GC_SOLARIS_THREADS to test for either old or pthreads. */ + /* Internally we use GC_SOLARIS_THREADS to test for either old or pthreads. */ # if defined(GC_SOLARIS_PTHREADS) && !defined(GC_SOLARIS_THREADS) # define GC_SOLARIS_THREADS # endif # if defined(GC_IRIX_THREADS) && !defined(IRIX5) ---> inconsistent configuration + --> inconsistent configuration # endif # if defined(GC_LINUX_THREADS) && !defined(LINUX) ---> inconsistent configuration + --> inconsistent configuration # endif # if defined(GC_SOLARIS_THREADS) && !defined(SUNOS5) ---> inconsistent configuration + --> inconsistent configuration # endif # if defined(GC_HPUX_THREADS) && !defined(HPUX) ---> inconsistent configuration + --> inconsistent configuration +# endif +# if defined(GC_AIX_THREADS) && !defined(_AIX) + --> inconsistent configuration # endif -# if defined(GC_WIN32_THREADS) && !defined(MSWIN32) - /* Ideally CYGWIN32 should work, in addition to MSWIN32. I suspect */ - /* the necessary code is mostly there, but nobody has actually made */ - /* sure the right combination of pieces is compiled in, etc. */ ---> inconsistent configuration +# if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32) + --> inconsistent configuration # endif # if defined(PCR) || defined(SRC_M3) || \ - defined(GC_SOLARIS_THREADS) || defined(GC_WIN32_THREADS) || \ - defined(GC_PTHREADS) + defined(GC_SOLARIS_THREADS) || defined(GC_WIN32_THREADS) || \ + defined(GC_PTHREADS) # define THREADS # endif -# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(MACOSX) \ - || defined(LINT) || defined(MSWINCE) \ - || (defined(I386) && defined(__LCC__)) +# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(DARWIN) \ + || defined(LINT) || defined(MSWINCE) || defined(ARM32) \ + || (defined(I386) && defined(__LCC__)) /* Use setjmp based hack to mark from callee-save registers. */ /* The define should move to the individual platform */ /* descriptions. */ @@ -1867,36 +1980,26 @@ /* include assembly code to do it well. */ # endif -/* Can we save call chain in objects for debugging? */ -/* SET NFRAMES (# of saved frames) and NARGS (#of args for each frame) */ -/* to reasonable values for the platform. */ -/* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified at */ -/* build time, though we feel free to adjust it slightly. */ -/* Define NEED_CALLINFO if we either save the call stack or */ -/* GC_ADD_CALLER is defined. */ -#ifdef LINUX -# include <features.h> -# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2 -# define HAVE_BUILTIN_BACKTRACE -# endif -#endif + /* Can we save call chain in objects for debugging? */ + /* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ + /* frame) to reasonable values for the platform. */ + /* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ + /* at build time, though we feel free to adjust it slightly. */ + /* Define NEED_CALLINFO if we either save the call stack or */ + /* GC_ADD_CALLER is defined. */ + /* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ #if defined(SPARC) -# define CAN_SAVE_CALL_STACKS # define CAN_SAVE_CALL_ARGS #endif #if (defined(I386) || defined(X86_64)) && defined(LINUX) - /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ - /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ -# define CAN_SAVE_CALL_STACKS + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ # define CAN_SAVE_CALL_ARGS #endif -#if defined(HAVE_BUILTIN_BACKTRACE) && !defined(CAN_SAVE_CALL_STACKS) -# define CAN_SAVE_CALL_STACKS -#endif # if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ - && defined(CAN_SAVE_CALL_STACKS) + && defined(GC_CAN_SAVE_CALL_STACKS) # define SAVE_CALL_CHAIN # endif # ifdef SAVE_CALL_CHAIN @@ -1909,7 +2012,7 @@ # ifdef SAVE_CALL_CHAIN # ifndef SAVE_CALL_COUNT # define NFRAMES 6 /* Number of frames to save. Even for */ - /* alignment reasons. */ + /* alignment reasons. */ # else # define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) # endif @@ -1925,4 +2028,96 @@ # define DBG_HDRS_ALL # endif +# if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +# endif + +# if defined(POINTER_SHIFT) && !defined(POINTER_MASK) +# define POINTER_MASK ((GC_word)(-1)) +# endif + +# if !defined(FIXUP_POINTER) && defined(POINTER_MASK) +# define FIXUP_POINTER(p) (p) = ((p) & (POINTER_MASK) << POINTER_SHIFT) +# endif + +# if defined(FIXUP_POINTER) +# define NEED_FIXUP_POINTER 1 +# else +# define NEED_FIXUP_POINTER 0 +# define FIXUP_POINTER(p) +# endif + +#ifdef GC_PRIVATE_H + /* This relies on some type definitions from gc_priv.h, from */ + /* where it's normally included. */ + /* */ + /* How to get heap memory from the OS: */ + /* Note that sbrk()-like allocation is preferred, since it */ + /* usually makes it possible to merge consecutively allocated */ + /* chunks. It also avoids unintented recursion with */ + /* -DREDIRECT_MALLOC. */ + /* GET_MEM() returns a HLKSIZE aligned chunk. */ + /* 0 is taken to mean failure. */ + /* In the case os USE_MMAP, the argument must also be a */ + /* physical page size. */ + /* GET_MEM is currently not assumed to retrieve 0 filled space, */ + /* though we should perhaps take advantage of the case in which */ + /* does. */ + struct hblk; /* See gc_priv.h. */ +# ifdef PCR + char * real_malloc(); +# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef OS2 + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ + + GC_page_size) \ + + GC_page_size-1) +# else +# if defined(NEXT) || defined(DOS4GW) || \ + (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) || \ + (defined(SUNOS5) && !defined(USE_MMAP)) +# define GET_MEM(bytes) HBLKPTR((size_t) \ + calloc(1, (size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef MSWIN32 + extern ptr_t GC_win32_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# else +# ifdef MACOS +# if defined(USE_TEMPORARY_MEMORY) + extern Ptr GC_MacTemporaryNewPtr(size_t size, + Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR( \ + GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR( \ + NewPtrClear(bytes + GC_page_size) + GC_page_size-1) +# endif +# else +# ifdef MSWINCE + extern ptr_t GC_wince_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +# else +# if defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) + extern void *GC_amiga_get_mem(size_t size); +# define GET_MEM(bytes) HBLKPTR((size_t) \ + GC_amiga_get_mem((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else + extern ptr_t GC_unix_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +# endif +# endif +# endif +# endif +# endif +# endif + +#endif /* GC_PRIVATE_H */ + # endif /* GCCONFIG_H */ diff --git a/boehm-gc/include/private/solaris_threads.h b/boehm-gc/include/private/solaris_threads.h index 1464bc14fd7..7d49c2987e0 100644 --- a/boehm-gc/include/private/solaris_threads.h +++ b/boehm-gc/include/private/solaris_threads.h @@ -16,7 +16,8 @@ # define DETACHED 2 /* Thread is intended to be detached. */ # define CLIENT_OWNS_STACK 4 /* Stack was supplied by client. */ -# define SUSPENDED 8 /* Currently suspended. */ +# define SUSPNDED 8 /* Currently suspended. */ + /* SUSPENDED is used insystem header. */ ptr_t stack; size_t stack_size; cond_t join_cv; diff --git a/boehm-gc/include/private/specific.h b/boehm-gc/include/private/specific.h index 399f84f58d6..a0e6ae0ebc1 100644 --- a/boehm-gc/include/private/specific.h +++ b/boehm-gc/include/private/specific.h @@ -85,7 +85,7 @@ static __inline__ void * PREFIXED(getspecific) (tsd * key) { unsigned hash_val = CACHE_HASH(qtid); tse * volatile * entry_ptr = key -> cache + hash_val; tse * entry = *entry_ptr; /* Must be loaded only once. */ - if (entry -> qtid == qtid) { + if (EXPECT(entry -> qtid == qtid, 1)) { GC_ASSERT(entry -> thread == pthread_self()); return entry -> value; } diff --git a/boehm-gc/irix_threads.c b/boehm-gc/irix_threads.c deleted file mode 100644 index 75b7c63125e..00000000000 --- a/boehm-gc/irix_threads.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. - * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. - * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. - * - * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED - * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. - * - * Permission is hereby granted to use or copy this program - * for any purpose, provided the above notices are retained on all copies. - * Permission to modify the code and to distribute modified code is granted, - * provided the above notices are retained, and a notice that the code was - * modified is included with the above copyright notice. - */ -/* - * 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. - * - * This now also includes an initial attempt at thread support for - * HP/UX 11. - * - * Note that there is a lot of code duplication between linux_threads.c - * and irix_threads.c; any changes made here may need to be reflected - * there too. - */ - -# if defined(GC_IRIX_THREADS) - -# include "private/gc_priv.h" -# include <pthread.h> -# include <semaphore.h> -# include <time.h> -# include <errno.h> -# include <unistd.h> -# include <sys/mman.h> -# include <sys/time.h> - -#undef pthread_create -#undef pthread_sigmask -#undef pthread_join -#undef pthread_detach - -void GC_thr_init(); - -#if 0 -void GC_print_sig_mask() -{ - sigset_t blocked; - int i; - - if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) - ABORT("pthread_sigmask"); - GC_printf0("Blocked: "); - for (i = 1; i <= MAXSIG; i++) { - if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } - } - GC_printf0("\n"); -} -#endif - -/* We use the allocation lock to protect thread-related data structures. */ - -/* The set of all known threads. We intercept thread creation and */ -/* joins. We never actually create detached threads. We allocate all */ -/* new thread stacks ourselves. These allow us to maintain this */ -/* data structure. */ -/* Protected by GC_thr_lock. */ -/* Some of this should be declared volatile, but that's incosnsistent */ -/* with some library routine declarations. */ -typedef struct GC_Thread_Rep { - struct GC_Thread_Rep * next; /* More recently allocated threads */ - /* with a given pthread id come */ - /* first. (All but the first are */ - /* guaranteed to be dead, but we may */ - /* not yet have registered the join.) */ - pthread_t id; - word stop; -# define NOT_STOPPED 0 -# define PLEASE_STOP 1 -# define STOPPED 2 - word flags; -# define FINISHED 1 /* Thread has exited. */ -# define DETACHED 2 /* Thread is intended to be detached. */ -# 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 - * a signal, and intercept client calls to change the signal mask. - * We use SIG_SUSPEND, defined in gc_priv.h. - */ - -pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER; - /* Number of threads stopped so far */ -pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER; -pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER; - -void GC_suspend_handler(int sig) -{ - int dummy; - GC_thread me; - sigset_t all_sigs; - sigset_t old_sigs; - int i; - - if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); - me = GC_lookup_thread(pthread_self()); - /* The lookup here is safe, since I'm doing this on behalf */ - /* of a thread which holds the allocation lock in order */ - /* to stop the world. Thus concurrent modification of the */ - /* data structure is impossible. */ - if (PLEASE_STOP != me -> stop) { - /* Misdirected signal. */ - pthread_mutex_unlock(&GC_suspend_lock); - return; - } - pthread_mutex_lock(&GC_suspend_lock); - me -> stack_ptr = (ptr_t)(&dummy); - me -> stop = STOPPED; - pthread_cond_signal(&GC_suspend_ack_cv); - pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock); - pthread_mutex_unlock(&GC_suspend_lock); - /* GC_printf1("Continuing 0x%x\n", pthread_self()); */ -} - - -GC_bool GC_thr_initialized = FALSE; - -size_t GC_min_stack_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_size); - result = (ptr_t)(((word)result + GC_page_size) & ~(GC_page_size - 1)); - /* Protect hottest page to detect overflow. */ -# ifdef STACK_GROWS_UP - /* mprotect(result + search_sz, GC_page_size, PROT_NONE); */ -# else - /* mprotect(result, GC_page_size, PROT_NONE); */ - result += GC_page_size; -# endif - } - *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]; - -void GC_push_thread_structures GC_PROTO((void)) -{ - GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads)); -} - -/* Add a thread to GC_threads. We assume it wasn't already there. */ -/* Caller holds allocation lock. */ -GC_thread GC_new_thread(pthread_t id) -{ - int hv = ((word)id) % THREAD_TABLE_SZ; - GC_thread result; - static struct GC_Thread_Rep first_thread; - static GC_bool first_thread_used = FALSE; - - 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_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); - } - if (result == 0) return(0); - result -> id = id; - result -> next = GC_threads[hv]; - GC_threads[hv] = result; - /* result -> flags = 0; */ - /* result -> stop = 0; */ - return(result); -} - -/* Delete a thread from GC_threads. We assume it is there. */ -/* (The code intentionally traps if it wasn't.) */ -/* Caller holds allocation lock. */ -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 result; - struct timespec timeout; - - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (p -> id != my_thread) { - if (p -> flags & FINISHED) { - p -> stop = STOPPED; - continue; - } - p -> stop = PLEASE_STOP; - result = pthread_kill(p -> id, SIG_SUSPEND); - /* GC_printf1("Sent signal to 0x%x\n", p -> id); */ - switch(result) { - case ESRCH: - /* Not really there anymore. Possible? */ - p -> stop = STOPPED; - break; - case 0: - break; - default: - ABORT("pthread_kill failed"); - } - } - } - } - pthread_mutex_lock(&GC_suspend_lock); - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - while (p -> id != my_thread && p -> stop != STOPPED) { - clock_gettime(CLOCK_REALTIME, &timeout); - timeout.tv_nsec += 50000000; /* 50 msecs */ - if (timeout.tv_nsec >= 1000000000) { - timeout.tv_nsec -= 1000000000; - ++timeout.tv_sec; - } - result = pthread_cond_timedwait(&GC_suspend_ack_cv, - &GC_suspend_lock, - &timeout); - if (result == ETIMEDOUT) { - /* Signal was lost or misdirected. Try again. */ - /* Duplicate signals should be benign. */ - result = pthread_kill(p -> id, SIG_SUSPEND); - } - } - } - } - pthread_mutex_unlock(&GC_suspend_lock); - /* GC_printf1("World stopped 0x%x\n", pthread_self()); */ -} - -/* Caller holds allocation lock. */ -void GC_start_world() -{ - GC_thread p; - unsigned i; - - /* GC_printf0("World starting\n"); */ - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - p -> stop = NOT_STOPPED; - } - } - pthread_mutex_lock(&GC_suspend_lock); - /* All other threads are at pthread_cond_wait in signal handler. */ - /* Otherwise we couldn't have acquired the lock. */ - pthread_mutex_unlock(&GC_suspend_lock); - pthread_cond_broadcast(&GC_continue_cv); -} - -# ifdef MMAP_STACKS ---> not really supported yet. -int GC_is_thread_stack(ptr_t addr) -{ - register int i; - register GC_thread p; - - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (p -> stack_size != 0) { - if (p -> stack <= addr && - addr < p -> stack + p -> stack_size) - return 1; - } - } - } - return 0; -} -# endif - -/* We hold allocation lock. Should do exactly the right thing if the */ -/* world is stopped. Should not fail if it isn't. */ -void GC_push_all_stacks() -{ - register int i; - register GC_thread p; - register ptr_t sp = GC_approx_sp(); - register ptr_t hot, cold; - 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)) { - hot = GC_approx_sp(); - } else { - hot = p -> stack_ptr; - } - if (p -> stack_size != 0) { -# ifdef STACK_GROWS_UP - cold = p -> stack; -# else - cold = p -> stack + p -> stack_size; -# endif - } else { - /* The original stack. */ - cold = GC_stackbottom; - } -# ifdef STACK_GROWS_UP - GC_push_all_stack(cold, hot); -# else - GC_push_all_stack(hot, cold); -# endif - } - } -} - - -/* We hold the allocation lock. */ -void GC_thr_init() -{ - GC_thread t; - struct sigaction act; - - if (GC_thr_initialized) return; - GC_thr_initialized = TRUE; - GC_min_stack_sz = HBLKSIZE; - (void) sigaction(SIG_SUSPEND, 0, &act); - if (act.sa_handler != SIG_DFL) - ABORT("Previously installed SIG_SUSPEND handler"); - /* Install handler. */ - act.sa_handler = GC_suspend_handler; - act.sa_flags = SA_RESTART; - (void) sigemptyset(&act.sa_mask); - if (0 != sigaction(SIG_SUSPEND, &act, 0)) - ABORT("Failed to install SIG_SUSPEND handler"); - /* Add the initial thread, so we can stop it. */ - t = GC_new_thread(pthread_self()); - t -> stack_size = 0; - 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; - word flags; - ptr_t stack; - size_t stack_size; - sem_t registered; /* 1 ==> in our thread table, but */ - /* parent hasn't yet noticed. */ -}; - -void GC_thread_exit_proc(void *arg) -{ - 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); - /* Some versions of the Irix pthreads library can erroneously */ - /* return EINTR when the call succeeds. */ - if (EINTR == result) result = 0; - if (result == 0) { - LOCK(); - /* Here the pthread thread id may have been recycled. */ - GC_delete_gc_thread(thread, thread_gc_id); - UNLOCK(); - } - return result; -} - -int GC_pthread_detach(pthread_t thread) -{ - int result; - GC_thread thread_gc_id; - - LOCK(); - thread_gc_id = GC_lookup_thread(thread); - UNLOCK(); - result = pthread_detach(thread); - if (result == 0) { - LOCK(); - thread_gc_id -> flags |= DETACHED; - /* Here the pthread thread id may have been recycled. */ - if (thread_gc_id -> flags & FINISHED) { - GC_delete_gc_thread(thread, thread_gc_id); - } - UNLOCK(); - } - return result; -} - -void * GC_start_routine(void * arg) -{ - struct start_info * si = arg; - void * result; - GC_thread me; - pthread_t my_pthread; - void *(*start)(void *); - void *start_arg; - - my_pthread = pthread_self(); - /* If a GC occurs before the thread is registered, that GC will */ - /* ignore this thread. That's fine, since it will block trying to */ - /* acquire the allocation lock, and won't yet hold interesting */ - /* pointers. */ - LOCK(); - /* We register the thread here instead of in the parent, so that */ - /* we don't need to hold the allocation lock during pthread_create. */ - /* Holding the allocation lock there would make REDIRECT_MALLOC */ - /* impossible. It probably still doesn't work, but we're a little */ - /* closer ... */ - /* This unfortunately means that we have to be careful the parent */ - /* doesn't try to do a pthread_join before we're registered. */ - me = GC_new_thread(my_pthread); - me -> flags = si -> flags; - me -> stack = si -> stack; - me -> stack_size = si -> stack_size; - me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word); - UNLOCK(); - start = si -> start_routine; - start_arg = si -> arg; - sem_post(&(si -> registered)); - pthread_cleanup_push(GC_thread_exit_proc, 0); - result = (*start)(start_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); -} - -# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source) - -int -GC_pthread_create(pthread_t *new_thread, - const pthread_attr_t *attr, - void *(*start_routine)(void *), void *arg) -{ - int result; - GC_thread t; - void * stack; - size_t stacksize; - pthread_attr_t new_attr; - int detachstate; - word my_flags = 0; - struct start_info * si = GC_malloc(sizeof(struct start_info)); - /* This is otherwise saved only in an area mmapped by the thread */ - /* library, which isn't visible to the collector. */ - - if (0 == si) return(ENOMEM); - if (0 != sem_init(&(si -> registered), 0, 0)) { - ABORT("sem_init failed"); - } - si -> start_routine = start_routine; - si -> arg = arg; - LOCK(); - if (!GC_is_initialized) GC_init(); - if (NULL == attr) { - stack = 0; - (void) pthread_attr_init(&new_attr); - } else { - copy_attr(&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; - si -> flags = my_flags; - si -> stack = stack; - si -> stack_size = stacksize; - result = pthread_create(new_thread, &new_attr, GC_start_routine, si); - if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) { - GC_stack_free(stack, stacksize); - } - UNLOCK(); - /* Wait until child has been added to the thread table. */ - /* This also ensures that we hold onto si until the child is done */ - /* with it. Thus it doesn't matter whether it is otherwise */ - /* visible to the collector. */ - while (0 != sem_wait(&(si -> registered))) { - if (errno != EINTR) { - GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno); - ABORT("sem_wait failed"); - } - } - sem_destroy(&(si -> registered)); - pthread_attr_destroy(&new_attr); /* Probably unnecessary under Irix */ - return(result); -} - -VOLATILE GC_bool GC_collecting = 0; - /* A hint that we're in the collector and */ - /* holding the allocation lock for an */ - /* extended period. */ - -/* Reasonably fast spin locks. Basically the same implementation */ -/* as STL alloc.h. */ - -#define SLEEP_THRESHOLD 3 - -unsigned long GC_allocate_lock = 0; -# define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock) -# define GC_LOCK_TAKEN GC_allocate_lock - -void GC_lock() -{ -# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ -# define high_spin_max 1000 /* spin cycles for multiprocessor */ - static unsigned spin_max = low_spin_max; - unsigned my_spin_max; - static unsigned last_spins = 0; - unsigned my_last_spins; - volatile unsigned junk; -# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk - int i; - - if (GC_TRY_LOCK()) { - return; - } - junk = 0; - my_spin_max = spin_max; - my_last_spins = last_spins; - for (i = 0; i < my_spin_max; i++) { - if (GC_collecting) goto yield; - if (i < my_last_spins/2 || GC_LOCK_TAKEN) { - PAUSE; - continue; - } - if (GC_TRY_LOCK()) { - /* - * got it! - * Spinning worked. Thus we're probably not being scheduled - * against the other process with which we were contending. - * Thus it makes sense to spin longer the next time. - */ - last_spins = i; - spin_max = high_spin_max; - return; - } - } - /* We are probably being scheduled against the other process. Sleep. */ - spin_max = low_spin_max; -yield: - for (i = 0;; ++i) { - if (GC_TRY_LOCK()) { - return; - } - if (i < SLEEP_THRESHOLD) { - sched_yield(); - } else { - struct timespec ts; - - if (i > 26) i = 26; - /* Don't wait for more than about 60msecs, even */ - /* under extreme contention. */ - ts.tv_sec = 0; - ts.tv_nsec = 1 << i; - nanosleep(&ts, 0); - } - } -} - -# else - -#ifndef LINT - int GC_no_Irix_threads; -#endif - -# endif /* GC_IRIX_THREADS */ - diff --git a/boehm-gc/linux_threads.c b/boehm-gc/linux_threads.c deleted file mode 100644 index c968e7cbfd4..00000000000 --- a/boehm-gc/linux_threads.c +++ /dev/null @@ -1,1735 +0,0 @@ -/* - * Copyright (c) 1994 by Xerox Corporation. All rights reserved. - * Copyright (c) 1996 by Silicon Graphics. All rights reserved. - * Copyright (c) 1998 by Fergus Henderson. All rights reserved. - * Copyright (c) 2000-2001 by Hewlett-Packard Company. All rights reserved. - * - * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED - * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. - * - * Permission is hereby granted to use or copy this program - * for any purpose, provided the above notices are retained on all copies. - * Permission to modify the code and to distribute modified code is granted, - * provided the above notices are retained, and a notice that the code was - * modified is included with the above copyright notice. - */ -/* - * Support code for LinuxThreads, the clone()-based kernel - * thread package for Linux which is included in libc6. - * - * This code relies on implementation details of LinuxThreads, - * (i.e. properties not guaranteed by the Pthread standard), - * though this version now does less of that than the other Pthreads - * support code. - * - * Note that there is a lot of code duplication between linux_threads.c - * and thread support for some of the other Posix platforms; any changes - * made here may need to be reflected there too. - */ -/* - * Linux_threads.c now also includes some code to support HPUX and - * OSF1 (Compaq Tru64 Unix, really). The OSF1 support is not yet - * functional. The OSF1 code is based on Eric Benson's - * patch, though that was originally against hpux_irix_threads. The code - * here is completely untested. With 0.0000001% probability, it might - * actually work. - * - * Eric also suggested an alternate basis for a lock implementation in - * his code: - * + #elif defined(OSF1) - * + unsigned long GC_allocate_lock = 0; - * + msemaphore GC_allocate_semaphore; - * + # define GC_TRY_LOCK() \ - * + ((msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) == 0) \ - * + ? (GC_allocate_lock = 1) \ - * + : 0) - * + # define GC_LOCK_TAKEN GC_allocate_lock - */ - -/* #define DEBUG_THREADS 1 */ - -/* ANSI C requires that a compilation unit contains something */ - -# include "gc.h" - -# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ - && !defined(GC_IRIX_THREADS) - -# include "private/gc_priv.h" - -# if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \ - && !defined(USE_HPUX_TLS) -# define USE_HPUX_TLS -# endif - -# ifdef THREAD_LOCAL_ALLOC -# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS) -# include "private/specific.h" -# endif -# if defined(USE_PTHREAD_SPECIFIC) -# define GC_getspecific pthread_getspecific -# define GC_setspecific pthread_setspecific -# define GC_key_create pthread_key_create - typedef pthread_key_t GC_key_t; -# endif -# if defined(USE_HPUX_TLS) -# define GC_getspecific(x) (x) -# define GC_setspecific(key, v) ((key) = (v), 0) -# define GC_key_create(key, d) 0 - typedef void * GC_key_t; -# endif -# endif -# include <stdlib.h> -# include <pthread.h> -# include <sched.h> -# include <time.h> -# include <errno.h> -# include <unistd.h> -# include <sys/mman.h> -# include <sys/time.h> -# include <semaphore.h> -# include <signal.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <fcntl.h> - -#ifndef __GNUC__ -# define __inline__ -#endif - -#ifdef GC_USE_LD_WRAP -# define WRAP_FUNC(f) __wrap_##f -# define REAL_FUNC(f) __real_##f -#else -# define WRAP_FUNC(f) GC_##f -# define REAL_FUNC(f) f -# undef pthread_create -# undef pthread_sigmask -# undef pthread_join -# undef pthread_detach -#endif - - -void GC_thr_init(); - -#if 0 -void GC_print_sig_mask() -{ - sigset_t blocked; - int i; - - if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) - ABORT("pthread_sigmask"); - GC_printf0("Blocked: "); - for (i = 1; i <= MAXSIG; i++) { - if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } - } - GC_printf0("\n"); -} -#endif - - -/* We use the allocation lock to protect thread-related data structures. */ - -/* The set of all known threads. We intercept thread creation and */ -/* joins. */ -/* Protected by allocation/GC lock. */ -/* Some of this should be declared volatile, but that's inconsistent */ -/* with some library routine declarations. */ -typedef struct GC_Thread_Rep { - struct GC_Thread_Rep * next; /* More recently allocated threads */ - /* with a given pthread id come */ - /* first. (All but the first are */ - /* guaranteed to be dead, but we may */ - /* not yet have registered the join.) */ - pthread_t id; - short flags; -# define FINISHED 1 /* Thread has exited. */ -# define DETACHED 2 /* Thread is intended to be detached. */ -# define MAIN_THREAD 4 /* True for the original thread only. */ - short thread_blocked; /* Protected by GC lock. */ - /* Treated as a boolean value. If set, */ - /* thread will acquire GC lock before */ - /* doing any pointer manipulations, and */ - /* has set its sp value. Thus it does */ - /* not need to be sent a signal to stop */ - /* it. */ - ptr_t stack_end; /* Cold end of the stack. */ - ptr_t stack_ptr; /* Valid only when stopped. */ -# ifdef IA64 - ptr_t backing_store_end; - ptr_t backing_store_ptr; -# endif - int signal; - void * status; /* The value returned from the thread. */ - /* Used only to avoid premature */ - /* reclamation of any data it might */ - /* reference. */ -# ifdef THREAD_LOCAL_ALLOC -# if CPP_WORDSZ == 64 && defined(ALIGN_DOUBLE) -# define GRANULARITY 16 -# define NFREELISTS 49 -# else -# define GRANULARITY 8 -# define NFREELISTS 65 -# endif - /* The ith free list corresponds to size i*GRANULARITY */ -# define INDEX_FROM_BYTES(n) ((ADD_SLOP(n) + GRANULARITY - 1)/GRANULARITY) -# define BYTES_FROM_INDEX(i) ((i) * GRANULARITY - EXTRA_BYTES) -# define SMALL_ENOUGH(bytes) (ADD_SLOP(bytes) <= \ - (NFREELISTS-1)*GRANULARITY) - ptr_t ptrfree_freelists[NFREELISTS]; - ptr_t normal_freelists[NFREELISTS]; -# ifdef GC_GCJ_SUPPORT - ptr_t gcj_freelists[NFREELISTS]; -# endif - /* Free lists contain either a pointer or a small count */ - /* reflecting the number of granules allocated at that */ - /* size. */ - /* 0 ==> thread-local allocation in use, free list */ - /* empty. */ - /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ - /* too few objects of this size have been */ - /* allocated by this thread. */ - /* >= HBLKSIZE => pointer to nonempty free list. */ - /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ - /* local alloc, equivalent to 0. */ -# define DIRECT_GRANULES (HBLKSIZE/GRANULARITY) - /* Don't use local free lists for up to this much */ - /* allocation. */ -# endif -} * GC_thread; - -GC_thread GC_lookup_thread(pthread_t id); - -static GC_bool parallel_initialized = FALSE; - -void GC_init_parallel(); - -# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) - -/* We don't really support thread-local allocation with DBG_HDRS_ALL */ - -#ifdef USE_HPUX_TLS - __thread -#endif -GC_key_t GC_thread_key; - -static GC_bool keys_initialized; - -/* Recover the contents of the freelist array fl into the global one gfl.*/ -/* Note that the indexing scheme differs, in that gfl has finer size */ -/* resolution, even if not all entries are used. */ -/* We hold the allocator lock. */ -static void return_freelists(ptr_t *fl, ptr_t *gfl) -{ - int i; - ptr_t q, *qptr; - size_t nwords; - - for (i = 1; i < NFREELISTS; ++i) { - nwords = i * (GRANULARITY/sizeof(word)); - qptr = fl + i; - q = *qptr; - if ((word)q >= HBLKSIZE) { - if (gfl[nwords] == 0) { - gfl[nwords] = q; - } else { - /* Concatenate: */ - for (; (word)q >= HBLKSIZE; qptr = &(obj_link(q)), q = *qptr); - GC_ASSERT(0 == q); - *qptr = gfl[nwords]; - gfl[nwords] = fl[i]; - } - } - /* Clear fl[i], since the thread structure may hang around. */ - /* Do it in a way that is likely to trap if we access it. */ - fl[i] = (ptr_t)HBLKSIZE; - } -} - -/* We statically allocate a single "size 0" object. It is linked to */ -/* itself, and is thus repeatedly reused for all size 0 allocation */ -/* requests. (Size 0 gcj allocation requests are incorrect, and */ -/* we arrange for those to fault asap.) */ -static ptr_t size_zero_object = (ptr_t)(&size_zero_object); - -/* Each thread structure must be initialized. */ -/* This call must be made from the new thread. */ -/* Caller holds allocation lock. */ -void GC_init_thread_local(GC_thread p) -{ - int i; - - if (!keys_initialized) { - if (0 != GC_key_create(&GC_thread_key, 0)) { - ABORT("Failed to create key for local allocator"); - } - keys_initialized = TRUE; - } - if (0 != GC_setspecific(GC_thread_key, p)) { - ABORT("Failed to set thread specific allocation pointers"); - } - for (i = 1; i < NFREELISTS; ++i) { - p -> ptrfree_freelists[i] = (ptr_t)1; - p -> normal_freelists[i] = (ptr_t)1; -# ifdef GC_GCJ_SUPPORT - p -> gcj_freelists[i] = (ptr_t)1; -# endif - } - /* Set up the size 0 free lists. */ - p -> ptrfree_freelists[0] = (ptr_t)(&size_zero_object); - p -> normal_freelists[0] = (ptr_t)(&size_zero_object); -# ifdef GC_GCJ_SUPPORT - p -> gcj_freelists[0] = (ptr_t)(-1); -# endif -} - -#ifdef GC_GCJ_SUPPORT - extern ptr_t * GC_gcjobjfreelist; -#endif - -/* We hold the allocator lock. */ -void GC_destroy_thread_local(GC_thread p) -{ - /* We currently only do this from the thread itself. */ - GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p); - return_freelists(p -> ptrfree_freelists, GC_aobjfreelist); - return_freelists(p -> normal_freelists, GC_objfreelist); -# ifdef GC_GCJ_SUPPORT - return_freelists(p -> gcj_freelists, GC_gcjobjfreelist); -# endif -} - -extern GC_PTR GC_generic_malloc_many(); - -GC_PTR GC_local_malloc(size_t bytes) -{ - if (EXPECT(!SMALL_ENOUGH(bytes),0)) { - return(GC_malloc(bytes)); - } else { - int index = INDEX_FROM_BYTES(bytes); - ptr_t * my_fl; - ptr_t my_entry; - GC_key_t k = GC_thread_key; - void * tsd; - -# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC) \ - || !defined(__GNUC__) - if (EXPECT(0 == k, 0)) { - /* This can happen if we get called when the world is */ - /* being initialized. Whether we can actually complete */ - /* the initialization then is unclear. */ - GC_init_parallel(); - k = GC_thread_key; - } -# endif - tsd = GC_getspecific(GC_thread_key); -# ifdef GC_ASSERTIONS - LOCK(); - GC_ASSERT(tsd == (void *)GC_lookup_thread(pthread_self())); - UNLOCK(); -# endif - my_fl = ((GC_thread)tsd) -> normal_freelists + index; - my_entry = *my_fl; - if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { - ptr_t next = obj_link(my_entry); - GC_PTR result = (GC_PTR)my_entry; - *my_fl = next; - obj_link(my_entry) = 0; - PREFETCH_FOR_WRITE(next); - return result; - } else if ((word)my_entry - 1 < DIRECT_GRANULES) { - *my_fl = my_entry + index + 1; - return GC_malloc(bytes); - } else { - GC_generic_malloc_many(BYTES_FROM_INDEX(index), NORMAL, my_fl); - if (*my_fl == 0) return GC_oom_fn(bytes); - return GC_local_malloc(bytes); - } - } -} - -GC_PTR GC_local_malloc_atomic(size_t bytes) -{ - if (EXPECT(!SMALL_ENOUGH(bytes), 0)) { - return(GC_malloc_atomic(bytes)); - } else { - int index = INDEX_FROM_BYTES(bytes); - ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key)) - -> ptrfree_freelists + index; - ptr_t my_entry = *my_fl; - if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { - GC_PTR result = (GC_PTR)my_entry; - *my_fl = obj_link(my_entry); - return result; - } else if ((word)my_entry - 1 < DIRECT_GRANULES) { - *my_fl = my_entry + index + 1; - return GC_malloc_atomic(bytes); - } else { - GC_generic_malloc_many(BYTES_FROM_INDEX(index), PTRFREE, my_fl); - /* *my_fl is updated while the collector is excluded; */ - /* the free list is always visible to the collector as */ - /* such. */ - if (*my_fl == 0) return GC_oom_fn(bytes); - return GC_local_malloc_atomic(bytes); - } - } -} - -#ifdef GC_GCJ_SUPPORT - -#include "include/gc_gcj.h" - -#ifdef GC_ASSERTIONS - extern GC_bool GC_gcj_malloc_initialized; -#endif - -extern int GC_gcj_kind; - -GC_PTR GC_local_gcj_malloc(size_t bytes, - void * ptr_to_struct_containing_descr) -{ - GC_ASSERT(GC_gcj_malloc_initialized); - if (EXPECT(!SMALL_ENOUGH(bytes), 0)) { - return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr); - } else { - int index = INDEX_FROM_BYTES(bytes); - ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key)) - -> gcj_freelists + index; - ptr_t my_entry = *my_fl; - if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { - GC_PTR result = (GC_PTR)my_entry; - GC_ASSERT(!GC_incremental); - /* We assert that any concurrent marker will stop us. */ - /* Thus it is impossible for a mark procedure to see the */ - /* allocation of the next object, but to see this object */ - /* still containing a free list pointer. Otherwise the */ - /* marker might find a random "mark descriptor". */ - *(volatile ptr_t *)my_fl = obj_link(my_entry); - /* We must update the freelist before we store the pointer. */ - /* Otherwise a GC at this point would see a corrupted */ - /* free list. */ - /* A memory barrier is probably never needed, since the */ - /* action of stopping this thread will cause prior writes */ - /* to complete. */ - GC_ASSERT(((void * volatile *)result)[1] == 0); - *(void * volatile *)result = ptr_to_struct_containing_descr; - return result; - } else if ((word)my_entry - 1 < DIRECT_GRANULES) { - *my_fl = my_entry + index + 1; - return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr); - } else { - GC_generic_malloc_many(BYTES_FROM_INDEX(index), GC_gcj_kind, my_fl); - if (*my_fl == 0) return GC_oom_fn(bytes); - return GC_local_gcj_malloc(bytes, ptr_to_struct_containing_descr); - } - } -} - -#endif /* GC_GCJ_SUPPORT */ - -# else /* !THREAD_LOCAL_ALLOC && !DBG_HDRS_ALL */ - -# define GC_destroy_thread_local(t) - -# endif /* !THREAD_LOCAL_ALLOC */ - -/* - * We use signals to stop threads during GC. - * - * Suspended threads wait in signal handler for SIG_THR_RESTART. - * That's more portable than semaphores or condition variables. - * (We do use sem_post from a signal handler, but that should be portable.) - * - * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h. - * Note that we can't just stop a thread; we need it to save its stack - * pointer(s) and acknowledge. - */ - -#ifndef SIG_THR_RESTART -# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) -# define SIG_THR_RESTART _SIGRTMIN + 5 -# else -# define SIG_THR_RESTART SIGXCPU -# endif -#endif - -sem_t GC_suspend_ack_sem; - -#if 0 -/* -To make sure that we're using LinuxThreads and not some other thread -package, we generate a dummy reference to `pthread_kill_other_threads_np' -(was `__pthread_initial_thread_bos' but that disappeared), -which is a symbol defined in LinuxThreads, but (hopefully) not in other -thread packages. - -We no longer do this, since this code is now portable enough that it might -actually work for something else. -*/ -void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np; -#endif /* 0 */ - -#if defined(SPARC) || defined(IA64) - extern word GC_save_regs_in_stack(); -#endif - -long GC_nprocs = 1; /* Number of processors. We may not have */ - /* access to all of them, but this is as good */ - /* a guess as any ... */ - -#ifdef PARALLEL_MARK - -# ifndef MAX_MARKERS -# define MAX_MARKERS 16 -# endif - -static ptr_t marker_sp[MAX_MARKERS] = {0}; - -void * GC_mark_thread(void * id) -{ - word my_mark_no = 0; - - marker_sp[(word)id] = GC_approx_sp(); - for (;; ++my_mark_no) { - /* GC_mark_no is passed only to allow GC_help_marker to terminate */ - /* promptly. This is important if it were called from the signal */ - /* handler or from the GC lock acquisition code. Under Linux, it's */ - /* not safe to call it from a signal handler, since it uses mutexes */ - /* and condition variables. Since it is called only here, the */ - /* argument is unnecessary. */ - if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) { - /* resynchronize if we get far off, e.g. because GC_mark_no */ - /* wrapped. */ - my_mark_no = GC_mark_no; - } -# ifdef DEBUG_THREADS - GC_printf1("Starting mark helper for mark number %ld\n", my_mark_no); -# endif - GC_help_marker(my_mark_no); - } -} - -extern long GC_markers; /* Number of mark threads we would */ - /* like to have. Includes the */ - /* initiating thread. */ - -pthread_t GC_mark_threads[MAX_MARKERS]; - -#define PTHREAD_CREATE REAL_FUNC(pthread_create) - -static void start_mark_threads() -{ - unsigned i; - pthread_attr_t attr; - - if (GC_markers > MAX_MARKERS) { - WARN("Limiting number of mark threads\n", 0); - GC_markers = MAX_MARKERS; - } - if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); - - if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) - ABORT("pthread_attr_setdetachstate failed"); - -# ifdef HPUX - /* Default stack size is usually too small: fix it. */ - /* Otherwise marker threads or GC may run out of */ - /* space. */ -# define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word)) - { - size_t old_size; - int code; - - if (pthread_attr_getstacksize(&attr, &old_size) != 0) - ABORT("pthread_attr_getstacksize failed\n"); - if (old_size < MIN_STACK_SIZE) { - if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) - ABORT("pthread_attr_setstacksize failed\n"); - } - } -# endif /* HPUX */ -# ifdef CONDPRINT - if (GC_print_stats) { - GC_printf1("Starting %ld marker threads\n", GC_markers - 1); - } -# endif - for (i = 0; i < GC_markers - 1; ++i) { - if (0 != PTHREAD_CREATE(GC_mark_threads + i, &attr, - GC_mark_thread, (void *)(word)i)) { - WARN("Marker thread creation failed, errno = %ld.\n", errno); - } - } -} - -#else /* !PARALLEL_MARK */ - -static __inline__ void start_mark_threads() -{ -} - -#endif /* !PARALLEL_MARK */ - -void GC_suspend_handler(int sig) -{ - int dummy; - pthread_t my_thread = pthread_self(); - GC_thread me; - sigset_t all_sigs; - sigset_t old_sigs; - int i; - sigset_t mask; -# ifdef PARALLEL_MARK - word my_mark_no = GC_mark_no; - /* Marker can't proceed until we acknowledge. Thus this is */ - /* guaranteed to be the mark_no correspending to our */ - /* suspension, i.e. the marker can't have incremented it yet. */ -# endif - - if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); - -#if DEBUG_THREADS - GC_printf1("Suspending 0x%x\n", my_thread); -#endif - - me = GC_lookup_thread(my_thread); - /* The lookup here is safe, since I'm doing this on behalf */ - /* of a thread which holds the allocation lock in order */ - /* to stop the world. Thus concurrent modification of the */ - /* data structure is impossible. */ -# ifdef SPARC - me -> stack_ptr = (ptr_t)GC_save_regs_in_stack(); -# else - me -> stack_ptr = (ptr_t)(&dummy); -# endif -# ifdef IA64 - me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack(); -# endif - - /* Tell the thread that wants to stop the world that this */ - /* thread has been stopped. Note that sem_post() is */ - /* the only async-signal-safe primitive in LinuxThreads. */ - sem_post(&GC_suspend_ack_sem); - - /* Wait until that thread tells us to restart by sending */ - /* this thread a SIG_THR_RESTART signal. */ - /* SIG_THR_RESTART should be masked at this point. Thus there */ - /* is no race. */ - if (sigfillset(&mask) != 0) ABORT("sigfillset() failed"); - if (sigdelset(&mask, SIG_THR_RESTART) != 0) ABORT("sigdelset() failed"); -# ifdef NO_SIGNALS - if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed"); - if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed"); - if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed"); - if (sigdelset(&mask, SIGABRT) != 0) ABORT("sigdelset() failed"); -# endif - do { - me->signal = 0; - sigsuspend(&mask); /* Wait for signal */ - } while (me->signal != SIG_THR_RESTART); - -#if DEBUG_THREADS - GC_printf1("Continuing 0x%x\n", my_thread); -#endif -} - -void GC_restart_handler(int sig) -{ - GC_thread me; - - if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler"); - - /* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */ - /* The lookup here is safe, since I'm doing this on behalf */ - /* of a thread which holds the allocation lock in order */ - /* to stop the world. Thus concurrent modification of the */ - /* data structure is impossible. */ - me = GC_lookup_thread(pthread_self()); - me->signal = SIG_THR_RESTART; - - /* - ** Note: even if we didn't do anything useful here, - ** it would still be necessary to have a signal handler, - ** rather than ignoring the signals, otherwise - ** the signals will not be delivered at all, and - ** will thus not interrupt the sigsuspend() above. - */ - -#if DEBUG_THREADS - GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self()); -#endif -} - -/* Defining INSTALL_LOOPING_SEGV_HANDLER causes SIGSEGV and SIGBUS to */ -/* result in an infinite loop in a signal handler. This can be very */ -/* useful for debugging, since (as of RH7) gdb still seems to have */ -/* serious problems with threads. */ -#ifdef INSTALL_LOOPING_SEGV_HANDLER -void GC_looping_handler(int sig) -{ - GC_printf3("Signal %ld in thread %lx, pid %ld\n", - sig, pthread_self(), getpid()); - for (;;); -} -#endif - -GC_bool GC_thr_initialized = FALSE; - -# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ -volatile GC_thread GC_threads[THREAD_TABLE_SZ]; - -void GC_push_thread_structures GC_PROTO((void)) -{ - GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads)); -} - -#ifdef THREAD_LOCAL_ALLOC -/* We must explicitly mark ptrfree and gcj free lists, since the free */ -/* list links wouldn't otherwise be found. We also set them in the */ -/* normal free lists, since that involves touching less memory than if */ -/* we scanned them normally. */ -void GC_mark_thread_local_free_lists(void) -{ - int i, j; - GC_thread p; - ptr_t q; - - for (i = 0; i < THREAD_TABLE_SZ; ++i) { - for (p = GC_threads[i]; 0 != p; p = p -> next) { - for (j = 1; j < NFREELISTS; ++j) { - q = p -> ptrfree_freelists[j]; - if ((word)q > HBLKSIZE) GC_set_fl_marks(q); - q = p -> normal_freelists[j]; - if ((word)q > HBLKSIZE) GC_set_fl_marks(q); -# ifdef GC_GCJ_SUPPORT - q = p -> gcj_freelists[j]; - if ((word)q > HBLKSIZE) GC_set_fl_marks(q); -# endif /* GC_GCJ_SUPPORT */ - } - } - } -} -#endif /* THREAD_LOCAL_ALLOC */ - -/* Add a thread to GC_threads. We assume it wasn't already there. */ -/* Caller holds allocation lock. */ -GC_thread GC_new_thread(pthread_t id) -{ - int hv = ((word)id) % THREAD_TABLE_SZ; - GC_thread result; - static struct GC_Thread_Rep first_thread; - static GC_bool first_thread_used = FALSE; - - if (!first_thread_used) { - result = &first_thread; - first_thread_used = TRUE; - } else { - result = (struct GC_Thread_Rep *) - GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); - } - if (result == 0) return(0); - result -> id = id; - result -> next = GC_threads[hv]; - GC_threads[hv] = result; - GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 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; - } - GC_INTERNAL_FREE(p); -} - -/* 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; - } - GC_INTERNAL_FREE(p); -} - -/* 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); -} - -/* There seems to be a very rare thread stopping problem. To help us */ -/* debug that, we save the ids of the stopping thread. */ -pthread_t GC_stopping_thread; -int GC_stopping_pid; - -/* 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_stopping_thread = my_thread; /* debugging only. */ - GC_stopping_pid = getpid(); /* debugging only. */ - /* Make sure all free list construction has stopped before we start. */ - /* No new construction can start, since free list construction is */ - /* required to acquire and release the GC lock before it starts, */ - /* and we have the lock. */ -# ifdef PARALLEL_MARK - GC_acquire_mark_lock(); - GC_ASSERT(GC_fl_builder_count == 0); - /* We should have previously waited for it to become zero. */ -# endif /* PARALLEL_MARK */ - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (p -> id != my_thread) { - if (p -> flags & FINISHED) continue; - if (p -> thread_blocked) /* Will wait */ continue; - n_live_threads++; - #if DEBUG_THREADS - GC_printf1("Sending suspend signal to 0x%x\n", p -> id); - #endif - result = pthread_kill(p -> id, SIG_SUSPEND); - switch(result) { - case ESRCH: - /* Not really there anymore. Possible? */ - n_live_threads--; - break; - case 0: - break; - default: - ABORT("pthread_kill failed"); - } - } - } - } - for (i = 0; i < n_live_threads; i++) { - if (0 != sem_wait(&GC_suspend_ack_sem)) - ABORT("sem_wait in handler failed"); - } -# ifdef PARALLEL_MARK - GC_release_mark_lock(); -# endif - #if DEBUG_THREADS - GC_printf1("World stopped 0x%x\n", pthread_self()); - #endif - GC_stopping_thread = 0; /* debugging only */ -} - -/* Caller holds allocation lock, and has held it continuously since */ -/* the world stopped. */ -void GC_start_world() -{ - pthread_t my_thread = pthread_self(); - register int i; - register GC_thread p; - register int n_live_threads = 0; - register int result; - -# if DEBUG_THREADS - GC_printf0("World starting\n"); -# endif - - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (p -> id != my_thread) { - if (p -> flags & FINISHED) continue; - if (p -> thread_blocked) continue; - n_live_threads++; - #if DEBUG_THREADS - GC_printf1("Sending restart signal to 0x%x\n", p -> id); - #endif - result = pthread_kill(p -> id, SIG_THR_RESTART); - switch(result) { - case ESRCH: - /* Not really there anymore. Possible? */ - n_live_threads--; - break; - case 0: - break; - default: - ABORT("pthread_kill failed"); - } - } - } - } - #if DEBUG_THREADS - GC_printf0("World started\n"); - #endif - GC_stopping_thread = 0; /* debugging only */ -} - -# ifdef IA64 -# define IF_IA64(x) x -# else -# define IF_IA64(x) -# endif -/* We hold allocation lock. Should do exactly the right thing if the */ -/* world is stopped. Should not fail if it isn't. */ -void GC_push_all_stacks() -{ - int i; - GC_thread p; - ptr_t sp = GC_approx_sp(); - ptr_t lo, hi; - /* On IA64, we also need to scan the register backing store. */ - IF_IA64(ptr_t bs_lo; ptr_t bs_hi;) - pthread_t me = pthread_self(); - - if (!GC_thr_initialized) GC_thr_init(); - #if DEBUG_THREADS - GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me); - #endif - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (p -> flags & FINISHED) continue; - if (pthread_equal(p -> id, me)) { -# ifdef SPARC - lo = (ptr_t)GC_save_regs_in_stack(); -# else - lo = GC_approx_sp(); -# endif - IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();) - } else { - lo = p -> stack_ptr; - IF_IA64(bs_hi = p -> backing_store_ptr;) - } - if ((p -> flags & MAIN_THREAD) == 0) { - hi = p -> stack_end; - IF_IA64(bs_lo = p -> backing_store_end); - } else { - /* The original stack. */ - hi = GC_stackbottom; - IF_IA64(bs_lo = BACKING_STORE_BASE;) - } - #if DEBUG_THREADS - GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n", - (unsigned long) p -> id, - (unsigned long) lo, (unsigned long) hi); - #endif - if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n"); -# ifdef STACK_GROWS_UP - /* We got them backwards! */ - GC_push_all_stack(hi, lo); -# else - GC_push_all_stack(lo, hi); -# endif -# ifdef IA64 - if (pthread_equal(p -> id, me)) { - GC_push_all_eager(bs_lo, bs_hi); - } else { - GC_push_all_stack(bs_lo, bs_hi); - } -# endif - } - } -} - -#ifdef USE_PROC_FOR_LIBRARIES -int GC_segment_is_thread_stack(ptr_t lo, ptr_t hi) -{ - int i; - GC_thread p; - -# ifdef PARALLEL_MARK - for (i = 0; i < GC_markers; ++i) { - if (marker_sp[i] > lo & marker_sp[i] < hi) return 1; - } -# endif - for (i = 0; i < THREAD_TABLE_SZ; i++) { - for (p = GC_threads[i]; p != 0; p = p -> next) { - if (0 != p -> stack_end) { -# ifdef STACK_GROWS_UP - if (p -> stack_end >= lo && p -> stack_end < hi) return 1; -# else /* STACK_GROWS_DOWN */ - if (p -> stack_end > lo && p -> stack_end <= hi) return 1; -# endif - } - } - } - return 0; -} -#endif /* USE_PROC_FOR_LIBRARIES */ - -#ifdef GC_LINUX_THREADS -/* Return the number of processors, or i<= 0 if it can't be determined. */ -int GC_get_nprocs() -{ - /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */ - /* appears to be buggy in many cases. */ - /* We look for lines "cpu<n>" in /proc/stat. */ -# define STAT_BUF_SIZE 4096 -# if defined(GC_USE_LD_WRAP) -# define STAT_READ __real_read -# else -# define STAT_READ read -# endif - char stat_buf[STAT_BUF_SIZE]; - int f; - char c; - word result = 1; - /* Some old kernels only have a single "cpu nnnn ..." */ - /* entry in /proc/stat. We identify those as */ - /* uniprocessors. */ - size_t i, len = 0; - - f = open("/proc/stat", O_RDONLY); - if (f < 0 || (len = STAT_READ(f, stat_buf, STAT_BUF_SIZE)) < 100) { - WARN("Couldn't read /proc/stat\n", 0); - return -1; - } - close(f); - for (i = 0; i < len - 100; ++i) { - if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c' - && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') { - int cpu_no = atoi(stat_buf + i + 4); - if (cpu_no >= result) result = cpu_no + 1; - } - } - return result; -} -#endif /* GC_LINUX_THREADS */ - -/* We hold the allocation lock. */ -void GC_thr_init() -{ - int dummy; - GC_thread t; - struct sigaction act; - - if (GC_thr_initialized) return; - GC_thr_initialized = TRUE; - - if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0) - ABORT("sem_init failed"); - - act.sa_flags = SA_RESTART; - if (sigfillset(&act.sa_mask) != 0) { - ABORT("sigfillset() failed"); - } -# ifdef NO_SIGNALS - if (sigdelset(&act.sa_mask, SIGINT) != 0 - || sigdelset(&act.sa_mask, SIGQUIT != 0) - || sigdelset(&act.sa_mask, SIGABRT != 0) - || sigdelset(&act.sa_mask, SIGTERM != 0)) { - ABORT("sigdelset() failed"); - } -# endif - - /* SIG_THR_RESTART is unmasked by the handler when necessary. */ - act.sa_handler = GC_suspend_handler; - if (sigaction(SIG_SUSPEND, &act, NULL) != 0) { - ABORT("Cannot set SIG_SUSPEND handler"); - } - - act.sa_handler = GC_restart_handler; - if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) { - ABORT("Cannot set SIG_THR_RESTART handler"); - } -# ifdef INSTALL_LOOPING_SEGV_HANDLER - act.sa_handler = GC_looping_handler; - if (sigaction(SIGSEGV, &act, NULL) != 0 - || sigaction(SIGBUS, &act, NULL) != 0) { - ABORT("Cannot set SIGSEGV or SIGBUS looping handler"); - } -# endif /* INSTALL_LOOPING_SEGV_HANDLER */ - - /* Add the initial thread, so we can stop it. */ - t = GC_new_thread(pthread_self()); - t -> stack_ptr = (ptr_t)(&dummy); - t -> flags = DETACHED | MAIN_THREAD; - - /* Set GC_nprocs. */ - { - char * nprocs_string = GETENV("GC_NPROCS"); - GC_nprocs = -1; - if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string); - } - if (GC_nprocs <= 0) { -# if defined(GC_HPUX_THREADS) - GC_nprocs = pthread_num_processors_np(); -# endif -# if defined(GC_OSF1_THREADS) || defined(GC_FREEBSD_THREADS) - GC_nprocs = 1; -# endif -# if defined(GC_LINUX_THREADS) - GC_nprocs = GC_get_nprocs(); -# endif - } - if (GC_nprocs <= 0) { - WARN("GC_get_nprocs() returned %ld\n", GC_nprocs); - GC_nprocs = 2; -# ifdef PARALLEL_MARK - GC_markers = 1; -# endif - } else { -# ifdef PARALLEL_MARK - GC_markers = GC_nprocs; -# endif - } -# ifdef PARALLEL_MARK -# ifdef CONDPRINT - if (GC_print_stats) { - GC_printf2("Number of processors = %ld, " - "number of marker threads = %ld\n", GC_nprocs, GC_markers); - } -# endif - if (GC_markers == 1) { - GC_parallel = FALSE; -# ifdef CONDPRINT - if (GC_print_stats) { - GC_printf0("Single marker thread, turning off parallel marking\n"); - } -# endif - } else { - GC_parallel = TRUE; - } -# endif -} - - -/* Perform all initializations, including those that */ -/* may require allocation. */ -/* Called as constructor without allocation lock. */ -/* Must be called before a second thread is created. */ -/* Called without allocation lock. */ -void GC_init_parallel() -{ - if (parallel_initialized) return; - parallel_initialized = TRUE; - /* GC_init() calls us back, so set flag first. */ - if (!GC_is_initialized) GC_init(); - /* If we are using a parallel marker, start the helper threads. */ -# ifdef PARALLEL_MARK - if (GC_parallel) start_mark_threads(); -# endif - /* Initialize thread local free lists if used. */ -# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) - LOCK(); - GC_init_thread_local(GC_lookup_thread(pthread_self())); - UNLOCK(); -# endif -} - - -int WRAP_FUNC(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(REAL_FUNC(pthread_sigmask)(how, set, oset)); -} - -/* Wrappers for functions that are likely to block for an appreciable */ -/* length of time. Must be called in pairs, if at all. */ -/* Nothing much beyond the system call itself should be executed */ -/* between these. */ - -void GC_start_blocking(void) { -# define SP_SLOP 128 - GC_thread me; - LOCK(); - me = GC_lookup_thread(pthread_self()); - GC_ASSERT(!(me -> thread_blocked)); -# ifdef SPARC - me -> stack_ptr = (ptr_t)GC_save_regs_in_stack(); -# else - me -> stack_ptr = (ptr_t)GC_approx_sp(); -# endif -# ifdef IA64 - me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack() + SP_SLOP; -# endif - /* Add some slop to the stack pointer, since the wrapped call may */ - /* end up pushing more callee-save registers. */ -# ifdef STACK_GROWS_UP - me -> stack_ptr += SP_SLOP; -# else - me -> stack_ptr -= SP_SLOP; -# endif - me -> thread_blocked = TRUE; - UNLOCK(); -} - -GC_end_blocking(void) { - GC_thread me; - LOCK(); /* This will block if the world is stopped. */ - me = GC_lookup_thread(pthread_self()); - GC_ASSERT(me -> thread_blocked); - me -> thread_blocked = FALSE; - UNLOCK(); -} - -/* A wrapper for the standard C sleep function */ -int WRAP_FUNC(sleep) (unsigned int seconds) -{ - int result; - - GC_start_blocking(); - result = REAL_FUNC(sleep)(seconds); - GC_end_blocking(); - return result; -} - -struct start_info { - void *(*start_routine)(void *); - void *arg; - word flags; - sem_t registered; /* 1 ==> in our thread table, but */ - /* parent hasn't yet noticed. */ -}; - -/* Called at thread exit. */ -/* Never called for main thread. That's OK, since it */ -/* results in at most a tiny one-time leak. And */ -/* linuxthreads doesn't reclaim the main threads */ -/* resources or id anyway. */ -void GC_thread_exit_proc(void *arg) -{ - GC_thread me; - - LOCK(); - me = GC_lookup_thread(pthread_self()); - GC_destroy_thread_local(me); - if (me -> flags & DETACHED) { - GC_delete_thread(pthread_self()); - } else { - me -> flags |= FINISHED; - } -# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \ - && !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL) - GC_remove_specific(GC_thread_key); -# endif - if (GC_incremental && GC_collection_in_progress()) { - int old_gc_no = GC_gc_no; - - /* Make sure that no part of our stack is still on the mark stack, */ - /* since it's about to be unmapped. */ - while (GC_incremental && GC_collection_in_progress() - && old_gc_no == GC_gc_no) { - ENTER_GC(); - GC_collect_a_little_inner(1); - EXIT_GC(); - UNLOCK(); - sched_yield(); - LOCK(); - } - } - UNLOCK(); -} - -int WRAP_FUNC(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 = REAL_FUNC(pthread_join)(thread, retval); -# if defined (GC_FREEBSD_THREADS) - /* On FreeBSD, the wrapped pthread_join() sometimes returns (what - appears to be) a spurious EINTR which caused the test and real code - to gratuitously fail. Having looked at system pthread library source - code, I see how this return code may be generated. In one path of - code, pthread_join() just returns the errno setting of the thread - being joined. This does not match the POSIX specification or the - local man pages thus I have taken the liberty to catch this one - spurious return value properly conditionalized on GC_FREEBSD_THREADS. */ - if (result == EINTR) result = 0; -# endif - if (result == 0) { - LOCK(); - /* Here the pthread thread id may have been recycled. */ - GC_delete_gc_thread(thread, thread_gc_id); - UNLOCK(); - } - return result; -} - -int -WRAP_FUNC(pthread_detach)(pthread_t thread) -{ - int result; - GC_thread thread_gc_id; - - LOCK(); - thread_gc_id = GC_lookup_thread(thread); - UNLOCK(); - result = REAL_FUNC(pthread_detach)(thread); - if (result == 0) { - LOCK(); - thread_gc_id -> flags |= DETACHED; - /* Here the pthread thread id may have been recycled. */ - if (thread_gc_id -> flags & FINISHED) { - GC_delete_gc_thread(thread, thread_gc_id); - } - UNLOCK(); - } - return result; -} - -void * GC_start_routine(void * arg) -{ - int dummy; - struct start_info * si = arg; - void * result; - GC_thread me; - pthread_t my_pthread; - void *(*start)(void *); - void *start_arg; - - my_pthread = pthread_self(); -# ifdef DEBUG_THREADS - GC_printf1("Starting thread 0x%lx\n", my_pthread); - GC_printf1("pid = %ld\n", (long) getpid()); - GC_printf1("sp = 0x%lx\n", (long) &arg); -# endif - LOCK(); - me = GC_new_thread(my_pthread); - me -> flags = si -> flags; - me -> stack_ptr = 0; - /* me -> stack_end = GC_linux_stack_base(); -- currently (11/99) */ - /* doesn't work because the stack base in /proc/self/stat is the */ - /* one for the main thread. There is a strong argument that that's */ - /* a kernel bug, but a pervasive one. */ -# ifdef STACK_GROWS_DOWN - me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1)) - & ~(GC_page_size - 1)); - me -> stack_ptr = me -> stack_end - 0x10; - /* Needs to be plausible, since an asynchronous stack mark */ - /* should not crash. */ -# else - me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1)); - me -> stack_ptr = me -> stack_end + 0x10; -# endif - /* This is dubious, since we may be more than a page into the stack, */ - /* and hence skip some of it, though it's not clear that matters. */ -# ifdef IA64 - me -> backing_store_end = (ptr_t) - (GC_save_regs_in_stack() & ~(GC_page_size - 1)); - /* This is also < 100% convincing. We should also read this */ - /* from /proc, but the hook to do so isn't there yet. */ -# endif /* IA64 */ - UNLOCK(); - start = si -> start_routine; -# ifdef DEBUG_THREADS - GC_printf1("start_routine = 0x%lx\n", start); -# endif - start_arg = si -> arg; -# ifdef DEBUG_THREADS - GC_printf1("sem_post from 0x%lx\n", my_pthread); -# endif - sem_post(&(si -> registered)); /* Last action on si. */ - /* OK to deallocate. */ - pthread_cleanup_push(GC_thread_exit_proc, 0); -# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) - LOCK(); - GC_init_thread_local(me); - UNLOCK(); -# endif - result = (*start)(start_arg); -#if DEBUG_THREADS - GC_printf1("Finishing thread 0x%x\n", pthread_self()); -#endif - me -> status = result; - me -> flags |= FINISHED; - pthread_cleanup_pop(1); - /* Cleanup acquires lock, ensuring that we can't exit */ - /* while a collection that thinks we're alive is trying to stop */ - /* us. */ - return(result); -} - -int -WRAP_FUNC(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; - int detachstate; - word my_flags = 0; - struct start_info * si; - /* This is otherwise saved only in an area mmapped by the thread */ - /* library, which isn't visible to the collector. */ - - LOCK(); - si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info), NORMAL); - UNLOCK(); - if (!parallel_initialized) GC_init_parallel(); - if (0 == si) return(ENOMEM); - sem_init(&(si -> registered), 0, 0); - si -> start_routine = start_routine; - si -> arg = arg; - LOCK(); - if (!GC_thr_initialized) GC_thr_init(); - if (NULL == attr) { - detachstate = PTHREAD_CREATE_JOINABLE; - } else { - pthread_attr_getdetachstate(attr, &detachstate); - } - if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; - si -> flags = my_flags; - UNLOCK(); -# ifdef DEBUG_THREADS - GC_printf1("About to start new thread from thread 0x%X\n", - pthread_self()); -# endif - result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, si); -# ifdef DEBUG_THREADS - GC_printf1("Started thread 0x%X\n", *new_thread); -# endif - /* Wait until child has been added to the thread table. */ - /* This also ensures that we hold onto si until the child is done */ - /* with it. Thus it doesn't matter whether it is otherwise */ - /* visible to the collector. */ - while (0 != sem_wait(&(si -> registered))) { - if (EINTR != errno) ABORT("sem_wait failed"); - } -# ifdef DEBUG_THREADS - GC_printf1("sem_wait complete from thread 0x%X\n", - pthread_self()); -# endif - sem_destroy(&(si -> registered)); - LOCK(); - GC_INTERNAL_FREE(si); - UNLOCK(); - return(result); -} - -#ifdef GENERIC_COMPARE_AND_SWAP - pthread_mutex_t GC_compare_and_swap_lock = PTHREAD_MUTEX_INITIALIZER; - - GC_bool GC_compare_and_exchange(volatile GC_word *addr, - GC_word old, GC_word new_val) - { - GC_bool result; - pthread_mutex_lock(&GC_compare_and_swap_lock); - if (*addr == old) { - *addr = new_val; - result = TRUE; - } else { - result = FALSE; - } - pthread_mutex_unlock(&GC_compare_and_swap_lock); - return result; - } - - GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much) - { - GC_word old; - pthread_mutex_lock(&GC_compare_and_swap_lock); - old = *addr; - *addr = old + how_much; - pthread_mutex_unlock(&GC_compare_and_swap_lock); - return old; - } - -#endif /* GENERIC_COMPARE_AND_SWAP */ -/* Spend a few cycles in a way that can't introduce contention with */ -/* othre threads. */ -void GC_pause() -{ - int i; - volatile word dummy = 0; - - for (i = 0; i < 10; ++i) { -# ifdef __GNUC__ - __asm__ __volatile__ (" " : : : "memory"); -# else - /* Something that's unlikely to be optimized away. */ - GC_noop(++dummy); -# endif - } -} - -#define SPIN_MAX 1024 /* Maximum number of calls to GC_pause before */ - /* give up. */ - -VOLATILE GC_bool GC_collecting = 0; - /* A hint that we're in the collector and */ - /* holding the allocation lock for an */ - /* extended period. */ - -#if !defined(USE_SPIN_LOCK) || defined(PARALLEL_MARK) -/* If we don't want to use the below spinlock implementation, either */ -/* because we don't have a GC_test_and_set implementation, or because */ -/* we don't want to risk sleeping, we can still try spinning on */ -/* pthread_mutex_trylock for a while. This appears to be very */ -/* beneficial in many cases. */ -/* I suspect that under high contention this is nearly always better */ -/* than the spin lock. But it's a bit slower on a uniprocessor. */ -/* Hence we still default to the spin lock. */ -/* This is also used to acquire the mark lock for the parallel */ -/* marker. */ - -/* Here we use a strict exponential backoff scheme. I don't know */ -/* whether that's better or worse than the above. We eventually */ -/* yield by calling pthread_mutex_lock(); it never makes sense to */ -/* explicitly sleep. */ - -void GC_generic_lock(pthread_mutex_t * lock) -{ - unsigned pause_length = 1; - unsigned i; - - if (0 == pthread_mutex_trylock(lock)) return; - for (; pause_length <= SPIN_MAX; pause_length <<= 1) { - for (i = 0; i < pause_length; ++i) { - GC_pause(); - } - switch(pthread_mutex_trylock(lock)) { - case 0: - return; - case EBUSY: - break; - default: - ABORT("Unexpected error from pthread_mutex_trylock"); - } - } - pthread_mutex_lock(lock); -} - -#endif /* !USE_SPIN_LOCK || PARALLEL_MARK */ - -#if defined(USE_SPIN_LOCK) - -/* Reasonably fast spin locks. Basically the same implementation */ -/* as STL alloc.h. This isn't really the right way to do this. */ -/* but until the POSIX scheduling mess gets straightened out ... */ - -volatile unsigned int GC_allocate_lock = 0; - - -void GC_lock() -{ -# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ -# define high_spin_max SPIN_MAX /* spin cycles for multiprocessor */ - static unsigned spin_max = low_spin_max; - unsigned my_spin_max; - static unsigned last_spins = 0; - unsigned my_last_spins; - int i; - - if (!GC_test_and_set(&GC_allocate_lock)) { - return; - } - my_spin_max = spin_max; - my_last_spins = last_spins; - for (i = 0; i < my_spin_max; i++) { - if (GC_collecting || GC_nprocs == 1) goto yield; - if (i < my_last_spins/2 || GC_allocate_lock) { - GC_pause(); - continue; - } - if (!GC_test_and_set(&GC_allocate_lock)) { - /* - * got it! - * Spinning worked. Thus we're probably not being scheduled - * against the other process with which we were contending. - * Thus it makes sense to spin longer the next time. - */ - last_spins = i; - spin_max = high_spin_max; - return; - } - } - /* We are probably being scheduled against the other process. Sleep. */ - spin_max = low_spin_max; -yield: - for (i = 0;; ++i) { - if (!GC_test_and_set(&GC_allocate_lock)) { - return; - } -# define SLEEP_THRESHOLD 12 - /* nanosleep(<= 2ms) just spins under Linux. We */ - /* want to be careful to avoid that behavior. */ - if (i < SLEEP_THRESHOLD) { - sched_yield(); - } else { - struct timespec ts; - - if (i > 24) i = 24; - /* Don't wait for more than about 15msecs, even */ - /* under extreme contention. */ - ts.tv_sec = 0; - ts.tv_nsec = 1 << i; - nanosleep(&ts, 0); - } - } -} - -#else /* !USE_SPINLOCK */ - -void GC_lock() -{ - if (1 == GC_nprocs || GC_collecting) { - pthread_mutex_lock(&GC_allocate_ml); - } else { - GC_generic_lock(&GC_allocate_ml); - } -} - -#endif /* !USE_SPINLOCK */ - -#if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) - -#ifdef GC_ASSERTIONS - pthread_t GC_mark_lock_holder = NO_THREAD; -#endif - -#if 0 - /* Ugly workaround for a linux threads bug in the final versions */ - /* of glibc2.1. Pthread_mutex_trylock sets the mutex owner */ - /* field even when it fails to acquire the mutex. This causes */ - /* pthread_cond_wait to die. Remove for glibc2.2. */ - /* According to the man page, we should use */ - /* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, but that isn't actually */ - /* defined. */ - static pthread_mutex_t mark_mutex = - {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}}; -#else - static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - -static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; - -void GC_acquire_mark_lock() -{ -/* - if (pthread_mutex_lock(&mark_mutex) != 0) { - ABORT("pthread_mutex_lock failed"); - } -*/ - GC_generic_lock(&mark_mutex); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = pthread_self(); -# endif -} - -void GC_release_mark_lock() -{ - GC_ASSERT(GC_mark_lock_holder == pthread_self()); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = NO_THREAD; -# endif - if (pthread_mutex_unlock(&mark_mutex) != 0) { - ABORT("pthread_mutex_unlock failed"); - } -} - -/* Collector must wait for a freelist builders for 2 reasons: */ -/* 1) Mark bits may still be getting examined without lock. */ -/* 2) Partial free lists referenced only by locals may not be scanned */ -/* correctly, e.g. if they contain "pointer-free" objects, since the */ -/* free-list link may be ignored. */ -void GC_wait_builder() -{ - GC_ASSERT(GC_mark_lock_holder == pthread_self()); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = NO_THREAD; -# endif - if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) { - ABORT("pthread_cond_wait failed"); - } - GC_ASSERT(GC_mark_lock_holder == NO_THREAD); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = pthread_self(); -# endif -} - -void GC_wait_for_reclaim() -{ - GC_acquire_mark_lock(); - while (GC_fl_builder_count > 0) { - GC_wait_builder(); - } - GC_release_mark_lock(); -} - -void GC_notify_all_builder() -{ - GC_ASSERT(GC_mark_lock_holder == pthread_self()); - if (pthread_cond_broadcast(&builder_cv) != 0) { - ABORT("pthread_cond_broadcast failed"); - } -} - -#endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ - -#ifdef PARALLEL_MARK - -static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; - -void GC_wait_marker() -{ - GC_ASSERT(GC_mark_lock_holder == pthread_self()); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = NO_THREAD; -# endif - if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) { - ABORT("pthread_cond_wait failed"); - } - GC_ASSERT(GC_mark_lock_holder == NO_THREAD); -# ifdef GC_ASSERTIONS - GC_mark_lock_holder = pthread_self(); -# endif -} - -void GC_notify_all_marker() -{ - if (pthread_cond_broadcast(&mark_cv) != 0) { - ABORT("pthread_cond_broadcast failed"); - } -} - -#endif /* PARALLEL_MARK */ - -# endif /* GC_LINUX_THREADS and friends */ - diff --git a/boehm-gc/mach_dep.c b/boehm-gc/mach_dep.c index a741058d70e..3dc5f0b27b9 100644 --- a/boehm-gc/mach_dep.c +++ b/boehm-gc/mach_dep.c @@ -74,7 +74,8 @@ asm static void PushMacRegisters() /* on your architecture. Run the test_setjmp program to see whether */ /* there is any chance it will work. */ -#ifndef USE_GENERIC_PUSH_REGS +#if !defined(USE_GENERIC_PUSH_REGS) && !defined(USE_ASM_PUSH_REGS) +#undef HAVE_PUSH_REGS void GC_push_regs() { # ifdef RT @@ -91,6 +92,7 @@ void GC_push_regs() asm("pushl r8"); asm("calls $1,_GC_push_one"); asm("pushl r7"); asm("calls $1,_GC_push_one"); asm("pushl r6"); asm("calls $1,_GC_push_one"); +# define HAVE_PUSH_REGS # endif # if defined(M68K) && (defined(SUNOS4) || defined(NEXT)) /* M68K SUNOS - could be replaced by generic code */ @@ -113,6 +115,7 @@ void GC_push_regs() asm("movl d7,sp@"); asm("jbsr _GC_push_one"); asm("addqw #0x4,sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif # if defined(M68K) && defined(HP) @@ -135,6 +138,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif /* M68K HP */ # if defined(M68K) && defined(AMIGA) @@ -158,6 +162,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # else /* !__GNUC__ */ GC_push_one(getreg(REG_A2)); GC_push_one(getreg(REG_A3)); @@ -174,6 +179,7 @@ void GC_push_regs() GC_push_one(getreg(REG_D5)); GC_push_one(getreg(REG_D6)); GC_push_one(getreg(REG_D7)); +# define HAVE_PUSH_REGS # endif /* !__GNUC__ */ # endif /* AMIGA */ @@ -196,10 +202,12 @@ void GC_push_regs() PushMacReg(d7); add.w #4,sp ; fix stack. } +# define HAVE_PUSH_REGS # undef PushMacReg # endif /* THINK_C */ # if defined(__MWERKS__) PushMacRegisters(); +# define HAVE_PUSH_REGS # endif /* __MWERKS__ */ # endif /* MACOS */ @@ -222,13 +230,15 @@ void GC_push_regs() asm("pushl %esi"); asm("call _GC_push_one"); asm("addl $4,%esp"); asm("pushl %edi"); asm("call _GC_push_one"); asm("addl $4,%esp"); asm("pushl %ebx"); asm("call _GC_push_one"); asm("addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \ || ( defined(I386) && defined(FREEBSD) && defined(__ELF__) ) \ || ( defined(I386) && defined(NETBSD) && defined(__ELF__) ) \ || ( defined(I386) && defined(OPENBSD) && defined(__ELF__) ) \ - || ( defined(I386) && defined(HURD) && defined(__ELF__) ) + || ( defined(I386) && defined(HURD) && defined(__ELF__) ) \ + || ( defined(I386) && defined(DGUX) ) /* This is modified for Linux with ELF (Note: _ELF_ only) */ /* This section handles FreeBSD with ELF. */ @@ -243,6 +253,7 @@ void GC_push_regs() asm("pushl %esi; call GC_push_one; addl $4,%esp"); asm("pushl %edi; call GC_push_one; addl $4,%esp"); asm("pushl %ebx; call GC_push_one; addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if ( defined(I386) && defined(BEOS) && defined(__ELF__) ) @@ -254,6 +265,7 @@ void GC_push_regs() asm("pushl %esi; call GC_push_one; addl $4,%esp"); asm("pushl %edi; call GC_push_one; addl $4,%esp"); asm("pushl %ebx; call GC_push_one; addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if defined(I386) && defined(MSWIN32) && !defined(__MINGW32__) \ @@ -280,6 +292,7 @@ void GC_push_regs() __asm push edi __asm call GC_push_one __asm add esp,4 +# define HAVE_PUSH_REGS # endif # if defined(I386) && (defined(SVR4) || defined(SCO) || defined(SCO_ELF)) @@ -291,6 +304,7 @@ void GC_push_regs() asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # ifdef NS32K @@ -299,14 +313,12 @@ void GC_push_regs() asm ("movd r5, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); asm ("movd r6, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); asm ("movd r7, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); +# define HAVE_PUSH_REGS # endif # if defined(SPARC) - { - word GC_save_regs_in_stack(); - - GC_save_regs_ret_val = GC_save_regs_in_stack(); - } + GC_save_regs_ret_val = GC_save_regs_in_stack(); +# define HAVE_PUSH_REGS # endif # ifdef RT @@ -322,6 +334,7 @@ void GC_push_regs() asm("cas r11, r13, r0"); GC_push_one(TMP_SP); /* through */ asm("cas r11, r14, r0"); GC_push_one(TMP_SP); /* r15 */ asm("cas r11, r15, r0"); GC_push_one(TMP_SP); +# define HAVE_PUSH_REGS # endif # if defined(M68K) && defined(SYSV) @@ -345,6 +358,7 @@ void GC_push_regs() asm("movl %d7,%sp@"); asm("jbsr GC_push_one"); asm("addqw #0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # else /* !__GNUC__*/ asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ @@ -362,6 +376,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif /* !__GNUC__ */ # endif /* M68K/SYSV */ @@ -371,21 +386,19 @@ void GC_push_regs() extern int *__libc_stack_end; GC_push_all_stack (sp, __libc_stack_end); +# define HAVE_PUSH_REGS + /* Isn't this redundant with the code to push the stack? */ } # endif /* other machines... */ -# if !defined(M68K) && !defined(VAX) && !defined(RT) -# if !defined(SPARC) && !defined(I386) && !defined(NS32K) -# if !defined(POWERPC) && !defined(UTS4) -# if !defined(PJ) && !(defined(MIPS) && defined(LINUX)) - --> bad news <-- -# endif -# endif -# endif +# if !defined(HAVE_PUSH_REGS) + --> We just generated an empty GC_push_regs, which + --> is almost certainly broken. Try defining + --> USE_GENERIC_PUSH_REGS instead. # endif } -#endif /* !USE_GENERIC_PUSH_REGS */ +#endif /* !USE_GENERIC_PUSH_REGS && !USE_ASM_PUSH_REGS */ #if defined(USE_GENERIC_PUSH_REGS) void GC_generic_push_regs(cold_gc_frame) @@ -427,8 +440,6 @@ ptr_t cold_gc_frame; /* needed on IA64, since some non-windowed registers are */ /* preserved. */ { - word GC_save_regs_in_stack(); - GC_save_regs_ret_val = GC_save_regs_in_stack(); /* On IA64 gcc, could use __builtin_ia64_flushrs() and */ /* __builtin_ia64_flushrs(). The latter will be done */ @@ -445,7 +456,7 @@ ptr_t cold_gc_frame; /* the stack. Return sp. */ # ifdef SPARC asm(" .seg \"text\""); -# ifdef SVR4 +# if defined(SVR4) || defined(NETBSD) asm(" .globl GC_save_regs_in_stack"); asm("GC_save_regs_in_stack:"); asm(" .type GC_save_regs_in_stack,#function"); diff --git a/boehm-gc/malloc.c b/boehm-gc/malloc.c index 5ac21421bea..f5c8f06b0dd 100644 --- a/boehm-gc/malloc.c +++ b/boehm-gc/malloc.c @@ -182,6 +182,7 @@ register int k; ptr_t result; DCL_LOCK_STATE; + if (GC_have_errors) GC_print_all_errors(); GC_INVOKE_FINALIZERS(); if (SMALL_OBJ(lb)) { DISABLE_SIGNALS(); @@ -294,6 +295,11 @@ DCL_LOCK_STATE; return(GENERAL_MALLOC((word)lb, NORMAL)); } /* See above comment on signals. */ + GC_ASSERT(0 == obj_link(op) + || (word)obj_link(op) + <= (word)GC_greatest_plausible_heap_addr + && (word)obj_link(op) + >= (word)GC_least_plausible_heap_addr); *opp = obj_link(op); obj_link(op) = 0; GC_words_allocd += lw; @@ -338,6 +344,7 @@ DCL_LOCK_STATE; return((GC_PTR)REDIRECT_MALLOC(n*lb)); } +#ifndef strdup # include <string.h> # ifdef __STDC__ char *strdup(const char *s) @@ -346,11 +353,16 @@ DCL_LOCK_STATE; char *s; # endif { - size_t len = strlen + 1; + size_t len = strlen(s) + 1; char * result = ((char *)REDIRECT_MALLOC(len+1)); BCOPY(s, result, len+1); return result; } +#endif /* !defined(strdup) */ + /* If strdup is macro defined, we assume that it actually calls malloc, */ + /* and thus the right thing will happen even without overriding it. */ + /* This seems to be true on most Linux systems. */ + # endif /* REDIRECT_MALLOC */ /* Explicitly deallocate an object p. */ @@ -373,6 +385,7 @@ DCL_LOCK_STATE; /* Required by ANSI. It's not my fault ... */ h = HBLKPTR(p); hhdr = HDR(h); + GC_ASSERT(GC_base(p) == p); # if defined(REDIRECT_MALLOC) && \ (defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \ || defined(__MINGW32__)) /* Should this be MSWIN32 in general? */ @@ -454,7 +467,10 @@ void GC_free_inner(GC_PTR p) } #endif /* THREADS */ -# ifdef REDIRECT_MALLOC +# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_FREE) +# define REDIRECT_FREE GC_free +# endif +# ifdef REDIRECT_FREE # ifdef __STDC__ void free(GC_PTR p) # else @@ -463,7 +479,7 @@ void GC_free_inner(GC_PTR p) # endif { # ifndef IGNORE_FREE - GC_free(p); + REDIRECT_FREE(p); # endif } # endif /* REDIRECT_MALLOC */ diff --git a/boehm-gc/mallocx.c b/boehm-gc/mallocx.c index 031fcaf91a8..06f45622dfb 100644 --- a/boehm-gc/mallocx.c +++ b/boehm-gc/mallocx.c @@ -142,7 +142,11 @@ int obj_kind; } } -# if defined(REDIRECT_MALLOC) || defined(REDIRECT_REALLOC) +# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_REALLOC) +# define REDIRECT_REALLOC GC_realloc +# endif + +# ifdef REDIRECT_REALLOC # ifdef __STDC__ GC_PTR realloc(GC_PTR p, size_t lb) # else @@ -151,13 +155,9 @@ int obj_kind; size_t lb; # endif { -# ifdef REDIRECT_REALLOC - return(REDIRECT_REALLOC(p, lb)); -# else - return(GC_realloc(p, lb)); -# endif + return(REDIRECT_REALLOC(p, lb)); } -# endif /* REDIRECT_MALLOC */ +# endif /* REDIRECT_REALLOC */ /* The same thing, except caller does not hold allocation lock. */ @@ -177,6 +177,7 @@ register int k; lw = ROUNDED_UP_WORDS(lb); n_blocks = OBJ_SZ_TO_BLOCKS(lw); init = GC_obj_kinds[k].ok_init; + if (GC_have_errors) GC_print_all_errors(); GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); @@ -286,6 +287,7 @@ register struct obj_kind * kind = GC_obj_kinds + k; register ptr_t op; DCL_LOCK_STATE; + if (GC_have_errors) GC_print_all_errors(); GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); @@ -354,6 +356,7 @@ DCL_LOCK_STATE; return; } lw = ALIGNED_WORDS(lb); + if (GC_have_errors) GC_print_all_errors(); GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); @@ -375,6 +378,7 @@ DCL_LOCK_STATE; while ((hbp = *rlh) != 0) { hhdr = HDR(hbp); *rlh = hhdr -> hb_next; + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; # ifdef PARALLEL_MARK { signed_word my_words_allocd_tmp = GC_words_allocd_tmp; @@ -575,6 +579,44 @@ DCL_LOCK_STATE; } } +#ifdef __STDC__ +/* Not well tested nor integrated. */ +/* Debug version is tricky and currently missing. */ +#include <limits.h> + +GC_PTR GC_memalign(size_t align, size_t lb) +{ + size_t new_lb; + size_t offset; + ptr_t result; + +# ifdef ALIGN_DOUBLE + if (align <= WORDS_TO_BYTES(2) && lb > align) return GC_malloc(lb); +# endif + if (align <= WORDS_TO_BYTES(1)) return GC_malloc(lb); + if (align >= HBLKSIZE/2 || lb >= HBLKSIZE/2) { + if (align > HBLKSIZE) return GC_oom_fn(LONG_MAX-1024) /* Fail */; + return GC_malloc(lb <= HBLKSIZE? HBLKSIZE : lb); + /* Will be HBLKSIZE aligned. */ + } + /* We could also try to make sure that the real rounded-up object size */ + /* is a multiple of align. That would be correct up to HBLKSIZE. */ + new_lb = lb + align - 1; + result = GC_malloc(new_lb); + offset = (word)result % align; + if (offset != 0) { + offset = align - offset; + if (!GC_all_interior_pointers) { + if (offset >= VALID_OFFSET_SZ) return GC_malloc(HBLKSIZE); + GC_register_displacement(offset); + } + } + result = (GC_PTR) ((ptr_t)result + offset); + GC_ASSERT((word)result % align == 0); + return result; +} +#endif + # ifdef ATOMIC_UNCOLLECTABLE /* Allocate lb bytes of pointerfree, untraced, uncollectable data */ /* This is normally roughly equivalent to the system malloc. */ diff --git a/boehm-gc/mark.c b/boehm-gc/mark.c index eb5b9eeb34d..ca947290291 100644 --- a/boehm-gc/mark.c +++ b/boehm-gc/mark.c @@ -19,6 +19,10 @@ # include <stdio.h> # include "private/gc_pmark.h" +#if defined(MSWIN32) && defined(__GNUC__) +# include <excpt.h> +#endif + /* We put this here to minimize the risk of inlining. */ /*VARARGS*/ #ifdef __WATCOMC__ @@ -261,20 +265,20 @@ static void alloc_mark_stack(); /* remains valid until all marking is complete. */ /* A zero value indicates that it's OK to miss some */ /* register values. */ -GC_bool GC_mark_some(cold_gc_frame) -ptr_t cold_gc_frame; +/* We hold the allocation lock. In the case of */ +/* incremental collection, the world may not be stopped.*/ +#ifdef MSWIN32 + /* For win32, this is called after we establish a structured */ + /* exception handler, in case Windows unmaps one of our root */ + /* segments. See below. In either case, we acquire the */ + /* allocator lock long before we get here. */ + GC_bool GC_mark_some_inner(cold_gc_frame) + ptr_t cold_gc_frame; +#else + GC_bool GC_mark_some(cold_gc_frame) + ptr_t cold_gc_frame; +#endif { -#if defined(MSWIN32) && !defined(__GNUC__) - /* Windows 98 appears to asynchronously create and remove writable */ - /* memory mappings, for reasons we haven't yet understood. Since */ - /* we look for writable regions to determine the root set, we may */ - /* try to mark from an address range that disappeared since we */ - /* started the collection. Thus we have to recover from faults here. */ - /* This code does not appear to be necessary for Windows 95/NT/2000. */ - /* Note that this code should never generate an incremental GC write */ - /* fault. */ - __try { -#endif /* defined(MSWIN32) && !defined(__GNUC__) */ switch(GC_mark_state) { case MS_NONE: return(FALSE); @@ -395,23 +399,130 @@ ptr_t cold_gc_frame; ABORT("GC_mark_some: bad state"); return(FALSE); } -#if defined(MSWIN32) && !defined(__GNUC__) - } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? - EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { -# ifdef CONDPRINT - if (GC_print_stats) { - GC_printf0("Caught ACCESS_VIOLATION in marker. " - "Memory mapping disappeared.\n"); +} + + +#ifdef MSWIN32 + +# ifdef __GNUC__ + + typedef struct { + EXCEPTION_REGISTRATION ex_reg; + void *alt_path; + } ext_ex_regn; + + + static EXCEPTION_DISPOSITION mark_ex_handler( + struct _EXCEPTION_RECORD *ex_rec, + void *est_frame, + struct _CONTEXT *context, + void *disp_ctxt) + { + if (ex_rec->ExceptionCode == STATUS_ACCESS_VIOLATION) { + ext_ex_regn *xer = (ext_ex_regn *)est_frame; + + /* Unwind from the inner function assuming the standard */ + /* function prologue. */ + /* Assumes code has not been compiled with */ + /* -fomit-frame-pointer. */ + context->Esp = context->Ebp; + context->Ebp = *((DWORD *)context->Esp); + context->Esp = context->Esp - 8; + + /* Resume execution at the "real" handler within the */ + /* wrapper function. */ + context->Eip = (DWORD )(xer->alt_path); + + return ExceptionContinueExecution; + + } else { + return ExceptionContinueSearch; + } + } +# endif /* __GNUC__ */ + + + GC_bool GC_mark_some(cold_gc_frame) + ptr_t cold_gc_frame; + { + GC_bool ret_val; + +# ifndef __GNUC__ + /* Windows 98 appears to asynchronously create and remove */ + /* writable memory mappings, for reasons we haven't yet */ + /* understood. Since we look for writable regions to */ + /* determine the root set, we may try to mark from an */ + /* address range that disappeared since we started the */ + /* collection. Thus we have to recover from faults here. */ + /* This code does not appear to be necessary for Windows */ + /* 95/NT/2000. Note that this code should never generate */ + /* an incremental GC write fault. */ + + __try { + +# else /* __GNUC__ */ + + /* Manually install an exception handler since GCC does */ + /* not yet support Structured Exception Handling (SEH) on */ + /* Win32. */ + + ext_ex_regn er; + + er.alt_path = &&handle_ex; + er.ex_reg.handler = mark_ex_handler; + asm volatile ("movl %%fs:0, %0" : "=r" (er.ex_reg.prev)); + asm volatile ("movl %0, %%fs:0" : : "r" (&er)); + +# endif /* __GNUC__ */ + + ret_val = GC_mark_some_inner(cold_gc_frame); + +# ifndef __GNUC__ + + } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + +# else /* __GNUC__ */ + + /* Prevent GCC from considering the following code unreachable */ + /* and thus eliminating it. */ + if (er.alt_path != 0) + goto rm_handler; + +handle_ex: + /* Execution resumes from here on an access violation. */ + +# endif /* __GNUC__ */ + +# ifdef CONDPRINT + if (GC_print_stats) { + GC_printf0("Caught ACCESS_VIOLATION in marker. " + "Memory mapping disappeared.\n"); + } +# endif /* CONDPRINT */ + + /* We have bad roots on the stack. Discard mark stack. */ + /* Rescan from marked objects. Redetermine roots. */ + GC_invalidate_mark_state(); + scan_ptr = 0; + + ret_val = FALSE; + +# ifndef __GNUC__ + } -# endif /* CONDPRINT */ - /* We have bad roots on the stack. Discard mark stack. */ - /* Rescan from marked objects. Redetermine roots. */ - GC_invalidate_mark_state(); - scan_ptr = 0; - return FALSE; + +# else /* __GNUC__ */ + +rm_handler: + /* Uninstall the exception handler */ + asm volatile ("mov %0, %%fs:0" : : "r" (er.ex_reg.prev)); + +# endif /* __GNUC__ */ + + return ret_val; } -#endif /* defined(MSWIN32) && !defined(__GNUC__) */ -} +#endif /* MSWIN32 */ GC_bool GC_mark_stack_empty() @@ -434,13 +545,7 @@ GC_bool GC_mark_stack_empty() /* for the large object. */ /* - just return current if it does not point to a large object. */ /*ARGSUSED*/ -# ifdef PRINT_BLACK_LIST - ptr_t GC_find_start(current, hhdr, new_hdr_p, source) - ptr_t source; -# else - ptr_t GC_find_start(current, hhdr, new_hdr_p) -# define source 0 -# endif +ptr_t GC_find_start(current, hhdr, new_hdr_p) register ptr_t current; register hdr *hhdr, **new_hdr_p; { @@ -468,7 +573,6 @@ register hdr *hhdr, **new_hdr_p; } else { return(current); } -# undef source } void GC_invalidate_mark_state() @@ -546,8 +650,8 @@ mse * mark_stack_limit; /* Large length. */ /* Process part of the range to avoid pushing too much on the */ /* stack. */ - GC_ASSERT(descr < GC_greatest_plausible_heap_addr - - GC_least_plausible_heap_addr); + GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr + - (word)GC_least_plausible_heap_addr); # ifdef PARALLEL_MARK # define SHARE_BYTES 2048 if (descr > SHARE_BYTES && GC_parallel @@ -578,6 +682,7 @@ mse * mark_stack_limit; while (descr != 0) { if ((signed_word)descr < 0) { current = *current_p; + FIXUP_POINTER(current); if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { PREFETCH(current); HC_PUSH_CONTENTS((ptr_t)current, mark_stack_top, @@ -652,6 +757,7 @@ mse * mark_stack_limit; PREFETCH((ptr_t)limit - PREF_DIST*CACHE_LINE_SIZE); GC_ASSERT(limit >= current_p); deferred = *limit; + FIXUP_POINTER(deferred); limit = (word *)((char *)limit - ALIGNMENT); if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { PREFETCH(deferred); @@ -661,6 +767,7 @@ mse * mark_stack_limit; /* Unroll once, so we don't do too many of the prefetches */ /* based on limit. */ deferred = *limit; + FIXUP_POINTER(deferred); limit = (word *)((char *)limit - ALIGNMENT); if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { PREFETCH(deferred); @@ -675,6 +782,7 @@ mse * mark_stack_limit; /* Since HC_PUSH_CONTENTS expands to a lot of code, */ /* we don't. */ current = *current_p; + FIXUP_POINTER(current); PREFETCH((ptr_t)current_p + PREF_DIST*CACHE_LINE_SIZE); if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { /* Prefetch the contents of the object we just pushed. It's */ @@ -726,22 +834,33 @@ mse * GC_steal_mark_stack(mse * low, mse * high, mse * local, mse *top = local - 1; unsigned i = 0; + /* Make sure that prior writes to the mark stack are visible. */ + /* On some architectures, the fact that the reads are */ + /* volatile should suffice. */ +# if !defined(IA64) && !defined(HP_PA) && !defined(I386) + GC_memory_barrier(); +# endif GC_ASSERT(high >= low-1 && high - low + 1 <= GC_mark_stack_size); for (p = low; p <= high && i <= max; ++p) { word descr = *(volatile word *) &(p -> mse_descr); + /* In the IA64 memory model, the following volatile store is */ + /* ordered after this read of descr. Thus a thread must read */ + /* the original nonzero value. HP_PA appears to be similar, */ + /* and if I'm reading the P4 spec correctly, X86 is probably */ + /* also OK. In some other cases we need a barrier. */ +# if !defined(IA64) && !defined(HP_PA) && !defined(I386) + GC_memory_barrier(); +# endif if (descr != 0) { *(volatile word *) &(p -> mse_descr) = 0; + /* More than one thread may get this entry, but that's only */ + /* a minor performance problem. */ ++top; top -> mse_descr = descr; top -> mse_start = p -> mse_start; GC_ASSERT( top -> mse_descr & GC_DS_TAGS != GC_DS_LENGTH || top -> mse_descr < GC_greatest_plausible_heap_addr - GC_least_plausible_heap_addr); - /* There is no synchronization here. We assume that at */ - /* least one thread will see the original descriptor. */ - /* Otherwise we need a barrier. */ - /* More than one thread may get this entry, but that's only */ - /* a minor performance problem. */ /* If this is a big object, count it as */ /* size/256 + 1 objects. */ ++i; @@ -778,7 +897,7 @@ void GC_return_mark_stack(mse * low, mse * high) BCOPY(low, my_start, stack_size * sizeof(mse)); GC_ASSERT(GC_mark_stack_top = my_top); # if !defined(IA64) && !defined(HP_PA) - GC_memory_write_barrier(); + GC_memory_barrier(); # endif /* On IA64, the volatile write acts as a release barrier. */ GC_mark_stack_top = my_top + stack_size; @@ -1342,8 +1461,8 @@ ptr_t top; # define GC_least_plausible_heap_addr least_ha if (top == 0) return; - /* check all pointers in range and put in push if they appear */ - /* to be valid. */ + /* check all pointers in range and push if they appear */ + /* to be valid. */ lim = t - 1 /* longword */; for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) { q = *p; @@ -1366,7 +1485,7 @@ ptr_t bottom; ptr_t top; ptr_t cold_gc_frame; { - if (GC_all_interior_pointers) { + if (!NEED_FIXUP_POINTER && GC_all_interior_pointers) { # define EAGER_BYTES 1024 /* Push the hot end of the stack eagerly, so that register values */ /* saved inside GC frames are marked before they disappear. */ @@ -1375,6 +1494,7 @@ ptr_t cold_gc_frame; GC_push_all_stack(bottom, top); return; } + GC_ASSERT(bottom <= cold_gc_frame && cold_gc_frame <= top); # ifdef STACK_GROWS_DOWN GC_push_all(cold_gc_frame - sizeof(ptr_t), top); GC_push_all_eager(bottom, cold_gc_frame); @@ -1395,7 +1515,7 @@ void GC_push_all_stack(bottom, top) ptr_t bottom; ptr_t top; { - if (GC_all_interior_pointers) { + if (!NEED_FIXUP_POINTER && GC_all_interior_pointers) { GC_push_all(bottom, top); } else { GC_push_all_eager(bottom, top); diff --git a/boehm-gc/mark_rts.c b/boehm-gc/mark_rts.c index f663dcd55c3..55eb5d54339 100644 --- a/boehm-gc/mark_rts.c +++ b/boehm-gc/mark_rts.c @@ -275,33 +275,72 @@ void GC_clear_roots GC_PROTO((void)) } /* Internal use only; lock held. */ +static void GC_remove_root_at_pos(i) +int i; +{ + GC_root_size -= (GC_static_roots[i].r_end - GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; +} + +#if !defined(MSWIN32) && !defined(MSWINCE) +static void GC_rebuild_root_index() +{ + register int i; + + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); +} +#endif + +/* Internal use only; lock held. */ void GC_remove_tmp_roots() { register int i; for (i = 0; i < n_root_sets; ) { if (GC_static_roots[i].r_tmp) { - GC_root_size -= - (GC_static_roots[i].r_end - GC_static_roots[i].r_start); - GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; - GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; - GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; - n_root_sets--; + GC_remove_root_at_pos(i); } else { i++; - } } -# if !defined(MSWIN32) && !defined(MSWINCE) - { - register int i; - - for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; - for (i = 0; i < n_root_sets; i++) - add_roots_to_index(GC_static_roots + i); } -# endif + #if !defined(MSWIN32) && !defined(MSWINCE) + GC_rebuild_root_index(); + #endif +} + +#if !defined(MSWIN32) && !defined(MSWINCE) +void GC_remove_roots(b, e) +char * b; char * e; +{ + DCL_LOCK_STATE; + DISABLE_SIGNALS(); + LOCK(); + GC_remove_roots_inner(b, e); + UNLOCK(); + ENABLE_SIGNALS(); +} + +/* Should only be called when the lock is held */ +void GC_remove_roots_inner(b,e) +char * b; char * e; +{ + int i; + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_start >= (ptr_t)b && GC_static_roots[i].r_end <= (ptr_t)e) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } + GC_rebuild_root_index(); } +#endif /* !defined(MSWIN32) && !defined(MSWINCE) */ #if defined(MSWIN32) || defined(_WIN32_WCE_EMULATION) /* Workaround for the OS mapping and unmapping behind our back: */ @@ -573,8 +612,11 @@ ptr_t cold_gc_frame; /* Mark thread local free lists, even if their mark */ /* descriptor excludes the link field. */ + /* If the world is not stopped, this is unsafe. It is */ + /* also unnecessary, since we will do this again with the */ + /* world stopped. */ # ifdef THREAD_LOCAL_ALLOC - GC_mark_thread_local_free_lists(); + if (GC_world_stopped) GC_mark_thread_local_free_lists(); # endif /* diff --git a/boehm-gc/mips_sgi_mach_dep.S b/boehm-gc/mips_sgi_mach_dep.s index bae9b5ebfe4..56390280a71 100644 --- a/boehm-gc/mips_sgi_mach_dep.S +++ b/boehm-gc/mips_sgi_mach_dep.s @@ -1,5 +1,10 @@ #include <sys/regdef.h> #include <sys/asm.h> +/* This file must be preprocessed. But the SGI assembler always does */ +/* that. Furthermore, a generic preprocessor won't do, since some of */ +/* the SGI-supplied include files rely on behavior of the MIPS */ +/* assembler. Hence we treat and name this file as though it required */ +/* no preprocessing. */ # define call_push(x) move $4,x; jal GC_push_one diff --git a/boehm-gc/misc.c b/boehm-gc/misc.c index 8f83ff3ab53..814fa41ab24 100644 --- a/boehm-gc/misc.c +++ b/boehm-gc/misc.c @@ -46,8 +46,10 @@ # ifdef GC_SOLARIS_THREADS mutex_t GC_allocate_ml; /* Implicitly initialized. */ # else -# ifdef GC_WIN32_THREADS -# if !defined(GC_NOT_DLL) && (defined(_DLL) || defined(GC_DLL)) +# if defined(GC_WIN32_THREADS) +# if defined(GC_PTHREADS) + pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +# elif defined(GC_DLL) __declspec(dllexport) CRITICAL_SECTION GC_allocate_ml; # else CRITICAL_SECTION GC_allocate_ml; @@ -75,8 +77,8 @@ #undef STACKBASE #endif -/* Dont unnecessarily call GC_register_main_static_data() in case */ -/* dyn_load.c isn't linked in. */ +/* Dont unnecessarily call GC_register_main_static_data() in case */ +/* dyn_load.c isn't linked in. */ #ifdef DYNAMIC_LOADING # define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data() #else @@ -90,6 +92,7 @@ GC_bool GC_debugging_started = FALSE; /* defined here so we don't have to load debug_malloc.o */ void (*GC_check_heap) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0; +void (*GC_print_all_smashed) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0; void (*GC_start_call_back) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0; @@ -109,6 +112,10 @@ GC_bool GC_print_stats = 0; GC_bool GC_print_back_height = 0; +#ifndef NO_DEBUGGING + GC_bool GC_dump_regularly = 0; /* Generate regular debugging dumps. */ +#endif + #ifdef FIND_LEAK int GC_find_leak = 1; #else @@ -137,6 +144,13 @@ GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn; extern signed_word GC_mem_found; +void * GC_project2(arg1, arg2) +void *arg1; +void *arg2; +{ + return arg2; +} + # ifdef MERGE_SIZES /* Set things up so that GC_size_map[i] >= words(i), */ /* but not too much bigger */ @@ -455,7 +469,7 @@ void GC_init() DISABLE_SIGNALS(); -#ifdef MSWIN32 +#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) if (!GC_is_initialized) InitializeCriticalSection(&GC_allocate_ml); #endif /* MSWIN32 */ @@ -473,6 +487,15 @@ void GC_init() GC_init_parallel(); } # endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ + +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + { + /* This must be called WITHOUT the allocation lock held + and before any threads are created */ + extern void GC_init_dyld(); + GC_init_dyld(); + } +# endif } #if defined(MSWIN32) || defined(MSWINCE) @@ -485,6 +508,22 @@ void GC_init() extern void GC_setpagesize(); + +#ifdef MSWIN32 +extern GC_bool GC_no_win32_dlls; +#else +# define GC_no_win32_dlls FALSE +#endif + +void GC_exit_check GC_PROTO((void)) +{ + GC_gcollect(); +} + +#ifdef SEARCH_FOR_DATA_START + extern void GC_init_linux_data_start GC_PROTO((void)); +#endif + #ifdef UNIX_LIKE extern void GC_set_and_save_fault_handler GC_PROTO((void (*handler)(int))); @@ -495,12 +534,23 @@ int sig; GC_err_printf1("Caught signal %d: looping in handler\n", sig); for(;;); } -#endif -#ifdef MSWIN32 -extern GC_bool GC_no_win32_dlls; -#else -# define GC_no_win32_dlls FALSE +static GC_bool installed_looping_handler = FALSE; + +void maybe_install_looping_handler() +{ + /* Install looping handler before the write fault handler, so we */ + /* handle write faults correctly. */ + if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) { + GC_set_and_save_fault_handler(looping_handler); + installed_looping_handler = TRUE; + } +} + +#else /* !UNIX_LIKE */ + +# define maybe_install_looping_handler() + #endif void GC_init_inner() @@ -515,14 +565,21 @@ void GC_init_inner() GC_print_stats = 1; # endif # if defined(MSWIN32) || defined(MSWINCE) - InitializeCriticalSection(&GC_write_cs); + InitializeCriticalSection(&GC_write_cs); # endif - if (0 != GETENV("GC_PRINT_STATS")) { GC_print_stats = 1; } +# ifndef NO_DEBUGGING + if (0 != GETENV("GC_DUMP_REGULARLY")) { + GC_dump_regularly = 1; + } +# endif if (0 != GETENV("GC_FIND_LEAK")) { GC_find_leak = 1; +# ifdef __STDC__ + atexit(GC_exit_check); +# endif } if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { GC_all_interior_pointers = 1; @@ -560,11 +617,7 @@ void GC_init_inner() } } } -# ifdef UNIX_LIKE - if (0 != GETENV("GC_LOOP_ON_ABORT")) { - GC_set_and_save_fault_handler(looping_handler); - } -# endif + maybe_install_looping_handler(); /* Adjust normal object descriptor for extra allocation. */ if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) { GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH); @@ -599,11 +652,21 @@ void GC_init_inner() # if defined(LINUX) && defined(IA64) GC_register_stackbottom = GC_get_register_stack_base(); # endif + } else { +# if defined(LINUX) && defined(IA64) + if (GC_register_stackbottom == 0) { + WARN("GC_register_stackbottom should be set with GC_stackbottom", 0); + /* The following is likely to fail, since we rely on */ + /* alignment properties that may not hold with a user set */ + /* GC_stackbottom. */ + GC_register_stackbottom = GC_get_register_stack_base(); + } +# endif } # endif - GC_ASSERT(sizeof (ptr_t) == sizeof(word)); - GC_ASSERT(sizeof (signed_word) == sizeof(word)); - GC_ASSERT(sizeof (struct hblk) == HBLKSIZE); + GC_STATIC_ASSERT(sizeof (ptr_t) == sizeof(word)); + GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word)); + GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE); # ifndef THREADS # if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) ABORT( @@ -642,6 +705,18 @@ void GC_init_inner() initial_heap_sz = divHBLKSZ(initial_heap_sz); } } + { + char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE"); + if (sz_str != NULL) { + word max_heap_sz = (word)atol(sz_str); + if (max_heap_sz < initial_heap_sz * HBLKSIZE) { + WARN("Bad maximum heap size %s - ignoring it.\n", + sz_str); + } + if (0 == GC_max_retries) GC_max_retries = 2; + GC_set_max_heap_size(max_heap_sz); + } + } if (!GC_expand_hp_inner(initial_heap_sz)) { GC_err_printf0("Can't start up: not enough memory\n"); EXIT(); @@ -677,6 +752,7 @@ void GC_init_inner() GC_incremental = TRUE; } # endif /* !SMALL_CONFIG */ + COND_DUMP; /* Get black list set up and/or incrmental GC started */ if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner(); GC_is_initialized = TRUE; @@ -712,8 +788,9 @@ void GC_enable_incremental GC_PROTO(()) if (GC_incremental) goto out; GC_setpagesize(); if (GC_no_win32_dlls) goto out; -# ifndef GC_SOLARIS_THREADS - GC_dirty_init(); +# ifndef GC_SOLARIS_THREADS + maybe_install_looping_handler(); /* Before write fault handler! */ + GC_dirty_init(); # endif if (!GC_is_initialized) { GC_init_inner(); @@ -932,6 +1009,17 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; return(result); } +# if defined(__STDC__) || defined(__cplusplus) + GC_word GC_set_free_space_divisor (GC_word value) +# else + GC_word GC_set_free_space_divisor (value) + GC_word value; +# endif +{ + GC_word old = GC_free_space_divisor; + GC_free_space_divisor = value; + return old; +} #ifndef PCR void GC_abort(msg) @@ -958,122 +1046,18 @@ GC_CONST char * msg; } #endif -#ifdef NEED_CALLINFO - -#ifdef HAVE_BUILTIN_BACKTRACE -# include <execinfo.h> -# ifdef LINUX -# include <unistd.h> -# endif -#endif - -void GC_print_callers (info) -struct callinfo info[NFRAMES]; -{ - register int i; - -# if NFRAMES == 1 - GC_err_printf0("\tCaller at allocation:\n"); -# else - GC_err_printf0("\tCall chain at allocation:\n"); -# endif - for (i = 0; i < NFRAMES; i++) { - if (info[i].ci_pc == 0) break; -# if NARGS > 0 - { - int j; - - GC_err_printf0("\t\targs: "); - for (j = 0; j < NARGS; j++) { - if (j != 0) GC_err_printf0(", "); - GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), - ~(info[i].ci_arg[j])); - } - GC_err_printf0("\n"); - } -# endif -# if defined(HAVE_BUILTIN_BACKTRACE) && !defined(REDIRECT_MALLOC) - /* Unfortunately backtrace_symbols calls malloc, which makes */ - /* it dangersous if that has been redirected. */ - { - char **sym_name = - backtrace_symbols((void **)(&(info[i].ci_pc)), 1); - char *name = sym_name[0]; - GC_bool found_it = (strchr(name, '(') != 0); - FILE *pipe; -# ifdef LINUX - if (!found_it) { -# define EXE_SZ 100 - static char exe_name[EXE_SZ]; -# define CMD_SZ 200 - char cmd_buf[CMD_SZ]; -# define RESULT_SZ 200 - static char result_buf[RESULT_SZ]; - size_t result_len; - static GC_bool found_exe_name = FALSE; - static GC_bool will_fail = FALSE; - int ret_code; - /* Unfortunately, this is the common case for the */ - /* main executable. */ - /* Try to get it via a hairy and expensive scheme. */ - /* First we get the name of the executable: */ - if (will_fail) goto out; - if (!found_exe_name) { - ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); - if (ret_code < 0 || ret_code >= EXE_SZ || exe_name[0] != '/') { - will_fail = TRUE; /* Dont try again. */ - goto out; - } - exe_name[ret_code] = '\0'; - found_exe_name = TRUE; - } - /* Then we use popen to start addr2line -e <exe> <addr> */ - /* There are faster ways to do this, but hopefully this */ - /* isn't time critical. */ - sprintf(cmd_buf, "/usr/bin/addr2line -e %s 0x%lx", exe_name, - (unsigned long)info[i].ci_pc); - pipe = popen(cmd_buf, "r"); - if (pipe < 0 || fgets(result_buf, RESULT_SZ, pipe) == 0) { - will_fail = TRUE; - goto out; - } - result_len = strlen(result_buf); - if (result_buf[result_len - 1] == '\n') --result_len; - if (result_buf[0] == '?' - || result_buf[result_len-2] == ':' - && result_buf[result_len-1] == '0') - goto out; - if (result_len < RESULT_SZ - 25) { - /* Add in hex address */ - sprintf(result_buf + result_len, " [0x%lx]", - (unsigned long)info[i].ci_pc); - } - name = result_buf; - pclose(pipe); - out: - } -# endif - GC_err_printf1("\t\t%s\n", name); - free(sym_name); - } -# else - GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc); -# endif - } -} - -#endif /* SAVE_CALL_CHAIN */ - -/* Needed by SRC_M3, gcj, and should perhaps be the official interface */ -/* to GC_dont_gc. */ void GC_enable() { + LOCK(); GC_dont_gc--; + UNLOCK(); } void GC_disable() { + LOCK(); GC_dont_gc++; + UNLOCK(); } #if !defined(NO_DEBUGGING) @@ -1088,6 +1072,8 @@ void GC_dump() GC_print_hblkfreelist(); GC_printf0("\n***Blocks in use:\n"); GC_print_block_list(); + GC_printf0("\n***Finalization statistics:\n"); + GC_print_finalization_stats(); } #endif /* NO_DEBUGGING */ diff --git a/boehm-gc/missing b/boehm-gc/missing deleted file mode 100755 index dd583709f53..00000000000 --- a/boehm-gc/missing +++ /dev/null @@ -1,336 +0,0 @@ -#! /bin/sh -# Common stub for a few missing GNU programs while installing. -# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. -# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 -fi - -run=: - -# In the cases where this matters, `missing' is being run in the -# srcdir already. -if test -f configure.ac; then - configure_ac=configure.ac -else - configure_ac=configure.in -fi - -case "$1" in ---run) - # Try to run requested program, and just exit if it succeeds. - run= - shift - "$@" && exit 0 - ;; -esac - -# If it does not exist, or fails to run (possibly an outdated version), -# try to emulate it. -case "$1" in - - -h|--h|--he|--hel|--help) - echo "\ -$0 [OPTION]... PROGRAM [ARGUMENT]... - -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an -error status if there is no known handling for PROGRAM. - -Options: - -h, --help display this help and exit - -v, --version output version information and exit - --run try to run the given command, and emulate it if it fails - -Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c - help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c - makeinfo touch the output file - tar try tar, gnutar, gtar, then tar without non-portable flags - yacc create \`y.tab.[ch]', if possible, from existing .[ch]" - ;; - - -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing 0.4 - GNU automake" - ;; - - -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 - ;; - - aclocal*) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from - any GNU archive site." - touch aclocal.m4 - ;; - - autoconf) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU - archive site." - touch configure - ;; - - autoheader) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them - from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` - test -z "$files" && files="config.h" - touch_files= - for f in $files; do - case "$f" in - *:*) touch_files="$touch_files "`echo "$f" | - sed -e 's/^[^:]*://' -e 's/:.*//'`;; - *) touch_files="$touch_files $f.in";; - esac - done - touch $touch_files - ;; - - automake*) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. - Grab them from any GNU archive site." - find . -type f -name Makefile.am -print | - sed 's/\.am$/.in/' | - while read f; do touch "$f"; done - ;; - - autom4te) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is needed, and you do not seem to have it handy on your - system. You might have modified some files without having the - proper tools for further handling them. - You can get \`$1Help2man' as part of \`Autoconf' from any GNU - archive site." - - file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` - test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo "#! /bin/sh" - echo "# Created by GNU Automake missing as a replacement of" - echo "# $ $@" - echo "exit 0" - chmod +x $file - exit 1 - fi - ;; - - bison|yacc) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package - in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." - rm -f y.tab.c y.tab.h - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.y) - SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.c - fi - SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.h - fi - ;; - esac - fi - if [ ! -f y.tab.h ]; then - echo >y.tab.h - fi - if [ ! -f y.tab.c ]; then - echo 'main() { return 0; }' >y.tab.c - fi - ;; - - lex|flex) - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package - in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." - rm -f lex.yy.c - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.l) - SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" lex.yy.c - fi - ;; - esac - fi - if [ ! -f lex.yy.c ]; then - echo 'main() { return 0; }' >lex.yy.c - fi - ;; - - help2man) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." - - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` - fi - if [ -f "$file" ]; then - touch $file - else - test -z "$file" || exec >$file - echo ".ab help2man is required to generate this page" - exit 1 - fi - ;; - - makeinfo) - if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then - # We have makeinfo, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is missing on your system. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file - indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` - file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` - fi - touch $file - ;; - - tar) - shift - if test -n "$run"; then - echo 1>&2 "ERROR: \`tar' requires --run" - exit 1 - fi - - # We have already tried tar in the generic part. - # Look for gnutar/gtar before invocation to avoid ugly error - # messages. - if (gnutar --version > /dev/null 2>&1); then - gnutar ${1+"$@"} && exit 0 - fi - if (gtar --version > /dev/null 2>&1); then - gtar ${1+"$@"} && exit 0 - fi - firstarg="$1" - if shift; then - case "$firstarg" in - *o*) - firstarg=`echo "$firstarg" | sed s/o//` - tar "$firstarg" ${1+"$@"} && exit 0 - ;; - esac - case "$firstarg" in - *h*) - firstarg=`echo "$firstarg" | sed s/h//` - tar "$firstarg" ${1+"$@"} && exit 0 - ;; - esac - fi - - echo 1>&2 "\ -WARNING: I can't seem to be able to run \`tar' with the given arguments. - You may want to install GNU tar or Free paxutils, or check the - command line arguments." - exit 1 - ;; - - *) - echo 1>&2 "\ -WARNING: \`$1' is needed, and you do not seem to have it handy on your - system. You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, - it often tells you about the needed prerequirements for installing - this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." - exit 1 - ;; -esac - -exit 0 diff --git a/boehm-gc/os_dep.c b/boehm-gc/os_dep.c index 82386ec4fba..fecda89db77 100644 --- a/boehm-gc/os_dep.c +++ b/boehm-gc/os_dep.c @@ -80,6 +80,13 @@ # define NEED_FIND_LIMIT # endif +#if defined(FREEBSD) && defined(I386) +# include <machine/trap.h> +# if !defined(PCR) +# define NEED_FIND_LIMIT +# endif +#endif + #ifdef NEED_FIND_LIMIT # include <setjmp.h> #endif @@ -129,6 +136,11 @@ # define jmp_buf sigjmp_buf #endif +#ifdef DARWIN +/* for get_etext and friends */ +#include <mach-o/getsect.h> +#endif + #ifdef DJGPP /* Apparently necessary for djgpp 2.01. May cause problems with */ /* other versions. */ @@ -147,6 +159,156 @@ # define OPT_PROT_EXEC 0 #endif +#if defined(LINUX) && \ + (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG)) + +/* We need to parse /proc/self/maps, either to find dynamic libraries, */ +/* and/or to find the register backing store base (IA64). Do it once */ +/* here. */ + +#define READ read + +/* Repeatedly perform a read call until the buffer is filled or */ +/* we encounter EOF. */ +ssize_t GC_repeat_read(int fd, char *buf, size_t count) +{ + ssize_t num_read = 0; + ssize_t result; + + while (num_read < count) { + result = READ(fd, buf + num_read, count - num_read); + if (result < 0) return result; + if (result == 0) break; + num_read += result; + } + return num_read; +} + +/* + * Apply fn to a buffer containing the contents of /proc/self/maps. + * Return the result of fn or, if we failed, 0. + */ + +word GC_apply_to_maps(word (*fn)(char *)) +{ + int f; + int result; + int maps_size; + char maps_temp[32768]; + char *maps_buf; + + /* Read /proc/self/maps */ + /* Note that we may not allocate, and thus can't use stdio. */ + f = open("/proc/self/maps", O_RDONLY); + if (-1 == f) return 0; + /* stat() doesn't work for /proc/self/maps, so we have to + read it to find out how large it is... */ + maps_size = 0; + do { + result = GC_repeat_read(f, maps_temp, sizeof(maps_temp)); + if (result <= 0) return 0; + maps_size += result; + } while (result == sizeof(maps_temp)); + + if (maps_size > sizeof(maps_temp)) { + /* If larger than our buffer, close and re-read it. */ + close(f); + f = open("/proc/self/maps", O_RDONLY); + if (-1 == f) return 0; + maps_buf = alloca(maps_size); + if (NULL == maps_buf) return 0; + result = GC_repeat_read(f, maps_buf, maps_size); + if (result <= 0) return 0; + } else { + /* Otherwise use the fixed size buffer */ + maps_buf = maps_temp; + } + + close(f); + maps_buf[result] = '\0'; + + /* Apply fn to result. */ + return fn(maps_buf); +} + +#endif /* Need GC_apply_to_maps */ + +#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64)) +// +// GC_parse_map_entry parses an entry from /proc/self/maps so we can +// locate all writable data segments that belong to shared libraries. +// The format of one of these entries and the fields we care about +// is as follows: +// XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n +// ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ +// start end prot maj_dev +// 0 9 18 32 +// +// For 64 bit ABIs: +// 0 17 34 56 +// +// The parser is called with a pointer to the entry and the return value +// is either NULL or is advanced to the next entry(the byte after the +// trailing '\n'.) +// +#if CPP_WORDSZ == 32 +# define OFFSET_MAP_START 0 +# define OFFSET_MAP_END 9 +# define OFFSET_MAP_PROT 18 +# define OFFSET_MAP_MAJDEV 32 +# define ADDR_WIDTH 8 +#endif + +#if CPP_WORDSZ == 64 +# define OFFSET_MAP_START 0 +# define OFFSET_MAP_END 17 +# define OFFSET_MAP_PROT 34 +# define OFFSET_MAP_MAJDEV 56 +# define ADDR_WIDTH 16 +#endif + +/* + * Assign various fields of the first line in buf_ptr to *start, *end, + * *prot_buf and *maj_dev. Only *prot_buf may be set for unwritable maps. + */ +char *GC_parse_map_entry(char *buf_ptr, word *start, word *end, + char *prot_buf, unsigned int *maj_dev) +{ + int i; + char *tok; + + if (buf_ptr == NULL || *buf_ptr == '\0') { + return NULL; + } + + memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4); + /* do the protections first. */ + prot_buf[4] = '\0'; + + if (prot_buf[1] == 'w') {/* we can skip all of this if it's not writable. */ + + tok = buf_ptr; + buf_ptr[OFFSET_MAP_START+ADDR_WIDTH] = '\0'; + *start = strtoul(tok, NULL, 16); + + tok = buf_ptr+OFFSET_MAP_END; + buf_ptr[OFFSET_MAP_END+ADDR_WIDTH] = '\0'; + *end = strtoul(tok, NULL, 16); + + buf_ptr += OFFSET_MAP_MAJDEV; + tok = buf_ptr; + while (*buf_ptr != ':') buf_ptr++; + *buf_ptr++ = '\0'; + *maj_dev = strtoul(tok, NULL, 16); + } + + while (*buf_ptr && *buf_ptr++ != '\n'); + + return buf_ptr; +} + +#endif /* Need to parse /proc/self/maps. */ + #if defined(SEARCH_FOR_DATA_START) /* The I386 case can be handled without a search. The Alpha case */ /* used to be handled differently as well, but the rules changed */ @@ -154,6 +316,11 @@ /* cover all versions. */ # ifdef LINUX + /* Some Linux distributions arrange to define __data_start. Some */ + /* define data_start as a weak symbol. The latter is technically */ + /* broken, since the user program may define data_start, in which */ + /* case we lose. Nonetheless, we try both, prefering __data_start. */ + /* We assume gcc-compatible pragmas. */ # pragma weak __data_start extern int __data_start[]; # pragma weak data_start @@ -169,16 +336,16 @@ # ifdef LINUX /* Try the easy approaches first: */ - if (__data_start != 0) { - GC_data_start = (ptr_t)__data_start; + if ((ptr_t)__data_start != 0) { + GC_data_start = (ptr_t)(__data_start); return; } - if (data_start != 0) { - GC_data_start = (ptr_t)data_start; + if ((ptr_t)data_start != 0) { + GC_data_start = (ptr_t)(data_start); return; } # endif /* LINUX */ - GC_data_start = GC_find_limit((ptr_t)_end, FALSE); + GC_data_start = GC_find_limit((ptr_t)(_end), FALSE); } #endif @@ -617,7 +784,8 @@ ptr_t GC_get_stack_base() } /* Return the first nonaddressible location > p (up) or */ - /* the smallest location q s.t. [q,p] is addressible (!up). */ + /* the smallest location q s.t. [q,p) is addressable (!up). */ + /* We assume that p (up) or p-1 (!up) is addressable. */ ptr_t GC_find_limit(p, up) ptr_t p; GC_bool up; @@ -650,18 +818,18 @@ ptr_t GC_get_stack_base() } # endif -# if defined(ECOS) || defined(NOSYS) -ptr_t GC_get_stack_base() -{ - return STACKBOTTOM; -} - -#else +#if defined(ECOS) || defined(NOSYS) + ptr_t GC_get_stack_base() + { + return STACKBOTTOM; + } +#endif #ifdef LINUX_STACKBOTTOM #include <sys/types.h> #include <sys/stat.h> +#include <ctype.h> # define STAT_SKIP 27 /* Number of fields preceding startstack */ /* field in /proc/self/stat */ @@ -670,6 +838,33 @@ ptr_t GC_get_stack_base() extern ptr_t __libc_stack_end; # ifdef IA64 + /* Try to read the backing store base from /proc/self/maps. */ + /* We look for the writable mapping with a 0 major device, */ + /* which is as close to our frame as possible, but below it.*/ + static word backing_store_base_from_maps(char *maps) + { + char prot_buf[5]; + char *buf_ptr = maps; + word start, end; + unsigned int maj_dev; + word current_best = 0; + word dummy; + + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, prot_buf, &maj_dev); + if (buf_ptr == NULL) return current_best; + if (prot_buf[1] == 'w' && maj_dev == 0) { + if (end < (word)(&dummy) && start > current_best) current_best = start; + } + } + return current_best; + } + + static word backing_store_base_from_proc(void) + { + return GC_apply_to_maps(backing_store_base_from_maps); + } + # pragma weak __libc_ia64_register_backing_store_base extern ptr_t __libc_ia64_register_backing_store_base; @@ -679,13 +874,19 @@ ptr_t GC_get_stack_base() && 0 != __libc_ia64_register_backing_store_base) { /* Glibc 2.2.4 has a bug such that for dynamically linked */ /* executables __libc_ia64_register_backing_store_base is */ - /* defined but ininitialized during constructor calls. */ + /* defined but uninitialized during constructor calls. */ /* Hence we check for both nonzero address and value. */ return __libc_ia64_register_backing_store_base; } else { - word result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT; - result += BACKING_STORE_ALIGNMENT - 1; - result &= ~(BACKING_STORE_ALIGNMENT - 1); + word result = backing_store_base_from_proc(); + if (0 == result) { + /* Use dumb heuristics. Works only for default configuration. */ + result = (word)GC_stackbottom - BACKING_STORE_DISPLACEMENT; + result += BACKING_STORE_ALIGNMENT - 1; + result &= ~(BACKING_STORE_ALIGNMENT - 1); + /* Verify that it's at least readable. If not, we goofed. */ + GC_noop1(*(word *)result); + } return (ptr_t)result; } } @@ -697,11 +898,8 @@ ptr_t GC_get_stack_base() /* using direct I/O system calls in order to avoid calling malloc */ /* in case REDIRECT_MALLOC is defined. */ # define STAT_BUF_SIZE 4096 -# if defined(GC_USE_LD_WRAP) -# define STAT_READ __real_read -# else -# define STAT_READ read -# endif +# define STAT_READ read + /* Should probably call the real read, if read is wrapped. */ char stat_buf[STAT_BUF_SIZE]; int f; char c; @@ -710,7 +908,16 @@ ptr_t GC_get_stack_base() /* First try the easy way. This should work for glibc 2.2 */ if (0 != &__libc_stack_end) { - return __libc_stack_end; +# ifdef IA64 + /* Some versions of glibc set the address 16 bytes too */ + /* low while the initialization code is running. */ + if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) { + return __libc_stack_end + 0x10; + } /* Otherwise it's not safe to add 16 bytes and we fall */ + /* back to using /proc. */ +# else + return __libc_stack_end; +# endif } f = open("/proc/self/stat", O_RDONLY); if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) { @@ -764,8 +971,11 @@ ptr_t GC_get_stack_base() ptr_t GC_get_stack_base() { +# if defined(HEURISTIC1) || defined(HEURISTIC2) || \ + defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM) word dummy; ptr_t result; +# endif # define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) @@ -814,7 +1024,6 @@ ptr_t GC_get_stack_base() return(result); # endif /* STACKBOTTOM */ } -# endif /* NOSYS ECOS */ # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */ @@ -924,15 +1133,14 @@ void GC_register_data_segments() /* Unfortunately, we have to handle win32s very differently from NT, */ /* Since VirtualQuery has very different semantics. In particular, */ /* under win32s a VirtualQuery call on an unmapped page returns an */ - /* invalid result. Under GC_register_data_segments is a noop and */ + /* invalid result. Under NT, GC_register_data_segments is a noop and */ /* all real work is done by GC_register_dynamic_libraries. Under */ /* win32s, we cannot find the data segments associated with dll's. */ - /* We rgister the main data segment here. */ -# ifdef __GCC__ - GC_bool GC_no_win32_dlls = TRUE; /* GCC can't do SEH, so we can't use VirtualQuery */ -# else + /* We register the main data segment here. */ GC_bool GC_no_win32_dlls = FALSE; -# endif + /* This used to be set for gcc, to avoid dealing with */ + /* the structured exception handling issues. But we now have */ + /* assembly code to do that right. */ void GC_init_win32() { @@ -964,36 +1172,102 @@ void GC_register_data_segments() return(p); } # endif + +# ifndef REDIRECT_MALLOC + /* We maintain a linked list of AllocationBase values that we know */ + /* correspond to malloc heap sections. Currently this is only called */ + /* during a GC. But there is some hope that for long running */ + /* programs we will eventually see most heap sections. */ + + /* In the long run, it would be more reliable to occasionally walk */ + /* the malloc heap with HeapWalk on the default heap. But that */ + /* apparently works only for NT-based Windows. */ + + /* In the long run, a better data structure would also be nice ... */ + struct GC_malloc_heap_list { + void * allocation_base; + struct GC_malloc_heap_list *next; + } *GC_malloc_heap_l = 0; + + /* Is p the base of one of the malloc heap sections we already know */ + /* about? */ + GC_bool GC_is_malloc_heap_base(ptr_t p) + { + struct GC_malloc_heap_list *q = GC_malloc_heap_l; + + while (0 != q) { + if (q -> allocation_base == p) return TRUE; + q = q -> next; + } + return FALSE; + } + + void *GC_get_allocation_base(void *p) + { + MEMORY_BASIC_INFORMATION buf; + DWORD result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + return buf.AllocationBase; + } + + size_t GC_max_root_size = 100000; /* Appr. largest root size. */ + + void GC_add_current_malloc_heap() + { + struct GC_malloc_heap_list *new_l = + malloc(sizeof(struct GC_malloc_heap_list)); + void * candidate = GC_get_allocation_base(new_l); + + if (new_l == 0) return; + if (GC_is_malloc_heap_base(candidate)) { + /* Try a little harder to find malloc heap. */ + size_t req_size = 10000; + do { + void *p = malloc(req_size); + if (0 == p) { free(new_l); return; } + candidate = GC_get_allocation_base(p); + free(p); + req_size *= 2; + } while (GC_is_malloc_heap_base(candidate) + && req_size < GC_max_root_size/10 && req_size < 500000); + if (GC_is_malloc_heap_base(candidate)) { + free(new_l); return; + } + } +# ifdef CONDPRINT + if (GC_print_stats) + GC_printf1("Found new system malloc AllocationBase at 0x%lx\n", + candidate); +# endif + new_l -> allocation_base = candidate; + new_l -> next = GC_malloc_heap_l; + GC_malloc_heap_l = new_l; + } +# endif /* REDIRECT_MALLOC */ /* Is p the start of either the malloc heap, or of one of our */ /* heap sections? */ GC_bool GC_is_heap_base (ptr_t p) { - register unsigned i; + unsigned i; # ifndef REDIRECT_MALLOC - static ptr_t malloc_heap_pointer = 0; + static word last_gc_no = -1; - if (0 == malloc_heap_pointer) { - MEMORY_BASIC_INFORMATION buf; - void *pTemp = malloc( 1 ); - register DWORD result = VirtualQuery(pTemp, &buf, sizeof(buf)); - - free( pTemp ); - - - if (result != sizeof(buf)) { - ABORT("Weird VirtualQuery result"); - } - malloc_heap_pointer = (ptr_t)(buf.AllocationBase); + if (last_gc_no != GC_gc_no) { + GC_add_current_malloc_heap(); + last_gc_no = GC_gc_no; } - if (p == malloc_heap_pointer) return(TRUE); + if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size; + if (GC_is_malloc_heap_base(p)) return TRUE; # endif for (i = 0; i < GC_n_heap_bases; i++) { - if (GC_heap_bases[i] == p) return(TRUE); + if (GC_heap_bases[i] == p) return TRUE; } - return(FALSE); + return FALSE ; } # ifdef MSWIN32 @@ -1043,7 +1317,7 @@ void GC_register_data_segments() # if (defined(SVR4) || defined(AUX) || defined(DGUX) \ || (defined(LINUX) && defined(SPARC))) && !defined(PCR) -char * GC_SysVGetDataStart(max_page_size, etext_addr) +ptr_t GC_SysVGetDataStart(max_page_size, etext_addr) int max_page_size; int * etext_addr; { @@ -1069,12 +1343,45 @@ int * etext_addr; /* string constants in the text segment, but after etext. */ /* Use plan B. Note that we now know there is a gap between */ /* text and data segments, so plan A bought us something. */ - result = (char *)GC_find_limit((ptr_t)(DATAEND) - MIN_PAGE_SIZE, FALSE); + result = (char *)GC_find_limit((ptr_t)(DATAEND), FALSE); } - return((char *)result); + return((ptr_t)result); } # endif +# if defined(FREEBSD) && defined(I386) && !defined(PCR) +/* Its unclear whether this should be identical to the above, or */ +/* whether it should apply to non-X86 architectures. */ +/* For now we don't assume that there is always an empty page after */ +/* etext. But in some cases there actually seems to be slightly more. */ +/* This also deals with holes between read-only data and writable data. */ +ptr_t GC_FreeBSDGetDataStart(max_page_size, etext_addr) +int max_page_size; +int * etext_addr; +{ + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(sizeof(word) - 1); + /* etext rounded to word boundary */ + VOLATILE word next_page = (text_end + (word)max_page_size - 1) + & ~((word)max_page_size - 1); + VOLATILE ptr_t result = (ptr_t)text_end; + GC_setup_temporary_fault_handler(); + if (setjmp(GC_jmp_buf) == 0) { + /* Try reading at the address. */ + /* This should happen before there is another thread. */ + for (; next_page < (word)(DATAEND); next_page += (word)max_page_size) + *(VOLATILE char *)next_page; + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* As above, we go to plan B */ + result = GC_find_limit((ptr_t)(DATAEND), FALSE); + } + return(result); +} + +# endif + #ifdef AMIGA @@ -1086,8 +1393,7 @@ int * etext_addr; void GC_register_data_segments() { -# if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \ - && !defined(MACOSX) +# if !defined(PCR) && !defined(SRC_M3) && !defined(MACOS) # if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS) /* As of Solaris 2.3, the Solaris threads implementation */ /* allocates the data structure for the initial thread with */ @@ -1104,9 +1410,6 @@ void GC_register_data_segments() # endif # endif # endif -# if !defined(PCR) && (defined(NEXT) || defined(MACOSX)) - GC_add_roots_inner(DATASTART, (char *) get_end(), FALSE); -# endif # if defined(MACOS) { # if defined(THINK_C) @@ -1216,18 +1519,28 @@ word bytes; ptr_t GC_unix_get_mem(bytes) word bytes; { - static GC_bool initialized = FALSE; - static int fd; void *result; static ptr_t last_addr = HEAP_START; - if (!initialized) { - fd = open("/dev/zero", O_RDONLY); - initialized = TRUE; - } +# ifndef USE_MMAP_ANON + static GC_bool initialized = FALSE; + static int fd; + + if (!initialized) { + fd = open("/dev/zero", O_RDONLY); + fcntl(fd, F_SETFD, FD_CLOEXEC); + initialized = TRUE; + } +# endif + if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg"); - result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, - GC_MMAP_FLAGS, fd, 0/* offset */); +# ifdef USE_MMAP_ANON + result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, + GC_MMAP_FLAGS | MAP_ANON, -1, 0/* offset */); +# else + result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, + GC_MMAP_FLAGS, fd, 0/* offset */); +# endif if (result == MAP_FAILED) return(0); last_addr = (ptr_t)result + bytes + GC_page_size - 1; last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1)); @@ -1322,7 +1635,15 @@ word bytes; result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE); result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1)); } else { - result = (ptr_t) VirtualAlloc(NULL, bytes, + /* VirtualProtect only works on regions returned by a */ + /* single VirtualAlloc call. Thus we allocate one */ + /* extra page, which will prevent merging of blocks */ + /* in separate regions, and eliminate any temptation */ + /* to call VirtualProtect on a range spanning regions. */ + /* This wastes a small amount of memory, and risks */ + /* increased fragmentation. But better alternatives */ + /* would require effort. */ + result = (ptr_t) VirtualAlloc(NULL, bytes + 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } @@ -1378,6 +1699,10 @@ word bytes; /* Reserve more pages */ word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1) & ~(GC_sysinfo.dwAllocationGranularity-1); + /* If we ever support MPROTECT_VDB here, we will probably need to */ + /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */ + /* never spans regions. It seems to be OK for a VirtualFree argument */ + /* to span regions, so we should be OK for now. */ result = (ptr_t) VirtualAlloc(NULL, res_bytes, MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); @@ -1508,6 +1833,7 @@ void GC_remap(ptr_t start, word bytes) } # else if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR); + fcntl(zero_descr, F_SETFD, FD_CLOEXEC); if (0 == start_addr) return; result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, MAP_FIXED | MAP_PRIVATE, zero_descr, 0); @@ -1694,7 +2020,6 @@ void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots; * make sure that other system calls are similarly protected * or write only to the stack. */ - GC_bool GC_dirty_maintained = FALSE; # ifdef DEFAULT_VDB @@ -1708,6 +2033,9 @@ GC_bool GC_dirty_maintained = FALSE; /* Initialize virtual dirty bit implementation. */ void GC_dirty_init() { +# ifdef PRINTSTATS + GC_printf0("Initializing DEFAULT_VDB...\n"); +# endif GC_dirty_maintained = TRUE; } @@ -1776,17 +2104,21 @@ GC_bool is_ptrfree; /* * This implementation maintains dirty bits itself by catching write * faults and keeping track of them. We assume nobody else catches - * SIGBUS or SIGSEGV. We assume no write faults occur in system calls - * except as a result of a read system call. This means clients must - * either ensure that system calls do not touch the heap, or must - * provide their own wrappers analogous to the one for read. + * SIGBUS or SIGSEGV. We assume no write faults occur in system calls. + * This means that clients must ensure that system calls don't write + * to the write-protected heap. Probably the best way to do this is to + * ensure that system calls write at most to POINTERFREE objects in the + * heap, and do even that only if we are on a platform on which those + * are not protected. Another alternative is to wrap system calls + * (see example for read below), but the current implementation holds + * a lock across blocking calls, making it problematic for multithreaded + * applications. * We assume the page size is a multiple of HBLKSIZE. - * This implementation is currently SunOS 4.X and IRIX 5.X specific, though we - * tried to use portable code where easily possible. It is known - * not to work under a number of other systems. + * We prefer them to be the same. We avoid protecting POINTERFREE + * objects only if they are the same. */ -# if !defined(MSWIN32) && !defined(MSWINCE) +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN) # include <sys/mman.h> # include <signal.h> @@ -1805,6 +2137,23 @@ GC_bool is_ptrfree; # else +# ifdef DARWIN + /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to + decrease the likelihood of some of the problems described below. */ + #include <mach/vm_map.h> + extern mach_port_t GC_task_self; + #define PROTECT(addr,len) \ + if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ + FALSE,VM_PROT_READ) != KERN_SUCCESS) { \ + ABORT("vm_portect failed"); \ + } + #define UNPROTECT(addr,len) \ + if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ + FALSE,VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) { \ + ABORT("vm_portect failed"); \ + } +# else + # ifndef MSWINCE # include <signal.h> # endif @@ -1822,20 +2171,22 @@ GC_bool is_ptrfree; &protect_junk)) { \ ABORT("un-VirtualProtect failed"); \ } - -# endif +# endif /* !DARWIN */ +# endif /* MSWIN32 || MSWINCE || DARWIN */ #if defined(SUNOS4) || defined(FREEBSD) typedef void (* SIG_PF)(); -#endif +#endif /* SUNOS4 || FREEBSD */ + #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \ - || defined(MACOSX) || defined(HURD) + || defined(HURD) # ifdef __STDC__ typedef void (* SIG_PF)(int); # else typedef void (* SIG_PF)(); # endif -#endif +#endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */ + #if defined(MSWIN32) typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF; # undef SIG_DFL @@ -1849,7 +2200,8 @@ GC_bool is_ptrfree; #if defined(IRIX5) || defined(OSF1) || defined(HURD) typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *); -#endif +#endif /* IRIX5 || OSF1 || HURD */ + #if defined(SUNOS5SIGS) # ifdef HPUX # define SIGINFO __siginfo @@ -1861,13 +2213,14 @@ GC_bool is_ptrfree; # else typedef void (* REAL_SIG_PF)(); # endif -#endif +#endif /* SUNOS5SIGS */ + #if defined(LINUX) # if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 typedef struct sigcontext s_c; # else /* glibc < 2.2 */ # include <linux/version.h> -# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) +# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) || defined(ARM32) typedef struct sigcontext s_c; # else typedef struct sigcontext_struct s_c; @@ -1895,139 +2248,14 @@ GC_bool is_ptrfree; return (char *)faultaddr; } # endif /* !ALPHA */ -# endif - -# if defined(MACOSX) /* Should also test for PowerPC? */ - typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *); - -/* Decodes the machine instruction which was responsible for the sending of the - SIGBUS signal. Sadly this is the only way to find the faulting address because - the signal handler doesn't get it directly from the kernel (although it is - available on the Mach level, but droppped by the BSD personality before it - calls our signal handler...) - This code should be able to deal correctly with all PPCs starting from the - 601 up to and including the G4s (including Velocity Engine). */ -#define EXTRACT_OP1(iw) (((iw) & 0xFC000000) >> 26) -#define EXTRACT_OP2(iw) (((iw) & 0x000007FE) >> 1) -#define EXTRACT_REGA(iw) (((iw) & 0x001F0000) >> 16) -#define EXTRACT_REGB(iw) (((iw) & 0x03E00000) >> 21) -#define EXTRACT_REGC(iw) (((iw) & 0x0000F800) >> 11) -#define EXTRACT_DISP(iw) ((short *) &(iw))[1] - -static char *get_fault_addr(struct sigcontext *scp) -{ - unsigned int instr = *((unsigned int *) scp->sc_ir); - unsigned int * regs = &((unsigned int *) scp->sc_regs)[2]; - int disp = 0, tmp; - unsigned int baseA = 0, baseB = 0; - unsigned int addr, alignmask = 0xFFFFFFFF; - -#ifdef GC_DEBUG_DECODER - GC_err_printf1("Instruction: 0x%lx\n", instr); - GC_err_printf1("Opcode 1: d\n", (int)EXTRACT_OP1(instr)); -#endif - switch(EXTRACT_OP1(instr)) { - case 38: /* stb */ - case 39: /* stbu */ - case 54: /* stfd */ - case 55: /* stfdu */ - case 52: /* stfs */ - case 53: /* stfsu */ - case 44: /* sth */ - case 45: /* sthu */ - case 47: /* stmw */ - case 36: /* stw */ - case 37: /* stwu */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - disp = EXTRACT_DISP(instr); - break; - case 31: -#ifdef GC_DEBUG_DECODER - GC_err_printf1("Opcode 2: %d\n", (int)EXTRACT_OP2(instr)); -#endif - switch(EXTRACT_OP2(instr)) { - case 86: /* dcbf */ - case 54: /* dcbst */ - case 1014: /* dcbz */ - case 247: /* stbux */ - case 215: /* stbx */ - case 759: /* stfdux */ - case 727: /* stfdx */ - case 983: /* stfiwx */ - case 695: /* stfsux */ - case 663: /* stfsx */ - case 918: /* sthbrx */ - case 439: /* sthux */ - case 407: /* sthx */ - case 661: /* stswx */ - case 662: /* stwbrx */ - case 150: /* stwcx. */ - case 183: /* stwux */ - case 151: /* stwx */ - case 135: /* stvebx */ - case 167: /* stvehx */ - case 199: /* stvewx */ - case 231: /* stvx */ - case 487: /* stvxl */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - baseB = regs[EXTRACT_REGC(instr)]; - /* determine Altivec alignment mask */ - switch(EXTRACT_OP2(instr)) { - case 167: /* stvehx */ - alignmask = 0xFFFFFFFE; - break; - case 199: /* stvewx */ - alignmask = 0xFFFFFFFC; - break; - case 231: /* stvx */ - alignmask = 0xFFFFFFF0; - break; - case 487: /* stvxl */ - alignmask = 0xFFFFFFF0; - break; - } - break; - case 725: /* stswi */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - break; - default: /* ignore instruction */ -#ifdef GC_DEBUG_DECODER - GC_err_printf("Ignored by inner handler\n"); -#endif - return NULL; - break; - } - break; - default: /* ignore instruction */ -#ifdef GC_DEBUG_DECODER - GC_err_printf("Ignored by main handler\n"); -#endif - return NULL; - break; - } - - addr = (baseA + baseB) + disp; - addr &= alignmask; -#ifdef GC_DEBUG_DECODER - GC_err_printf1("BaseA: %d\n", baseA); - GC_err_printf1("BaseB: %d\n", baseB); - GC_err_printf1("Disp: %d\n", disp); - GC_err_printf1("Address: %d\n", addr); -#endif - return (char *)addr; -} -#endif /* MACOSX */ +# endif /* LINUX */ +#ifndef DARWIN SIG_PF GC_old_bus_handler; SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ +#endif /* !DARWIN */ -#ifdef THREADS +#if defined(THREADS) /* We need to lock around the bitmap update in the write fault handler */ /* in order to avoid the risk of losing a bit. We do this with a */ /* test-and-set spin lock if we know how to do that. Otherwise we */ @@ -2076,6 +2304,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ #endif /* !THREADS */ /*ARGSUSED*/ +#if !defined(DARWIN) # if defined (SUNOS4) || defined(FREEBSD) void GC_write_fault_handler(sig, code, scp, addr) int sig, code; @@ -2091,7 +2320,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGBUS) # define CODE_OK (code == BUS_PAGE_FAULT) # endif -# endif +# endif /* SUNOS4 || FREEBSD */ + # if defined(IRIX5) || defined(OSF1) || defined(HURD) # include <errno.h> void GC_write_fault_handler(int sig, int code, struct sigcontext *scp) @@ -2107,7 +2337,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGBUS || sig == SIGSEGV) # define CODE_OK TRUE # endif -# endif +# endif /* IRIX5 || OSF1 || HURD */ + # if defined(LINUX) # if defined(ALPHA) || defined(M68K) void GC_write_fault_handler(int sig, int code, s_c * sc) @@ -2115,7 +2346,11 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # if defined(IA64) || defined(HP_PA) void GC_write_fault_handler(int sig, siginfo_t * si, s_c * scp) # else - void GC_write_fault_handler(int sig, s_c sc) +# if defined(ARM32) + void GC_write_fault_handler(int sig, int a2, int a3, int a4, s_c sc) +# else + void GC_write_fault_handler(int sig, s_c sc) +# endif # endif # endif # define SIG_OK (sig == SIGSEGV) @@ -2123,7 +2358,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ /* Empirically c.trapno == 14, on IA32, but is that useful? */ /* Should probably consider alignment issues on other */ /* architectures. */ -# endif +# endif /* LINUX */ + # if defined(SUNOS5SIGS) # ifdef __STDC__ void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context) @@ -2144,13 +2380,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGSEGV) # define CODE_OK (scp -> si_code == SEGV_ACCERR) # endif -# endif - -# if defined(MACOSX) - void GC_write_fault_handler(int sig, int code, struct sigcontext *scp) -# define SIG_OK (sig == SIGBUS) -# define CODE_OK (code == 0 /* experimentally determined */) -# endif +# endif /* SUNOS5SIGS */ # if defined(MSWIN32) || defined(MSWINCE) LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info) @@ -2158,7 +2388,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ STATUS_ACCESS_VIOLATION) # define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1) /* Write fault */ -# endif +# endif /* MSWIN32 || MSWINCE */ { register unsigned i; # if defined(HURD) @@ -2218,16 +2448,17 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # if defined(POWERPC) char * addr = (char *) (sc.regs->dar); # else - --> architecture not supported +# if defined(ARM32) + char * addr = (char *)sc.fault_address; +# else + --> architecture not supported +# endif # endif # endif # endif # endif # endif # endif -# if defined(MACOSX) - char * addr = get_fault_addr(scp); -# endif # if defined(MSWIN32) || defined(MSWINCE) char * addr = (char *) (exc_info -> ExceptionRecord -> ExceptionInformation[1]); @@ -2291,9 +2522,6 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ (*(REAL_SIG_PF)old_handler) (sig, code, scp); return; # endif -# ifdef MACOSX - (*(REAL_SIG_PF)old_handler) (sig, code, scp); -# endif # ifdef MSWIN32 return((*old_handler)(exc_info)); # endif @@ -2335,10 +2563,11 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ ABORT("Unexpected bus error or segmentation fault"); #endif } +#endif /* !DARWIN */ /* * We hold the allocation lock. We expect block h to be written - * shortly. Ensure that all pages cvontaining any part of the n hblks + * shortly. Ensure that all pages containing any part of the n hblks * starting at h are no longer protected. If is_ptrfree is false, * also ensure that they will subsequently appear to be dirty. */ @@ -2367,6 +2596,7 @@ GC_bool is_ptrfree; UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); } +#if !defined(DARWIN) void GC_dirty_init() { # if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \ @@ -2389,13 +2619,6 @@ void GC_dirty_init() (void)sigaddset(&act.sa_mask, SIG_SUSPEND); # endif /* SIG_SUSPEND */ # endif -# if defined(MACOSX) - struct sigaction act, oldact; - - act.sa_flags = SA_RESTART; - act.sa_handler = GC_write_fault_handler; - sigemptyset(&act.sa_mask); -# endif # ifdef PRINTSTATS GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n"); # endif @@ -2435,9 +2658,12 @@ void GC_dirty_init() sigaction(SIGSEGV, 0, &oldact); sigaction(SIGSEGV, &act, 0); # else - sigaction(SIGSEGV, &act, &oldact); + { + int res = sigaction(SIGSEGV, &act, &oldact); + if (res != 0) ABORT("Sigaction failed"); + } # endif -# if defined(_sigargs) || defined(HURD) +# if defined(_sigargs) || defined(HURD) || !defined(SA_SIGINFO) /* This is Irix 5.x, not 6.x. Irix 5.x does not have */ /* sa_sigaction. */ GC_old_segv_handler = oldact.sa_handler; @@ -2458,7 +2684,7 @@ void GC_dirty_init() # endif } # endif -# if defined(MACOSX) || defined(HPUX) || defined(LINUX) || defined(HURD) +# if defined(HPUX) || defined(LINUX) || defined(HURD) sigaction(SIGBUS, &act, &oldact); GC_old_bus_handler = oldact.sa_handler; if (GC_old_bus_handler == SIG_IGN) { @@ -2470,7 +2696,7 @@ void GC_dirty_init() GC_err_printf0("Replaced other SIGBUS handler\n"); # endif } -# endif /* MACOS || HPUX || LINUX */ +# endif /* HPUX || LINUX || HURD */ # if defined(MSWIN32) GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler); if (GC_old_segv_handler != NULL) { @@ -2482,6 +2708,7 @@ void GC_dirty_init() } # endif } +#endif /* !DARWIN */ int GC_incremental_protection_needs() { @@ -2628,15 +2855,23 @@ word len; ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE); } -#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(THREADS) \ - && !defined(GC_USE_LD_WRAP) -/* Replacement for UNIX system call. */ -/* Other calls that write to the heap should be handled similarly. */ -/* Note that this doesn't work well for blocking reads: It will hold */ -/* tha allocation lock for the entur duration of the call. Multithreaded */ -/* clients should really ensure that it won't block, either by setting */ -/* the descriptor nonblocking, or by calling select or poll first, to */ -/* make sure that input is available. */ +#if 0 + +/* We no longer wrap read by default, since that was causing too many */ +/* problems. It is preferred that the client instead avoids writing */ +/* to the write-protected heap with a system call. */ +/* This still serves as sample code if you do want to wrap system calls.*/ + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP) +/* Replacement for UNIX system call. */ +/* Other calls that write to the heap should be handled similarly. */ +/* Note that this doesn't work well for blocking reads: It will hold */ +/* the allocation lock for the entire duration of the call. Multithreaded */ +/* clients should really ensure that it won't block, either by setting */ +/* the descriptor nonblocking, or by calling select or poll first, to */ +/* make sure that input is available. */ +/* Another, preferred alternative is to ensure that system calls never */ +/* write to the protected heap (see above). */ # if defined(__STDC__) && !defined(SUNOS4) # include <unistd.h> # include <sys/uio.h> @@ -2706,6 +2941,8 @@ word len; /* actually calls. */ #endif +#endif /* 0 */ + /*ARGSUSED*/ GC_bool GC_page_was_ever_dirty(h) struct hblk *h; @@ -2721,13 +2958,6 @@ word n; { } -# else /* !MPROTECT_VDB */ - -# ifdef GC_USE_LD_WRAP - ssize_t __wrap_read(int fd, void *buf, size_t nbyte) - { return __real_read(fd, buf, nbyte); } -# endif - # endif /* MPROTECT_VDB */ # ifdef PROC_VDB @@ -2806,6 +3036,7 @@ void GC_dirty_init() } GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0); close(fd); + syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC); if (GC_proc_fd < 0) { ABORT("/proc ioctl failed"); } @@ -3045,6 +3276,552 @@ GC_bool is_ptrfree; # endif /* PCR_VDB */ +#if defined(MPROTECT_VDB) && defined(DARWIN) +/* The following sources were used as a *reference* for this exception handling + code: + 1. Apple's mach/xnu documentation + 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the + omnigroup's macosx-dev list. + www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html + 3. macosx-nat.c from Apple's GDB source code. +*/ + +/* The bug that caused all this trouble should now be fixed. This should + eventually be removed if all goes well. */ +/* define BROKEN_EXCEPTION_HANDLING */ + +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <mach/thread_status.h> +#include <mach/exception.h> +#include <mach/task.h> +#include <pthread.h> + +/* These are not defined in any header, although they are documented */ +extern boolean_t exc_server(mach_msg_header_t *,mach_msg_header_t *); +extern kern_return_t exception_raise( + mach_port_t,mach_port_t,mach_port_t, + exception_type_t,exception_data_t,mach_msg_type_number_t); +extern kern_return_t exception_raise_state( + mach_port_t,mach_port_t,mach_port_t, + exception_type_t,exception_data_t,mach_msg_type_number_t, + thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t, + thread_state_t,mach_msg_type_number_t*); +extern kern_return_t exception_raise_state_identity( + mach_port_t,mach_port_t,mach_port_t, + exception_type_t,exception_data_t,mach_msg_type_number_t, + thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t, + thread_state_t,mach_msg_type_number_t*); + + +#define MAX_EXCEPTION_PORTS 16 + +static mach_port_t GC_task_self; + +static struct { + mach_msg_type_number_t count; + exception_mask_t masks[MAX_EXCEPTION_PORTS]; + exception_handler_t ports[MAX_EXCEPTION_PORTS]; + exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; + thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; +} GC_old_exc_ports; + +static struct { + mach_port_t exception; +#if defined(THREADS) + mach_port_t reply; +#endif +} GC_ports; + +typedef struct { + mach_msg_header_t head; +} GC_msg_t; + +typedef enum { + GC_MP_NORMAL, GC_MP_DISCARDING, GC_MP_STOPPED +} GC_mprotect_state_t; + +/* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, + but it isn't documented. Use the source and see if they + should be ok. */ +#define ID_STOP 1 +#define ID_RESUME 2 + +/* These values are only used on the reply port */ +#define ID_ACK 3 + +#if defined(THREADS) + +GC_mprotect_state_t GC_mprotect_state; + +/* The following should ONLY be called when the world is stopped */ +static void GC_mprotect_thread_notify(mach_msg_id_t id) { + struct { + GC_msg_t msg; + mach_msg_trailer_t trailer; + } buf; + mach_msg_return_t r; + /* remote, local */ + buf.msg.head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0); + buf.msg.head.msgh_size = sizeof(buf.msg); + buf.msg.head.msgh_remote_port = GC_ports.exception; + buf.msg.head.msgh_local_port = MACH_PORT_NULL; + buf.msg.head.msgh_id = id; + + r = mach_msg( + &buf.msg.head, + MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_LARGE, + sizeof(buf.msg), + sizeof(buf), + GC_ports.reply, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if(r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_notify"); + if(buf.msg.head.msgh_id != ID_ACK) + ABORT("invalid ack in GC_mprotect_thread_notify"); +} + +/* Should only be called by the mprotect thread */ +static void GC_mprotect_thread_reply() { + GC_msg_t msg; + mach_msg_return_t r; + /* remote, local */ + msg.head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0); + msg.head.msgh_size = sizeof(msg); + msg.head.msgh_remote_port = GC_ports.reply; + msg.head.msgh_local_port = MACH_PORT_NULL; + msg.head.msgh_id = ID_ACK; + + r = mach_msg( + &msg.head, + MACH_SEND_MSG, + sizeof(msg), + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if(r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_reply"); +} + +void GC_mprotect_stop() { + GC_mprotect_thread_notify(ID_STOP); +} +void GC_mprotect_resume() { + GC_mprotect_thread_notify(ID_RESUME); +} + +#else /* !THREADS */ +/* The compiler should optimize away any GC_mprotect_state computations */ +#define GC_mprotect_state GC_MP_NORMAL +#endif + +static void *GC_mprotect_thread(void *arg) { + mach_msg_return_t r; + /* These two structures contain some private kernel data. We don't need to + access any of it so we don't bother defining a proper struct. The + correct definitions are in the xnu source code. */ + struct { + mach_msg_header_t head; + char data[256]; + } reply; + struct { + mach_msg_header_t head; + mach_msg_body_t msgh_body; + char data[1024]; + } msg; + + mach_msg_id_t id; + + for(;;) { + r = mach_msg( + &msg.head, + MACH_RCV_MSG|MACH_RCV_LARGE| + (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0), + 0, + sizeof(msg), + GC_ports.exception, + GC_mprotect_state == GC_MP_DISCARDING ? 0 : MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; + +#if defined(THREADS) + if(GC_mprotect_state == GC_MP_DISCARDING) { + if(r == MACH_RCV_TIMED_OUT) { + GC_mprotect_state = GC_MP_STOPPED; + GC_mprotect_thread_reply(); + continue; + } + if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) + ABORT("out of order mprotect thread request"); + } +#endif + + if(r != MACH_MSG_SUCCESS) { + GC_err_printf2("mach_msg failed with %d %s\n", + (int)r,mach_error_string(r)); + ABORT("mach_msg failed"); + } + + switch(id) { +#if defined(THREADS) + case ID_STOP: + if(GC_mprotect_state != GC_MP_NORMAL) + ABORT("Called mprotect_stop when state wasn't normal"); + GC_mprotect_state = GC_MP_DISCARDING; + break; + case ID_RESUME: + if(GC_mprotect_state != GC_MP_STOPPED) + ABORT("Called mprotect_resume when state wasn't stopped"); + GC_mprotect_state = GC_MP_NORMAL; + GC_mprotect_thread_reply(); + break; +#endif /* THREADS */ + default: + /* Handle the message (calls catch_exception_raise) */ + if(!exc_server(&msg.head,&reply.head)) + ABORT("exc_server failed"); + /* Send the reply */ + r = mach_msg( + &reply.head, + MACH_SEND_MSG, + reply.head.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if(r != MACH_MSG_SUCCESS) { + /* This will fail if the thread dies, but the thread shouldn't + die... */ + #ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf2( + "mach_msg failed with %d %s while sending exc reply\n", + (int)r,mach_error_string(r)); + #else + ABORT("mach_msg failed while sending exception reply"); + #endif + } + } /* switch */ + } /* for(;;) */ + /* NOT REACHED */ + return NULL; +} + +/* All this SIGBUS code shouldn't be necessary. All protection faults should + be going throught the mach exception handler. However, it seems a SIGBUS is + occasionally sent for some unknown reason. Even more odd, it seems to be + meaningless and safe to ignore. */ +#ifdef BROKEN_EXCEPTION_HANDLING + +typedef void (* SIG_PF)(); +static SIG_PF GC_old_bus_handler; + +/* Updates to this aren't atomic, but the SIGBUSs seem pretty rare. + Even if this doesn't get updated property, it isn't really a problem */ +static int GC_sigbus_count; + +static void GC_darwin_sigbus(int num,siginfo_t *sip,void *context) { + if(num != SIGBUS) ABORT("Got a non-sigbus signal in the sigbus handler"); + + /* Ugh... some seem safe to ignore, but too many in a row probably means + trouble. GC_sigbus_count is reset for each mach exception that is + handled */ + if(GC_sigbus_count >= 8) { + ABORT("Got more than 8 SIGBUSs in a row!"); + } else { + GC_sigbus_count++; + GC_err_printf0("GC: WARNING: Ignoring SIGBUS.\n"); + } +} +#endif /* BROKEN_EXCEPTION_HANDLING */ + +void GC_dirty_init() { + kern_return_t r; + mach_port_t me; + pthread_t thread; + pthread_attr_t attr; + exception_mask_t mask; + +# ifdef PRINTSTATS + GC_printf0("Inititalizing mach/darwin mprotect virtual dirty bit " + "implementation\n"); +# endif +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf0("GC: WARNING: Enabling workarounds for various darwin " + "exception handling bugs.\n"); +# endif + GC_dirty_maintained = TRUE; + if (GC_page_size % HBLKSIZE != 0) { + GC_err_printf0("Page size not multiple of HBLKSIZE\n"); + ABORT("Page size not multiple of HBLKSIZE"); + } + + GC_task_self = me = mach_task_self(); + + r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.exception); + if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (exception port)"); + + r = mach_port_insert_right(me,GC_ports.exception,GC_ports.exception, + MACH_MSG_TYPE_MAKE_SEND); + if(r != KERN_SUCCESS) + ABORT("mach_port_insert_right failed (exception port)"); + + #if defined(THREADS) + r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.reply); + if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (reply port)"); + #endif + + /* The exceptions we want to catch */ + mask = EXC_MASK_BAD_ACCESS; + + r = task_get_exception_ports( + me, + mask, + GC_old_exc_ports.masks, + &GC_old_exc_ports.count, + GC_old_exc_ports.ports, + GC_old_exc_ports.behaviors, + GC_old_exc_ports.flavors + ); + if(r != KERN_SUCCESS) ABORT("task_get_exception_ports failed"); + + r = task_set_exception_ports( + me, + mask, + GC_ports.exception, + EXCEPTION_DEFAULT, + MACHINE_THREAD_STATE + ); + if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed"); + + if(pthread_attr_init(&attr) != 0) ABORT("pthread_attr_init failed"); + if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0) + ABORT("pthread_attr_setdetachedstate failed"); + +# undef pthread_create + /* This will call the real pthread function, not our wrapper */ + if(pthread_create(&thread,&attr,GC_mprotect_thread,NULL) != 0) + ABORT("pthread_create failed"); + pthread_attr_destroy(&attr); + + /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */ + #ifdef BROKEN_EXCEPTION_HANDLING + { + struct sigaction sa, oldsa; + sa.sa_handler = (SIG_PF)GC_darwin_sigbus; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART|SA_SIGINFO; + if(sigaction(SIGBUS,&sa,&oldsa) < 0) ABORT("sigaction"); + GC_old_bus_handler = (SIG_PF)oldsa.sa_handler; + if (GC_old_bus_handler != SIG_DFL) { +# ifdef PRINTSTATS + GC_err_printf0("Replaced other SIGBUS handler\n"); +# endif + } + } + #endif /* BROKEN_EXCEPTION_HANDLING */ +} + +/* The source code for Apple's GDB was used as a reference for the exception + forwarding code. This code is similar to be GDB code only because there is + only one way to do it. */ +static kern_return_t GC_forward_exception( + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + exception_data_t data, + mach_msg_type_number_t data_count +) { + int i; + kern_return_t r; + mach_port_t port; + exception_behavior_t behavior; + thread_state_flavor_t flavor; + + thread_state_data_t thread_state; + mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; + + for(i=0;i<GC_old_exc_ports.count;i++) + if(GC_old_exc_ports.masks[i] & (1 << exception)) + break; + if(i==GC_old_exc_ports.count) ABORT("No handler for exception!"); + + port = GC_old_exc_ports.ports[i]; + behavior = GC_old_exc_ports.behaviors[i]; + flavor = GC_old_exc_ports.flavors[i]; + + if(behavior != EXCEPTION_DEFAULT) { + r = thread_get_state(thread,flavor,thread_state,&thread_state_count); + if(r != KERN_SUCCESS) + ABORT("thread_get_state failed in forward_exception"); + } + + switch(behavior) { + case EXCEPTION_DEFAULT: + r = exception_raise(port,thread,task,exception,data,data_count); + break; + case EXCEPTION_STATE: + r = exception_raise_state(port,thread,task,exception,data, + data_count,&flavor,thread_state,thread_state_count, + thread_state,&thread_state_count); + break; + case EXCEPTION_STATE_IDENTITY: + r = exception_raise_state_identity(port,thread,task,exception,data, + data_count,&flavor,thread_state,thread_state_count, + thread_state,&thread_state_count); + break; + default: + r = KERN_FAILURE; /* make gcc happy */ + ABORT("forward_exception: unknown behavior"); + break; + } + + if(behavior != EXCEPTION_DEFAULT) { + r = thread_set_state(thread,flavor,thread_state,thread_state_count); + if(r != KERN_SUCCESS) + ABORT("thread_set_state failed in forward_exception"); + } + + return r; +} + +#define FWD() GC_forward_exception(thread,task,exception,code,code_count) + +/* This violates the namespace rules but there isn't anything that can be done + about it. The exception handling stuff is hard coded to call this */ +kern_return_t +catch_exception_raise( + mach_port_t exception_port,mach_port_t thread,mach_port_t task, + exception_type_t exception,exception_data_t code, + mach_msg_type_number_t code_count +) { + kern_return_t r; + char *addr; + struct hblk *h; + int i; +#ifdef POWERPC + thread_state_flavor_t flavor = PPC_EXCEPTION_STATE; + mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT; + ppc_exception_state_t exc_state; +#else +# error FIXME for non-ppc darwin +#endif + + + if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { + #ifdef DEBUG_EXCEPTION_HANDLING + /* We aren't interested, pass it on to the old handler */ + GC_printf3("Exception: 0x%x Code: 0x%x 0x%x in catch....\n", + exception, + code_count > 0 ? code[0] : -1, + code_count > 1 ? code[1] : -1); + #endif + return FWD(); + } + + r = thread_get_state(thread,flavor, + (natural_t*)&exc_state,&exc_state_count); + if(r != KERN_SUCCESS) { + /* The thread is supposed to be suspended while the exception handler + is called. This shouldn't fail. */ + #ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf0("thread_get_state failed in " + "catch_exception_raise\n"); + return KERN_SUCCESS; + #else + ABORT("thread_get_state failed in catch_exception_raise"); + #endif + } + + /* This is the address that caused the fault */ + addr = (char*) exc_state.dar; + + if((HDR(addr)) == 0) { + /* Ugh... just like the SIGBUS problem above, it seems we get a bogus + KERN_PROTECTION_FAILURE every once and a while. We wait till we get + a bunch in a row before doing anything about it. If a "real" fault + ever occurres it'll just keep faulting over and over and we'll hit + the limit pretty quickly. */ + #ifdef BROKEN_EXCEPTION_HANDLING + static char *last_fault; + static int last_fault_count; + + if(addr != last_fault) { + last_fault = addr; + last_fault_count = 0; + } + if(++last_fault_count < 32) { + if(last_fault_count == 1) + GC_err_printf1( + "GC: WARNING: Ignoring KERN_PROTECTION_FAILURE at %p\n", + addr); + return KERN_SUCCESS; + } + + GC_err_printf1("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr); + /* Can't pass it along to the signal handler because that is + ignoring SIGBUS signals. We also shouldn't call ABORT here as + signals don't always work too well from the exception handler. */ + GC_err_printf0("Aborting\n"); + exit(EXIT_FAILURE); + #else /* BROKEN_EXCEPTION_HANDLING */ + /* Pass it along to the next exception handler + (which should call SIGBUS/SIGSEGV) */ + return FWD(); + #endif /* !BROKEN_EXCEPTION_HANDLING */ + } + + #ifdef BROKEN_EXCEPTION_HANDLING + /* Reset the number of consecutive SIGBUSs */ + GC_sigbus_count = 0; + #endif + + if(GC_mprotect_state == GC_MP_NORMAL) { /* common case */ + h = (struct hblk*)((word)addr & ~(GC_page_size-1)); + UNPROTECT(h, GC_page_size); + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + register int index = PHT_HASH(h+i); + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } else if(GC_mprotect_state == GC_MP_DISCARDING) { + /* Lie to the thread for now. No sense UNPROTECT()ing the memory + when we're just going to PROTECT() it again later. The thread + will just fault again once it resumes */ + } else { + /* Shouldn't happen, i don't think */ + GC_printf0("KERN_PROTECTION_FAILURE while world is stopped\n"); + return FWD(); + } + return KERN_SUCCESS; +} +#undef FWD + +/* These should never be called, but just in case... */ +kern_return_t catch_exception_raise_state(mach_port_name_t exception_port, + int exception, exception_data_t code, mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt) +{ + ABORT("catch_exception_raise_state"); + return(KERN_INVALID_ARGUMENT); +} +kern_return_t catch_exception_raise_state_identity( + mach_port_name_t exception_port, mach_port_t thread, mach_port_t task, + int exception, exception_data_t code, mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt) +{ + ABORT("catch_exception_raise_state_identity"); + return(KERN_INVALID_ARGUMENT); +} + + +#endif /* DARWIN && MPROTECT_VDB */ + # ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS int GC_incremental_protection_needs() { @@ -3105,19 +3882,20 @@ GC_bool is_ptrfree; # endif #endif /* SPARC */ -#ifdef SAVE_CALL_CHAIN +#ifdef NEED_CALLINFO /* Fill in the pc and argument information for up to NFRAMES of my */ /* callers. Ignore my frame and my callers frame. */ #ifdef LINUX -# include <features.h> -# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2 -# define HAVE_BUILTIN_BACKTRACE -# endif +# include <unistd.h> #endif +#endif /* NEED_CALLINFO */ + +#ifdef SAVE_CALL_CHAIN + #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \ - && defined(HAVE_BUILTIN_BACKTRACE) + && defined(GC_HAVE_BUILTIN_BACKTRACE) #include <execinfo.h> @@ -3163,8 +3941,6 @@ struct callinfo info[NFRAMES]; asm("movl %%ebp,%0" : "=r"(frame)); fp = frame; # else - word GC_save_regs_in_stack(); - frame = (struct frame *) GC_save_regs_in_stack (); fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS); #endif @@ -3188,31 +3964,139 @@ struct callinfo info[NFRAMES]; #endif /* SAVE_CALL_CHAIN */ -#if defined(LINUX) && defined(__ELF__) && \ - (!defined(SMALL_CONFIG) || defined(USE_PROC_FOR_LIBRARIES)) -#ifdef GC_USE_LD_WRAP -# define READ __real_read -#else -# define READ read -#endif - +#ifdef NEED_CALLINFO -/* Repeatedly perform a read call until the buffer is filled or */ -/* we encounter EOF. */ -ssize_t GC_repeat_read(int fd, char *buf, size_t count) +/* Print info to stderr. We do NOT hold the allocation lock */ +void GC_print_callers (info) +struct callinfo info[NFRAMES]; { - ssize_t num_read = 0; - ssize_t result; + register int i; + static int reentry_count = 0; + GC_bool stop = FALSE; + + LOCK(); + ++reentry_count; + UNLOCK(); - while (num_read < count) { - result = READ(fd, buf + num_read, count - num_read); - if (result < 0) return result; - if (result == 0) break; - num_read += result; +# if NFRAMES == 1 + GC_err_printf0("\tCaller at allocation:\n"); +# else + GC_err_printf0("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES && !stop ; i++) { + if (info[i].ci_pc == 0) break; +# if NARGS > 0 + { + int j; + + GC_err_printf0("\t\targs: "); + for (j = 0; j < NARGS; j++) { + if (j != 0) GC_err_printf0(", "); + GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), + ~(info[i].ci_arg[j])); + } + GC_err_printf0("\n"); + } +# endif + if (reentry_count > 1) { + /* We were called during an allocation during */ + /* a previous GC_print_callers call; punt. */ + GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc); + continue; + } + { +# ifdef LINUX + FILE *pipe; +# endif +# if defined(GC_HAVE_BUILTIN_BACKTRACE) + char **sym_name = + backtrace_symbols((void **)(&(info[i].ci_pc)), 1); + char *name = sym_name[0]; +# else + char buf[40]; + char *name = buf; + sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc); +# endif +# if defined(LINUX) && !defined(SMALL_CONFIG) + /* Try for a line number. */ + { +# define EXE_SZ 100 + static char exe_name[EXE_SZ]; +# define CMD_SZ 200 + char cmd_buf[CMD_SZ]; +# define RESULT_SZ 200 + static char result_buf[RESULT_SZ]; + size_t result_len; + static GC_bool found_exe_name = FALSE; + static GC_bool will_fail = FALSE; + int ret_code; + /* Try to get it via a hairy and expensive scheme. */ + /* First we get the name of the executable: */ + if (will_fail) goto out; + if (!found_exe_name) { + ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); + if (ret_code < 0 || ret_code >= EXE_SZ + || exe_name[0] != '/') { + will_fail = TRUE; /* Dont try again. */ + goto out; + } + exe_name[ret_code] = '\0'; + found_exe_name = TRUE; + } + /* Then we use popen to start addr2line -e <exe> <addr> */ + /* There are faster ways to do this, but hopefully this */ + /* isn't time critical. */ + sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name, + (unsigned long)info[i].ci_pc); + pipe = popen(cmd_buf, "r"); + if (pipe == NULL + || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe)) + == 0) { + if (pipe != NULL) pclose(pipe); + will_fail = TRUE; + goto out; + } + if (result_buf[result_len - 1] == '\n') --result_len; + result_buf[result_len] = 0; + if (result_buf[0] == '?' + || result_buf[result_len-2] == ':' + && result_buf[result_len-1] == '0') { + pclose(pipe); + goto out; + } + /* Get rid of embedded newline, if any. Test for "main" */ + { + char * nl = strchr(result_buf, '\n'); + if (nl != NULL && nl < result_buf + result_len) { + *nl = ':'; + } + if (strncmp(result_buf, "main", nl - result_buf) == 0) { + stop = TRUE; + } + } + if (result_len < RESULT_SZ - 25) { + /* Add in hex address */ + sprintf(result_buf + result_len, " [0x%lx]", + (unsigned long)info[i].ci_pc); + } + name = result_buf; + pclose(pipe); + out:; + } +# endif /* LINUX */ + GC_err_printf1("\t\t%s\n", name); +# if defined(GC_HAVE_BUILTIN_BACKTRACE) + free(sym_name); /* May call GC_free; that's OK */ +# endif + } } - return num_read; + LOCK(); + --reentry_count; + UNLOCK(); } -#endif /* LINUX && ... */ + +#endif /* NEED_CALLINFO */ + #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) @@ -3220,20 +4104,16 @@ ssize_t GC_repeat_read(int fd, char *buf, size_t count) /* Dump /proc/self/maps to GC_stderr, to enable looking up names for addresses in FIND_LEAK output. */ +static word dump_maps(char *maps) +{ + GC_err_write(maps, strlen(maps)); + return 1; +} + void GC_print_address_map() { - int f; - int result; - char maps_temp[32768]; GC_err_printf0("---------- Begin address map ----------\n"); - f = open("/proc/self/maps", O_RDONLY); - if (-1 == f) ABORT("Couldn't open /proc/self/maps"); - do { - result = GC_repeat_read(f, maps_temp, sizeof(maps_temp)); - if (result <= 0) ABORT("Couldn't read /proc/self/maps"); - GC_err_write(maps_temp, result); - } while (result == sizeof(maps_temp)); - + GC_apply_to_maps(dump_maps); GC_err_printf0("---------- End address map ----------\n"); } diff --git a/boehm-gc/powerpc_macosx_mach_dep.s b/boehm-gc/powerpc_macosx_mach_dep.s deleted file mode 100644 index 92f06286e73..00000000000 --- a/boehm-gc/powerpc_macosx_mach_dep.s +++ /dev/null @@ -1,95 +0,0 @@ - -.text - - .set linkageArea,24 - .set params,4 - .set alignment,4 - - .set spaceToSave,linkageArea+params+alignment - .set spaceToSave8,spaceToSave+8 - -; Mark from machine registers that are saved by C compiler - .globl _GC_push_regs -_GC_push_regs: - ; PROLOG - mflr r0 ; get return address - stw r0,8(r1) ; save return address - stwu r1,-spaceToSave(r1) ; skip over caller save area - ; - mr r3,r2 ; mark from r2. Well Im not really sure - ; that this is necessary or even the right - ; thing to do - at least it doesnt harm... - ; According to Apples docs it points to - ; the direct data area, whatever that is... - bl L_GC_push_one$stub - mr r3,r13 ; mark from r13-r31 - bl L_GC_push_one$stub - mr r3,r14 - bl L_GC_push_one$stub - mr r3,r15 - bl L_GC_push_one$stub - mr r3,r16 - bl L_GC_push_one$stub - mr r3,r17 - bl L_GC_push_one$stub - mr r3,r18 - bl L_GC_push_one$stub - mr r3,r19 - bl L_GC_push_one$stub - mr r3,r20 - bl L_GC_push_one$stub - mr r3,r21 - bl L_GC_push_one$stub - mr r3,r22 - bl L_GC_push_one$stub - mr r3,r23 - bl L_GC_push_one$stub - mr r3,r24 - bl L_GC_push_one$stub - mr r3,r25 - bl L_GC_push_one$stub - mr r3,r26 - bl L_GC_push_one$stub - mr r3,r27 - bl L_GC_push_one$stub - mr r3,r28 - bl L_GC_push_one$stub - mr r3,r29 - bl L_GC_push_one$stub - mr r3,r30 - bl L_GC_push_one$stub - mr r3,r31 - bl L_GC_push_one$stub - ; EPILOG - lwz r0,spaceToSave8(r1) ; get return address back - mtlr r0 ; reset link register - addic r1,r1,spaceToSave ; restore stack pointer - blr - -.data -.picsymbol_stub -L_GC_push_one$stub: - .indirect_symbol _GC_push_one - mflr r0 - bcl 20,31,L0$_GC_push_one -L0$_GC_push_one: - mflr r11 - addis r11,r11,ha16(L_GC_push_one$lazy_ptr-L0$_GC_push_one) - mtlr r0 - lwz r12,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)(r11) - mtctr r12 - addi r11,r11,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one) - bctr -.data -.lazy_symbol_pointer -L_GC_push_one$lazy_ptr: - .indirect_symbol _GC_push_one - .long dyld_stub_binding_helper -.non_lazy_symbol_pointer -L_GC_push_one$non_lazy_ptr: - .indirect_symbol _GC_push_one - .long 0 - - - - diff --git a/boehm-gc/ptr_chck.c b/boehm-gc/ptr_chck.c index af49d5f5b79..d83d730d343 100644 --- a/boehm-gc/ptr_chck.c +++ b/boehm-gc/ptr_chck.c @@ -79,7 +79,7 @@ void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR, GC_PTR)) return(p); } sz = WORDS_TO_BYTES(hhdr -> hb_sz); - if (sz > WORDS_TO_BYTES(MAXOBJSZ)) { + if (sz > MAXOBJBYTES) { base = (ptr_t)HBLKPTR(p); limit = base + sz; if ((ptr_t)p >= limit) { @@ -165,7 +165,7 @@ void (*GC_is_valid_displacement_print_proc) GC_PROTO((GC_PTR)) = pdispl = HBLKDISPL(p); map_entry = MAP_ENTRY((hhdr -> hb_map), pdispl); if (map_entry == OBJ_INVALID - || sz > MAXOBJSZ && (ptr_t)p >= (ptr_t)h + sz) { + || sz > MAXOBJBYTES && (ptr_t)p >= (ptr_t)h + sz) { goto fail; } return(p); diff --git a/boehm-gc/reclaim.c b/boehm-gc/reclaim.c index 0418e9dea65..323d420fa40 100644 --- a/boehm-gc/reclaim.c +++ b/boehm-gc/reclaim.c @@ -27,23 +27,61 @@ signed_word GC_mem_found = 0; /* nonzero. */ #endif /* PARALLEL_MARK */ -static void report_leak(p, sz) -ptr_t p; -word sz; +/* We defer printing of leaked objects until we're done with the GC */ +/* cycle, since the routine for printing objects needs to run outside */ +/* the collector, e.g. without the allocation lock. */ +#define MAX_LEAKED 40 +ptr_t GC_leaked[MAX_LEAKED]; +unsigned GC_n_leaked = 0; + +GC_bool GC_have_errors = FALSE; + +void GC_add_leaked(leaked) +ptr_t leaked; { - if (HDR(p) -> hb_obj_kind == PTRFREE) { - GC_err_printf0("Leaked atomic object at "); - } else { - GC_err_printf0("Leaked composite object at "); + if (GC_n_leaked < MAX_LEAKED) { + GC_have_errors = TRUE; + GC_leaked[GC_n_leaked++] = leaked; + /* Make sure it's not reclaimed this cycle */ + GC_set_mark_bit(leaked); } - GC_print_heap_obj(p); - GC_err_printf0("\n"); } +static GC_bool printing_errors = FALSE; +/* Print all objects on the list after printing any smashed objs. */ +/* Clear both lists. */ +void GC_print_all_errors () +{ + unsigned i; + + LOCK(); + if (printing_errors) { + UNLOCK(); + return; + } + printing_errors = TRUE; + UNLOCK(); + if (GC_debugging_started) GC_print_all_smashed(); + for (i = 0; i < GC_n_leaked; ++i) { + ptr_t p = GC_leaked[i]; + if (HDR(p) -> hb_obj_kind == PTRFREE) { + GC_err_printf0("Leaked atomic object at "); + } else { + GC_err_printf0("Leaked composite object at "); + } + GC_print_heap_obj(p); + GC_err_printf0("\n"); + GC_free(p); + GC_leaked[i] = 0; + } + GC_n_leaked = 0; + printing_errors = FALSE; +} + + # define FOUND_FREE(hblk, word_no) \ { \ - report_leak((ptr_t)hblk + WORDS_TO_BYTES(word_no), \ - HDR(hblk) -> hb_sz); \ + GC_add_leaked((ptr_t)hblk + WORDS_TO_BYTES(word_no)); \ } /* @@ -866,7 +904,7 @@ void GC_print_block_list() * Clear *flp. * This must be done before dropping a list of free gcj-style objects, * since may otherwise end up with dangling "descriptor" pointers. - * It may help for other pointer-containg objects. + * It may help for other pointer-containing objects. */ void GC_clear_fl_links(flp) ptr_t *flp; diff --git a/boehm-gc/solaris_pthreads.c b/boehm-gc/solaris_pthreads.c index bd63e6c612c..bae77193593 100644 --- a/boehm-gc/solaris_pthreads.c +++ b/boehm-gc/solaris_pthreads.c @@ -13,9 +13,8 @@ /* * Support code for Solaris threads. Provides functionality we wish Sun * had provided. Relies on some information we probably shouldn't rely on. - * Modified Peter C. for Solaris Posix Threads. + * Modified by Peter C. for Solaris Posix Threads. */ -/* Boehm, September 14, 1994 4:44 pm PDT */ # if defined(GC_SOLARIS_PTHREADS) # include "private/gc_priv.h" diff --git a/boehm-gc/solaris_threads.c b/boehm-gc/solaris_threads.c index 2b520d3d085..5f05b19e008 100644 --- a/boehm-gc/solaris_threads.c +++ b/boehm-gc/solaris_threads.c @@ -37,6 +37,10 @@ # include <unistd.h> # include <errno.h> +#ifdef HANDLE_FORK + --> Not yet supported. Try porting the code from linux_threads.c. +#endif + /* * This is the default size of the LWP arrays. If there are more LWPs * than this when a stop-the-world GC happens, set_max_lwps will be @@ -361,7 +365,7 @@ static void restart_all_lwps() sizeof (prgregset_t)) != 0) { int j; - for(j = 0; j < NGREG; j++) + for(j = 0; j < NPRGREG; j++) { GC_printf3("%i: %x -> %x\n", j, GC_lwp_registers[i][j], @@ -821,7 +825,7 @@ int GC_thr_suspend(thread_t target_thread) if (result == 0) { t = GC_lookup_thread(target_thread); if (t == 0) ABORT("thread unknown to GC"); - t -> flags |= SUSPENDED; + t -> flags |= SUSPNDED; } UNLOCK(); return(result); @@ -837,7 +841,7 @@ int GC_thr_continue(thread_t target_thread) if (result == 0) { t = GC_lookup_thread(target_thread); if (t == 0) ABORT("thread unknown to GC"); - t -> flags &= ~SUSPENDED; + t -> flags &= ~SUSPNDED; } UNLOCK(); return(result); @@ -923,7 +927,7 @@ GC_thr_create(void *stack_base, size_t stack_size, my_flags |= CLIENT_OWNS_STACK; } if (flags & THR_DETACHED) my_flags |= DETACHED; - if (flags & THR_SUSPENDED) my_flags |= SUSPENDED; + if (flags & THR_SUSPENDED) my_flags |= SUSPNDED; result = thr_create(stack, stack_size, start_routine, arg, flags & ~THR_DETACHED, &my_new_thread); if (result == 0) { diff --git a/boehm-gc/sparc_mach_dep.S b/boehm-gc/sparc_mach_dep.S index 860eeb666f3..06a0f3b4673 100644 --- a/boehm-gc/sparc_mach_dep.S +++ b/boehm-gc/sparc_mach_dep.S @@ -37,7 +37,7 @@ loop: stx %g0,[%o3] ! *(long *)p = 0 cmp %o3,%o1 bgu,pt %xcc, loop ! if (p > limit) goto loop - add %o3,-8,%o3 ! p -= 8 (delay slot) + add %o3,-8,%o3 ! p -= 8 (delay slot) retl mov %o2,%sp ! Restore sp., delay slot #else /* 32 bit SPARC */ diff --git a/boehm-gc/tests/test.c b/boehm-gc/tests/test.c index 7cb4d0c768f..cfe23c0efde 100644 --- a/boehm-gc/tests/test.c +++ b/boehm-gc/tests/test.c @@ -43,7 +43,7 @@ # include "gc_local_alloc.h" # endif # include "private/gc_priv.h" /* For output, locking, MIN_WORDS, */ - /* and some statistics. */ + /* and some statistics. */ # include "private/gcconfig.h" # if defined(MSWIN32) || defined(MSWINCE) @@ -68,14 +68,14 @@ # include <pthread.h> # endif -# ifdef GC_WIN32_THREADS -# ifndef MSWINCE -# include <process.h> -# define GC_CreateThread(a,b,c,d,e,f) ((HANDLE) _beginthreadex(a,b,c,d,e,f)) -# endif +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) static CRITICAL_SECTION incr_cs; # endif +#ifdef __STDC__ +# include <stdarg.h> +#endif + /* Allocation Statistics */ int stubborn_count = 0; @@ -205,40 +205,6 @@ sexpr y; } # endif -sexpr small_cons (x, y) -sexpr x; -sexpr y; -{ - register sexpr r; - - collectable_count++; - r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); - if (r == 0) { - (void)GC_printf0("Out of memory\n"); - exit(1); - } - r -> sexpr_car = x; - r -> sexpr_cdr = y; - return(r); -} - -sexpr small_cons_uncollectable (x, y) -sexpr x; -sexpr y; -{ - register sexpr r; - - uncollectable_count++; - r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); - if (r == 0) { - (void)GC_printf0("Out of memory\n"); - exit(1); - } - r -> sexpr_car = x; - r -> sexpr_cdr = (sexpr)(~(unsigned long)y); - return(r); -} - #ifdef GC_GCJ_SUPPORT #include "gc_mark.h" @@ -279,6 +245,93 @@ struct GC_ms_entry * fake_gcj_mark_proc(word * addr, return(mark_stack_ptr); } +#endif /* GC_GCJ_SUPPORT */ + +#ifdef THREAD_LOCAL_ALLOC + +#undef GC_REDIRECT_TO_LOCAL +#include "gc_local_alloc.h" + +sexpr local_cons (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + register int *p; + register int my_extra = extra_count; + static int my_random = 0; + + collectable_count++; + r = (sexpr) GC_LOCAL_MALLOC(sizeof(struct SEXPR) + my_extra); +# ifdef GC_GCJ_SUPPORT + if (collectable_count % 2 == 0) { + r = (sexpr) GC_LOCAL_GCJ_MALLOC(sizeof(struct SEXPR) + sizeof(GC_word) + my_extra, + &gcj_class_struct1); + r = (sexpr) ((GC_word *)r + 1); + } +# endif + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + for (p = (int *)r; + ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { + if (*p) { + (void)GC_printf1("Found nonzero at 0x%lx (local) - allocator is broken\n", + (unsigned long)p); + FAIL; + } + *p = 13; + } + r -> sexpr_car = x; + r -> sexpr_cdr = y; + my_extra++; + if ( my_extra >= 5000 || my_extra == 200 && ++my_random % 37 != 0) { + extra_count = 0; + } else { + extra_count = my_extra; + } + return(r); +} +#endif /* THREAD_LOCAL_ALLOC */ + +sexpr small_cons (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + + collectable_count++; + r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + r -> sexpr_car = x; + r -> sexpr_cdr = y; + return(r); +} + +sexpr small_cons_uncollectable (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + + uncollectable_count++; + r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + r -> sexpr_car = x; + r -> sexpr_cdr = (sexpr)(~(unsigned long)y); + return(r); +} + +#ifdef GC_GCJ_SUPPORT + + sexpr gcj_cons(x, y) sexpr x; sexpr y; @@ -323,6 +376,9 @@ sexpr x, y; sexpr reverse(x) sexpr x; { +# ifdef TEST_WITH_SYSTEM_MALLOC + malloc(100000); +# endif return( reverse1(x, nil) ); } @@ -365,6 +421,35 @@ int low, up; } #endif /* GC_GCJ_SUPPORT */ +#ifdef THREAD_LOCAL_ALLOC +/* Return reverse(x) concatenated with y */ +sexpr local_reverse1(x, y) +sexpr x, y; +{ + if (is_nil(x)) { + return(y); + } else { + return( local_reverse1(cdr(x), local_cons(car(x), y)) ); + } +} + +sexpr local_reverse(x) +sexpr x; +{ + return( local_reverse1(x, nil) ); +} + +sexpr local_ints(low, up) +int low, up; +{ + if (low > up) { + return(nil); + } else { + return(local_cons(local_cons(INT_TO_SEXPR(low), nil), local_ints(low+1, up))); + } +} +#endif /* THREAD_LOCAL_ALLOC */ + /* To check uncollectable allocation we build lists with disguised cdr */ /* pointers, and make sure they don't go away. */ sexpr uncollectable_ints(low, up) @@ -435,25 +520,24 @@ sexpr x; } } -/* Try to force a to be strangely aligned */ -struct { - char dummy; - sexpr aa; -} A; -#define a A.aa - /* * A tiny list reversal test to check thread creation. */ #ifdef THREADS -# ifdef GC_WIN32_THREADS - unsigned __stdcall tiny_reverse_test(void * arg) +# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) + DWORD __stdcall tiny_reverse_test(void * arg) # else void * tiny_reverse_test(void * arg) # endif { - check_ints(reverse(reverse(ints(1,10))), 1, 10); + int i; + for (i = 0; i < 5; ++i) { + check_ints(reverse(reverse(ints(1,10))), 1, 10); +# ifdef THREAD_LOCAL_ALLOC + check_ints(local_reverse(local_reverse(local_ints(1,10))), 1, 10); +# endif + } return 0; } @@ -477,7 +561,7 @@ struct { # elif defined(GC_WIN32_THREADS) void fork_a_thread() { - unsigned thread_id; + DWORD thread_id; HANDLE h; h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id); if (h == (HANDLE)NULL) { @@ -506,6 +590,13 @@ struct { #endif +/* Try to force a to be strangely aligned */ +struct { + char dummy; + sexpr aa; +} A; +#define a A.aa + /* * Repeatedly reverse lists built out of very different sized cons cells. * Check that we didn't lose anything. @@ -563,7 +654,9 @@ void reverse_test() h = (sexpr *)GC_REALLOC((GC_PTR)h, 2000 * sizeof(sexpr)); # ifdef GC_GCJ_SUPPORT h[1999] = gcj_ints(1,200); - h[1999] = gcj_reverse(h[1999]); + for (i = 0; i < 51; ++i) + h[1999] = gcj_reverse(h[1999]); + /* Leave it as the reveresed list for now. */ # else h[1999] = ints(1,200); # endif @@ -594,6 +687,9 @@ void reverse_test() /* 49 integers. Thus this is thread safe without locks, */ /* assuming atomic pointer assignments. */ a = reverse(reverse(a)); +# ifdef THREAD_LOCAL_ALLOC + a = local_reverse(local_reverse(a)); +# endif # if !defined(AT_END) && !defined(THREADS) /* This is not thread safe, since realloc explicitly deallocates */ if (i & 1) { @@ -621,6 +717,8 @@ void reverse_test() b = c = 0; } +#undef a + /* * The rest of this builds balanced binary trees, checks that they don't * disappear, and tests finalization. @@ -655,15 +753,17 @@ VOLATILE int dropped_something = 0; # if defined(GC_PTHREADS) static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&incr_lock); -# endif -# ifdef GC_WIN32_THREADS - EnterCriticalSection(&incr_cs); +# else +# ifdef GC_WIN32_THREADS + EnterCriticalSection(&incr_cs); +# endif # endif if ((int)(GC_word)client_data != t -> level) { (void)GC_printf0("Wrong finalization data - collector is broken\n"); FAIL; } finalized_count++; + t -> level = -1; /* detect duplicate finalization immediately */ # ifdef PCR PCR_ThCrSec_ExitSys(); # endif @@ -672,9 +772,10 @@ VOLATILE int dropped_something = 0; # endif # if defined(GC_PTHREADS) pthread_mutex_unlock(&incr_lock); -# endif -# ifdef GC_WIN32_THREADS - LeaveCriticalSection(&incr_cs); +# else +# ifdef GC_WIN32_THREADS + LeaveCriticalSection(&incr_cs); +# endif # endif } @@ -746,9 +847,10 @@ int n; # if defined(GC_PTHREADS) static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&incr_lock); -# endif -# ifdef GC_WIN32_THREADS - EnterCriticalSection(&incr_cs); +# else +# ifdef GC_WIN32_THREADS + EnterCriticalSection(&incr_cs); +# endif # endif /* Losing a count here causes erroneous report of failure. */ finalizable_count++; @@ -761,9 +863,10 @@ int n; # endif # if defined(GC_PTHREADS) pthread_mutex_unlock(&incr_lock); -# endif -# ifdef GC_WIN32_THREADS - LeaveCriticalSection(&incr_cs); +# else +# ifdef GC_WIN32_THREADS + LeaveCriticalSection(&incr_cs); +# endif # endif } @@ -1068,6 +1171,25 @@ void fail_proc1(GC_PTR x) fail_count++; } +static void uniq(void *p, ...) { + va_list a; + void *q[100]; + int n = 0, i, j; + q[n++] = p; + va_start(a,p); + for (;(q[n] = va_arg(a,void *));n++) ; + va_end(a); + for (i=0; i<n; i++) + for (j=0; j<i; j++) + if (q[i] == q[j]) { + GC_printf0( + "Apparently failed to mark form some function arguments.\n" + "Perhaps GC_push_regs was configured incorrectly?\n" + ); + FAIL; + } +} + #endif /* __STDC__ */ #ifdef THREADS @@ -1148,6 +1270,19 @@ void run_one_test() "GC_is_valid_displacement produced incorrect result\n"); FAIL; } +# if defined(__STDC__) && !defined(MSWIN32) && !defined(MSWINCE) + /* Harder to test under Windows without a gc.h declaration. */ + { + size_t i; + extern void *GC_memalign(); + + GC_malloc(17); + for (i = sizeof(GC_word); i < 512; i *= 2) { + GC_word result = (GC_word) GC_memalign(i, 17); + if (result % i != 0 || result == 0 || *(int *)result != 0) FAIL; + } + } +# endif # ifndef ALL_INTERIOR_POINTERS # if defined(RS6000) || defined(POWERPC) if (!TEST_FAIL_COUNT(1)) { @@ -1168,6 +1303,21 @@ void run_one_test() GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *)); GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc); # endif + /* Make sure that fn arguments are visible to the collector. */ +# ifdef __STDC__ + uniq( + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + GC_malloc(12), GC_malloc(12), GC_malloc(12), + (GC_gcollect(),GC_malloc(12)), + (void *)0); +# endif /* Repeated list reversal test. */ reverse_test(); # ifdef PRINTSTATS @@ -1183,6 +1333,15 @@ void run_one_test() LOCK(); n_tests++; UNLOCK(); +# if defined(THREADS) && defined(HANDLE_FORK) + if (fork() == 0) { + GC_gcollect(); + tiny_reverse_test(0); + GC_gcollect(); + GC_printf0("Finished a child process\n"); + exit(0); + } +# endif /* GC_printf1("Finished %x\n", pthread_self()); */ } @@ -1202,7 +1361,7 @@ void check_heap_stats() } # else if (sizeof(char *) > 4) { - max_heap_sz = 15000000; + max_heap_sz = 19000000; } else { max_heap_sz = 11000000; } @@ -1212,7 +1371,7 @@ void check_heap_stats() # ifdef SAVE_CALL_CHAIN max_heap_sz *= 3; # ifdef SAVE_CALL_COUNT - max_heap_sz *= SAVE_CALL_COUNT/4; + max_heap_sz += max_heap_sz * SAVE_CALL_COUNT/4; # endif # endif # endif @@ -1327,6 +1486,10 @@ void SetMinimumStack(long minSize) # endif n_tests = 0; +#if defined(__APPLE__) && defined(__MACH__) + GC_INIT(); +#endif + # if defined(DJGPP) /* No good way to determine stack base from library; do it */ /* manually on this platform. */ @@ -1340,13 +1503,18 @@ void SetMinimumStack(long minSize) # endif GC_INIT(); /* Only needed if gc is dynamic library. */ (void) GC_set_warn_proc(warn_proc); -# if (defined(MPROTECT_VDB) || defined(PROC_VDB)) && !defined(MAKE_BACK_GRAPH) +# if (defined(MPROTECT_VDB) || defined(PROC_VDB)) \ + && !defined(MAKE_BACK_GRAPH) GC_enable_incremental(); (void) GC_printf0("Switched to incremental mode\n"); # if defined(MPROTECT_VDB) (void)GC_printf0("Emulating dirty bits with mprotect/signals\n"); # else +# ifdef PROC_VDB (void)GC_printf0("Reading dirty bits from /proc\n"); +# else + (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n"); +# endif # endif # endif run_one_test(); @@ -1378,9 +1546,9 @@ void SetMinimumStack(long minSize) } # endif -#ifdef GC_WIN32_THREADS +#if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) -unsigned __stdcall thr_run_one_test(void *arg) +DWORD __stdcall thr_run_one_test(void *arg) { run_one_test(); return 0; @@ -1412,7 +1580,7 @@ LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return ret; } -unsigned __stdcall thr_window(void *arg) +DWORD __stdcall thr_window(void *arg) { WNDCLASS win_class = { CS_NOCLOSE, @@ -1474,10 +1642,11 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) # ifdef MSWINCE HANDLE win_thr_h; # endif - unsigned thread_id; + DWORD thread_id; # if 0 GC_enable_incremental(); # endif + GC_init(); InitializeCriticalSection(&incr_cs); (void) GC_set_warn_proc(warn_proc); # ifdef MSWINCE @@ -1625,15 +1794,30 @@ main() (void)GC_printf0("pthread_default_stacksize_np failed.\n"); } # endif /* GC_HPUX_THREADS */ +# if defined(__APPLE__) && defined(__MACH__) + GC_INIT(); +# endif + pthread_attr_init(&attr); -# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) +# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) pthread_attr_setstacksize(&attr, 1000000); # endif n_tests = 0; -# if defined(MPROTECT_VDB) && !defined(PARALLEL_MARK) &&!defined(REDIRECT_MALLOC) && !defined(MAKE_BACK_GRAPH) +# if (defined(MPROTECT_VDB)) \ + && !defined(PARALLEL_MARK) &&!defined(REDIRECT_MALLOC) \ + && !defined(MAKE_BACK_GRAPH) GC_enable_incremental(); (void) GC_printf0("Switched to incremental mode\n"); - (void) GC_printf0("Emulating dirty bits with mprotect/signals\n"); +# if defined(MPROTECT_VDB) + (void)GC_printf0("Emulating dirty bits with mprotect/signals\n"); +# else +# ifdef PROC_VDB + (void)GC_printf0("Reading dirty bits from /proc\n"); +# else + (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n"); +# endif +# endif # endif (void) GC_set_warn_proc(warn_proc); if ((code = pthread_key_create(&fl_key, 0)) != 0) { diff --git a/boehm-gc/tests/test_cpp.cc b/boehm-gc/tests/test_cpp.cc index 75fd3668605..7e50e8aa0fd 100644 --- a/boehm-gc/tests/test_cpp.cc +++ b/boehm-gc/tests/test_cpp.cc @@ -28,7 +28,10 @@ few minutes to complete. #include <stdio.h> #include <stdlib.h> #include <string.h> -#ifdef __GNUC__ +#define USE_STD_ALLOCATOR +#ifdef USE_STD_ALLOCATOR +# include "gc_allocator.h" +#elif __GNUC__ # include "new_gc_alloc.h" #else # include "gc_alloc.h" @@ -189,25 +192,32 @@ int APIENTRY WinMain( # endif #endif + GC_init(); + # if defined(MACOS) // MacOS char* argv_[] = {"test_cpp", "10"}; // doesn't argv = argv_; // have a argc = sizeof(argv_)/sizeof(argv_[0]); // commandline # endif int i, iters, n; -# if !defined(MACOS) +# ifdef USE_STD_ALLOCATOR + int *x = gc_allocator<int>().allocate(1); + int **xptr = traceable_allocator<int *>().allocate(1); +# else # ifdef __GNUC__ - int *x = (int *)gc_alloc::allocate(sizeof(int)); + int *x = (int *)gc_alloc::allocate(sizeof(int)); # else - int *x = (int *)alloc::allocate(sizeof(int)); + int *x = (int *)alloc::allocate(sizeof(int)); # endif - - *x = 29; - x -= 3; +# endif + *x = 29; +# ifdef USE_STD_ALLOCATOR + *xptr = x; + x = 0; # endif if (argc != 2 || (0 >= (n = atoi( argv[ 1 ] )))) { - GC_printf0( "usage: test_cpp number-of-iterations\n" ); - exit( 1 );} + GC_printf0( "usage: test_cpp number-of-iterations\nAssuming 10 iters\n" ); + n = 10;} for (iters = 1; iters <= n; iters++) { GC_printf1( "Starting iteration %d\n", iters ); @@ -268,9 +278,10 @@ int APIENTRY WinMain( D::Test(); F::Test();} -# if !defined(__GNUC__) && !defined(MACOS) - my_assert (29 == x[3]); +# ifdef USE_STD_ALLOCATOR + x = *xptr; # endif + my_assert (29 == x[0]); GC_printf0( "The test appears to have succeeded.\n" ); return( 0 );} diff --git a/boehm-gc/threadlibs.c b/boehm-gc/threadlibs.c index 99968a94a6c..247d3c652ec 100644 --- a/boehm-gc/threadlibs.c +++ b/boehm-gc/threadlibs.c @@ -4,13 +4,14 @@ int main() { # if defined(GC_USE_LD_WRAP) - printf("-Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen " + printf("-Wl,--wrap -Wl,dlopen " "-Wl,--wrap -Wl,pthread_create -Wl,--wrap -Wl,pthread_join " "-Wl,--wrap -Wl,pthread_detach " "-Wl,--wrap -Wl,pthread_sigmask -Wl,--wrap -Wl,sleep\n"); # endif # if defined(GC_LINUX_THREADS) || defined(GC_IRIX_THREADS) \ - || defined(GC_FREEBSD_THREADS) || defined(GC_SOLARIS_PTHREADS) + || defined(GC_FREEBSD_THREADS) || defined(GC_SOLARIS_PTHREADS) \ + || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) printf("-lpthread\n"); # endif # if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) @@ -19,6 +20,17 @@ int main() # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) printf("-lthread -ldl\n"); # endif +# if defined(GC_WIN32_THREADS) && defined(CYGWIN32) + printf("-lpthread\n"); +# endif +# if defined(GC_OSF1_THREADS) + printf("-pthread -lrt"); /* DOB: must be -pthread, not -lpthread */ +# endif + /* You need GCC 3.0.3 to build this one! */ + /* DG/UX native gcc doesnt know what "-pthread" is */ +# if defined(GC_DGUX386_THREADS) + printf("-ldl -pthread\n"); +# endif return 0; } diff --git a/boehm-gc/typd_mlc.c b/boehm-gc/typd_mlc.c index 07717001e9d..a081c9797c3 100644 --- a/boehm-gc/typd_mlc.c +++ b/boehm-gc/typd_mlc.c @@ -437,6 +437,7 @@ void GC_init_explicit_typing() for (; bm != 0; bm >>= 1, current_p++) { if (bm & 1) { current = *current_p; + FIXUP_POINTER(current); if ((ptr_t)current >= least_ha && (ptr_t)current <= greatest_ha) { PUSH_CONTENTS((ptr_t)current, mark_stack_ptr, mark_stack_limit, current_p, exit1); @@ -674,9 +675,9 @@ DCL_LOCK_STATE; if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { FASTUNLOCK(); op = (ptr_t)GENERAL_MALLOC((word)lb, GC_explicit_kind); - if (0 == op) return(0); + if (0 == op) return 0; # ifdef MERGE_SIZES - lw = GC_size_map[lb]; /* May have been uninitialized. */ + lw = GC_size_map[lb]; /* May have been uninitialized. */ # endif } else { *opp = obj_link(op); @@ -720,7 +721,7 @@ DCL_LOCK_STATE; FASTUNLOCK(); op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); # ifdef MERGE_SIZES - lw = GC_size_map[lb]; /* May have been uninitialized. */ + lw = GC_size_map[lb]; /* May have been uninitialized. */ # endif } else { *opp = obj_link(op); diff --git a/boehm-gc/version.h b/boehm-gc/version.h index 96b7e64cbbe..9d858b2aaa6 100644 --- a/boehm-gc/version.h +++ b/boehm-gc/version.h @@ -1,11 +1,30 @@ -#define GC_VERSION_MAJOR 6 -#define GC_VERSION_MINOR 1 -#define GC_ALPHA_VERSION 3 +/* The version here should match that in configure/configure.in */ +/* Eventually this one may become unnecessary. For now we need */ +/* it to keep the old-style build process working. */ +#define GC_TMP_VERSION_MAJOR 6 +#define GC_TMP_VERSION_MINOR 3 +#define GC_TMP_ALPHA_VERSION 1 +#ifndef GC_NOT_ALPHA # define GC_NOT_ALPHA 0xff +#endif + +#if defined(GC_VERSION_MAJOR) +# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR || \ + GC_TMP_VERSION_MINOR != GC_VERSION_MINOR || \ + defined(GC_ALPHA_VERSION) != (GC_TMP_ALPHA_VERSION != GC_NOT_ALPHA) || \ + defined(GC_ALPHA_VERSION) && GC_TMP_ALPHA_VERSION != GC_ALPHA_VERSION +# error Inconsistent version info. Check version.h and configure.in. +# endif +#else +# define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +# define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +# define GC_ALPHA_VERSION GC_TMP_ALPHA_VERSION +#endif + #ifndef GC_NO_VERSION_VAR -unsigned GC_version = ((GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | GC_ALPHA_VERSION); +unsigned GC_version = ((GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | GC_TMP_ALPHA_VERSION); #endif /* GC_NO_VERSION_VAR */ diff --git a/boehm-gc/win32_threads.c b/boehm-gc/win32_threads.c index 954b18d7018..ff1d06625f8 100644 --- a/boehm-gc/win32_threads.c +++ b/boehm-gc/win32_threads.c @@ -1,13 +1,28 @@ -#if defined(GC_WIN32_THREADS) +#if defined(GC_WIN32_THREADS) #include "private/gc_priv.h" - -#if 0 -#define STRICT #include <windows.h> + +#ifdef CYGWIN32 +# include <errno.h> + + /* Cygwin-specific forward decls */ +# undef pthread_create +# undef pthread_sigmask +# undef pthread_join +# undef dlopen + +# define DEBUG_CYGWIN_THREADS 0 + + GC_bool GC_thr_initialized = FALSE; + void * GC_start_routine(void * arg); + void GC_thread_exit_proc(void *arg); + #endif -#define MAX_THREADS 64 +#ifndef MAX_THREADS +# define MAX_THREADS 64 +#endif struct thread_entry { LONG in_use; @@ -18,6 +33,12 @@ struct thread_entry { /* !in_use ==> stack == 0 */ CONTEXT context; GC_bool suspended; + +# ifdef CYGWIN32 + void *status; /* hold exit value until join in case it's a pointer */ + pthread_t pthread_id; +# endif + }; volatile GC_bool GC_please_stop = FALSE; @@ -29,6 +50,12 @@ void GC_push_thread_structures GC_PROTO((void)) /* Unlike the other threads implementations, the thread table here */ /* contains no pointers to the collectable heap. Thus we have */ /* no private structures we need to preserve. */ +# ifdef CYGWIN32 + { int i; /* pthreads may keep a pointer in the thread exit value */ + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1)); + } +# endif } void GC_stop_world() @@ -36,6 +63,10 @@ void GC_stop_world() DWORD thread_id = GetCurrentThreadId(); int i; +#ifdef CYGWIN32 + if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); +#endif + GC_please_stop = TRUE; for (i = 0; i < MAX_THREADS; i++) if (thread_table[i].stack != 0 @@ -53,11 +84,15 @@ void GC_stop_world() DWORD exitCode; if (GetExitCodeThread(thread_table[i].handle,&exitCode) && exitCode != STILL_ACTIVE) { - thread_table[i].stack = 0; + thread_table[i].stack = 0; /* prevent stack from being pushed */ +# ifndef CYGWIN32 + /* this breaks pthread_join on Cygwin, which is guaranteed to */ + /* only see user pthreads */ thread_table[i].in_use = FALSE; CloseHandle(thread_table[i].handle); BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT)); - continue; +# endif + continue; } if (SuspendThread(thread_table[i].handle) == (DWORD)-1) ABORT("SuspendThread failed"); @@ -335,9 +370,13 @@ void GC_get_next_stack(char *start, char **lo, char **hi) if (*lo < start) *lo = start; } -#if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) +#if !defined(CYGWIN32) -HANDLE WINAPI GC_CreateThread( +#if !defined(MSWINCE) && defined(GC_DLL) + +/* We register threads from DllMain */ + +GC_API HANDLE GC_CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) @@ -346,7 +385,10 @@ HANDLE WINAPI GC_CreateThread( lpParameter, dwCreationFlags, lpThreadId); } -#else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#else /* defined(MSWINCE) || !defined(GC_DLL)) */ + +/* We have no DllMain to take care of new threads. Thus we */ +/* must properly intercept thread creation. */ typedef struct { HANDLE child_ready_h, parent_ready_h; @@ -400,7 +442,17 @@ HANDLE WINAPI GC_CreateThread( /* fill in ID and handle; tell child this is done */ thread_table[i].id = *lpThreadId; - thread_table[i].handle = thread_h; + if (!DuplicateHandle(GetCurrentProcess(), + thread_h, + 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"); + } SetEvent (parent_ready_h); /* wait for child to fill in stack and copy args */ @@ -470,7 +522,9 @@ static DWORD WINAPI thread_start(LPVOID arg) return ret; } -#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ + +#endif /* !CYGWIN32 */ #ifdef MSWINCE @@ -527,13 +581,255 @@ DWORD WINAPI main_thread_start(LPVOID arg) LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); -#ifdef GC_DLL +/* threadAttach/threadDetach routines used by both CYGWIN and DLL + * implementation, since both recieve explicit notification on thread + * creation/destruction. + */ +static void threadAttach() { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preeemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* inititalization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + + /* The following should be a noop according to the win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# if defined(MPROTECT_VDB) + if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); +# endif + /* cast away volatile qualifier */ + for (i = 0; InterlockedExchange((LONG*)&thread_table[i].in_use,1) != 0; i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + if (i == MAX_THREADS - 1) + ABORT("too many threads"); + } + thread_table[i].id = GetCurrentThreadId(); +# ifdef CYGWIN32 + thread_table[i].pthread_id = pthread_self(); +# endif + if (!DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + (HANDLE*)&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"); + } + thread_table[i].stack = GC_get_stack_base(); + if (thread_table[i].stack == NULL) + ABORT("Failed to find stack base in threadAttach"); + /* If this thread is being created while we are trying to stop */ + /* the world, wait here. Hopefully this can't happen on any */ + /* systems that don't allow us to block here. */ + while (GC_please_stop) Sleep(20); +} + +static void threadDetach(DWORD thread_id) { + int i; + + LOCK(); + for (i = 0; + i < MAX_THREADS && + (!thread_table[i].in_use || thread_table[i].id != thread_id); + i++) {} + if (i >= MAX_THREADS ) { + WARN("thread %ld not found on detach", (GC_word)thread_id); + } else { + thread_table[i].stack = 0; + thread_table[i].in_use = FALSE; + CloseHandle(thread_table[i].handle); + /* cast away volatile qualifier */ + BZERO((void *)&thread_table[i].context, sizeof(CONTEXT)); + } + UNLOCK(); +} + +#ifdef CYGWIN32 + +/* Called by GC_init() - we hold the allocation lock. */ +void GC_thr_init() { + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + +#if 0 + /* this might already be handled in GC_init... */ + InitializeCriticalSection(&GC_allocate_ml); +#endif + + /* Add the initial thread, so we can stop it. */ + threadAttach(); +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; +}; + +int GC_pthread_join(pthread_t pthread_id, void **retval) { + int result; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(), + GetCurrentThreadId(), (int)pthread_id); +# endif + + /* Can't do any table lookups here, because thread being joined + might not have registered itself yet */ + + result = pthread_join(pthread_id, retval); + + LOCK(); + for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id; + i++) { + if (i == MAX_THREADS - 1) { + GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id); + ABORT("thread not found on detach"); + } + } + UNLOCK(); + threadDetach(thread_table[i].id); + +# if DEBUG_CYGWIN_THREADS + GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n", + (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id); +# endif + + return result; +} + +/* Cygwin-pthreads calls CreateThread internally, but it's not + * easily interceptible by us.. + * so intercept pthread_create instead + */ +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) { + int result; + struct start_info * si; + + if (!GC_is_initialized) GC_init(); + /* make sure GC is initialized (i.e. main thread is attached) */ + + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + si = GC_malloc_uncollectable(sizeof(struct start_info)); + if (0 == si) return(EAGAIN); + + si -> start_routine = start_routine; + si -> arg = arg; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(), + GetCurrentThreadId); +# endif + result = pthread_create(new_thread, attr, GC_start_routine, si); + + if (result) { /* failure */ + GC_free(si); + } + + return(result); +} + +void * GC_start_routine(void * arg) +{ + struct start_info * si = arg; + void * result; + void *(*start)(void *); + void *start_arg; + pthread_t pthread_id; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(), + GetCurrentThreadId()); +# endif + + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + threadAttach(); + UNLOCK(); + + start = si -> start_routine; + start_arg = si -> arg; + pthread_id = pthread_self(); + + GC_free(si); /* was allocated uncollectable */ + + pthread_cleanup_push(GC_thread_exit_proc, pthread_id); + result = (*start)(start_arg); + pthread_cleanup_pop(0); + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) returned from start routine.\n", + (int)pthread_self(),GetCurrentThreadId()); +# endif + + LOCK(); + for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { + if (i == MAX_THREADS - 1) + ABORT("thread not found on exit"); + } + thread_table[i].status = result; + UNLOCK(); + + return(result); +} + +void GC_thread_exit_proc(void *arg) +{ + pthread_t pthread_id = (pthread_t)arg; + int i; + +# if DEBUG_CYGWIN_THREADS + GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n", + (int)pthread_self(),GetCurrentThreadId()); +# endif + + LOCK(); + for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { + if (i == MAX_THREADS - 1) + ABORT("thread not found on exit"); + } + UNLOCK(); + +#if 0 + /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */ + thread_table[i].status = ??? +#endif +} + +/* nothing required here... */ +int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { + return pthread_sigmask(how, set, oset); +} +int GC_pthread_detach(pthread_t thread) { + return pthread_detach(thread); +} +#else /* !CYGWIN32 */ /* - * This isn't generally safe, since DllMain is not premptible. - * If another thread holds the lock while this runs we're in trouble. + * We avoid acquiring locks here, since this doesn't seem to be preemptable. * Pontus Rydin suggests wrapping the thread start routine instead. */ +#ifdef GC_DLL BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) { switch (reason) { @@ -542,75 +838,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) GC_init(); /* Force initialization before thread attach. */ /* fall through */ case DLL_THREAD_ATTACH: - { - int i; - /* It appears to be unsafe to acquire a lock here, since this */ - /* code is apparently not preeemptible on some systems. */ - /* (This is based on complaints, not on Microsoft's official */ - /* documentation, which says this should perform "only simple */ - /* inititalization tasks".) */ - /* Hence we make do with nonblocking synchronization. */ - - /* The following should be a noop according to the win32 */ - /* documentation. There is empirical evidence that it */ - /* isn't. - HB */ -# ifdef MPROTECT_VDB - if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); -# endif - - for (i = 0; - /* cast away volatile qualifier */ - InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0; - i++) { - /* Compare-and-swap would make this cleaner, but that's not */ - /* supported before Windows 98 and NT 4.0. In Windows 2000, */ - /* InterlockedExchange is supposed to be replaced by */ - /* InterlockedExchangePointer, but that's not really what I */ - /* want here. */ - if (i == MAX_THREADS - 1) - ABORT("too many threads"); - } - thread_table[i].id = GetCurrentThreadId(); - if (!DuplicateHandle(GetCurrentProcess(), - GetCurrentThread(), - GetCurrentProcess(), - /* cast away volatile qualifier */ - (HANDLE *) &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"); - } - thread_table[i].stack = GC_get_stack_base(); - /* If this thread is being created while we are trying to stop */ - /* the world, wait here. Hopefully this can't happen on any */ - /* systems that don't allow us to block here. */ - while (GC_please_stop) Sleep(20); - } + threadAttach(); break; + case DLL_THREAD_DETACH: - { - int i; - DWORD thread_id = GetCurrentThreadId(); - LOCK(); - for (i = 0; - i < MAX_THREADS && - (thread_table[i].stack == 0 || thread_table[i].id != thread_id); - i++) {} - if (i >= MAX_THREADS) { - WARN("thread %ld not found on detach", (GC_word)thread_id); - } else { - thread_table[i].stack = 0; - thread_table[i].in_use = FALSE; - CloseHandle(thread_table[i].handle); - /* cast away volatile qualifier */ - BZERO((void *) &thread_table[i].context, sizeof(CONTEXT)); - } - UNLOCK(); - } + threadDetach(GetCurrentThreadId()); break; + case DLL_PROCESS_DETACH: { int i; @@ -636,8 +870,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) } return TRUE; } - -# endif /* GC_DLL */ +#endif /* GC_DLL */ +#endif /* !CYGWIN32 */ # endif /* !MSWINCE */ |