summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2011-07-26 17:46:13 +0400
committerIvan Maidanski <ivmai@mail.ru>2011-07-26 17:46:13 +0400
commit1b159c5602737076356b862a8d2205bb13b34909 (patch)
tree5f2cfffa8f8ab056fdd895285801a006519be135
parent8c9e394bc270dbaa121f3f0af5a68c2876ab3fff (diff)
downloadbdwgc-1b159c5602737076356b862a8d2205bb13b34909.tar.gz
gc6.3alpha4 tarball importgc6_3alpha4
-rw-r--r--BCC_MAKEFILE2
-rw-r--r--Makefile2
-rw-r--r--Makefile.am3
-rw-r--r--Makefile.direct2
-rw-r--r--Makefile.dj28
-rw-r--r--Makefile.in3
-rw-r--r--NT_MAKEFILE4
-rw-r--r--NT_STATIC_THREADS_MAKEFILE59
-rw-r--r--aix_irix_threads.c5
-rwxr-xr-xconfigure24
-rw-r--r--configure.in6
-rw-r--r--cord/cordprnt.c2
-rw-r--r--cord/cordtest.c6
-rw-r--r--cord/cordxtra.c2
-rw-r--r--darwin_stop_world.c428
-rw-r--r--doc/README2
-rw-r--r--[-rwxr-xr-x]doc/README.arm.cross0
-rw-r--r--doc/README.changes99
-rw-r--r--doc/README.darwin34
-rw-r--r--doc/README.environment9
-rw-r--r--doc/README.win32101
-rw-r--r--doc/debugging.html13
-rw-r--r--doc/gc.man16
-rw-r--r--doc/gcinterface.html119
-rw-r--r--dyn_load.c66
-rw-r--r--if_not_there.c12
-rw-r--r--include/gc.h5
-rw-r--r--include/new_gc_alloc.h32
-rw-r--r--include/private/darwin_stop_world.h7
-rw-r--r--include/private/gc_locks.h12
-rw-r--r--include/private/gc_pmark.h7
-rw-r--r--include/private/gc_priv.h16
-rw-r--r--include/private/gcconfig.h61
-rw-r--r--include/private/pthread_support.h5
-rw-r--r--include/private/solaris_threads.h2
-rw-r--r--mach_dep.c9
-rw-r--r--malloc.c21
-rw-r--r--mallocx.c12
-rw-r--r--mark.c4
-rw-r--r--misc.c28
-rw-r--r--os_dep.c202
-rw-r--r--pthread_stop_world.c9
-rw-r--r--pthread_support.c28
-rw-r--r--tests/trace_test.c1
-rw-r--r--version.h2
-rwxr-xr-xwin32_threads.c794
46 files changed, 1452 insertions, 852 deletions
diff --git a/BCC_MAKEFILE b/BCC_MAKEFILE
index 3f86ed56..e21bc3d8 100644
--- a/BCC_MAKEFILE
+++ b/BCC_MAKEFILE
@@ -16,7 +16,7 @@ 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 -DNO_GETENV -DJAVA_FINALIZATION -DGC_OPERATOR_NEW_ARRAY
+defines= -DSILENT -DALL_INTERIOR_POINTERS -DUSE_GENERIC -DNO_GETENV -DJAVA_FINALIZATION -DGC_OPERATOR_NEW_ARRAY
.c.obj:
$(cc) @&&|
diff --git a/Makefile b/Makefile
index 52b9137a..21588470 100644
--- a/Makefile
+++ b/Makefile
@@ -331,7 +331,7 @@ GNU_BUILD_FILES= configure.in Makefile.am configure acinclude.m4 \
OTHER_MAKEFILES= OS2_MAKEFILE NT_MAKEFILE NT_THREADS_MAKEFILE gc.mak \
BCC_MAKEFILE EMX_MAKEFILE WCC_MAKEFILE Makefile.dj \
PCR-Makefile SMakefile.amiga Makefile.DLLs \
- digimars.mak Makefile.direct
+ digimars.mak Makefile.direct NT_STATIC_THREADS_MAKEFILE
# Makefile and Makefile.direct are copies of each other.
OTHER_FILES= Makefile setjmp_t.c callprocs pc_excludes \
diff --git a/Makefile.am b/Makefile.am
index 9c4abfbb..645d465b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -144,11 +144,10 @@ EXTRA_DIST += BCC_MAKEFILE NT_MAKEFILE NT_THREADS_MAKEFILE \
WCC_MAKEFILE
# files used by makefiles other than Makefile.am
-# (:FIXME: do we really need gcc_support.c ?)
#
EXTRA_DIST += add_gc_prefix.c gcname.c if_mach.c if_not_there.c \
hpux_test_and_clear.s pc_excludes gc.mak MacOS.c \
- MacProjects.sit.hqx gcc_support.c mach_dep.c setjmp_t.c \
+ MacProjects.sit.hqx mach_dep.c setjmp_t.c \
threadlibs.c AmigaOS.c \
Mac_files/datastart.c Mac_files/dataend.c \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h
diff --git a/Makefile.direct b/Makefile.direct
index 52b9137a..21588470 100644
--- a/Makefile.direct
+++ b/Makefile.direct
@@ -331,7 +331,7 @@ GNU_BUILD_FILES= configure.in Makefile.am configure acinclude.m4 \
OTHER_MAKEFILES= OS2_MAKEFILE NT_MAKEFILE NT_THREADS_MAKEFILE gc.mak \
BCC_MAKEFILE EMX_MAKEFILE WCC_MAKEFILE Makefile.dj \
PCR-Makefile SMakefile.amiga Makefile.DLLs \
- digimars.mak Makefile.direct
+ digimars.mak Makefile.direct NT_STATIC_THREADS_MAKEFILE
# Makefile and Makefile.direct are copies of each other.
OTHER_FILES= Makefile setjmp_t.c callprocs pc_excludes \
diff --git a/Makefile.dj b/Makefile.dj
index 6097293f..7757f151 100644
--- a/Makefile.dj
+++ b/Makefile.dj
@@ -29,7 +29,7 @@ EXE_SUFFIX=.exe
srcdir= .
VPATH= $(srcdir)
-CFLAGS= -O -I$(srcdir)/include -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION -DSILENT
+CFLAGS= -gstabs+ -O2 -I$(srcdir)/include -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION -DSILENT
# Setjmp_test may yield overly optimistic results when compiled
# without optimization.
@@ -157,9 +157,9 @@ 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 hpux_irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.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 typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.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 hpux_irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.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 typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.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/SCOPTIONS.amiga cord/SMakefile.amiga
@@ -181,7 +181,7 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \
include/leak_detector.h $(CORD_SRCS)
OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \
- README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \
+ README tests/test.c test_cpp.cc setjmp_t.c SMakefile.amiga \
SCoptions.amiga README.amiga README.win32 cord/README \
README.rs6000 README.QUICK callprocs pc_excludes \
barrett_diagram README.OS2 README.Mac MacProjects.sit.hqx \
@@ -206,7 +206,7 @@ CURSES= -lcurses -ltermlib
# the SHELL environment variable.
SHELL= /bin/sh
-SPECIALCFLAGS =
+SPECIALCFLAGS = -I$(srcdir)/include
# Alternative flags to the C compiler for mach_dep.c.
# Mach_dep.c often doesn't like optimization, and it's
# not time-critical anyway.
@@ -224,7 +224,7 @@ $(OBJS) test.o dyn_load.o dyn_load_sunos53.o: \
# options such as -DSILENT affects the size of GC_arrays,
# invalidating all .o files that rely on gc_priv.h
-mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h
+mark.o typd_mlc.o finalize.o: $(srcdir)/include/gc_mark.h
base_lib gc.a: $(OBJS) dyn_load.o $(UTILS)
echo > base_lib
@@ -366,11 +366,11 @@ clean:
rm -f threadlibs$(EXE_SUFFIX) cord/cordtest$(EXE_SUFFIX)
-rm -f *~
-gctest$(EXE_SUFFIX): test.o gc.a if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX)
+gctest$(EXE_SUFFIX): tests/test.o gc.a if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX)
rm -f gctest gctest$(EXE_SUFFIX)
- ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest test.o gc.a -lucb
- ./if_mach HP_PA "" $(CC) $(CFLAGS) -o gctest test.o gc.a -ldld
- ./if_not_there gctest$(EXE_SUFFIX) $(CC) $(CFLAGS) -o gctest$(EXE_SUFFIX) test.o gc.a
+ ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -lucb
+ ./if_mach HP_PA "" $(CC) $(CFLAGS) -o gctest tests/test.o gc.a -ldld
+ ./if_not_there gctest$(EXE_SUFFIX) $(CC) $(CFLAGS) -o gctest$(EXE_SUFFIX) tests/test.o gc.a
rm -f gctest
# If an optimized setjmp_test generates a segmentation fault,
@@ -407,8 +407,8 @@ gc.tar.Z: gc.tar
gc.tar.gz: gc.tar
gzip gc.tar
-lint: $(CSRCS) test.c
- lint -DLINT $(CSRCS) test.c | egrep -v "possible pointer alignment problem|abort|exit|sbrk|mprotect|syscall"
+lint: $(CSRCS) tests/test.c
+ lint -DLINT $(CSRCS) tests/test.c | egrep -v "possible pointer alignment problem|abort|exit|sbrk|mprotect|syscall"
# BTL: added to test shared library version of collector.
# Currently works only under SunOS5. Requires GC_INIT call from statically
@@ -417,8 +417,8 @@ ABSDIR = `pwd`
gctest_dyn_link: test.o libgc.so
$(CC) -L$(ABSDIR) -R$(ABSDIR) -o gctest_dyn_link test.o -lgc -ldl -lthread
-test_dll.o: test.c libgc_globals.h
- $(CC) $(CFLAGS) -DGC_USE_DLL -c test.c -o test_dll.o
+test_dll.o: tests/test.c libgc_globals.h
+ $(CC) $(CFLAGS) -DGC_USE_DLL -c tests/test.c -o test_dll.o
test_dll: test_dll.o libgc_dll.a libgc.dll
$(CC) test_dll.o -L$(ABSDIR) -lgc_dll -o test_dll
diff --git a/Makefile.in b/Makefile.in
index 3def876e..9c8ec411 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -131,7 +131,6 @@ SUBDIRS = doc include
# :GOTCHA: deliberately we do not include 'Makefile'
# files used by makefiles other than Makefile.am
-# (:FIXME: do we really need gcc_support.c ?)
#
# part of C++ interface
@@ -151,7 +150,7 @@ EXTRA_DIST = alpha_mach_dep.S mips_sgi_mach_dep.s sparc_mach_dep.S README.QUICK
WCC_MAKEFILE\
add_gc_prefix.c gcname.c if_mach.c if_not_there.c \
hpux_test_and_clear.s pc_excludes gc.mak MacOS.c \
- MacProjects.sit.hqx gcc_support.c mach_dep.c setjmp_t.c \
+ MacProjects.sit.hqx mach_dep.c setjmp_t.c \
threadlibs.c AmigaOS.c \
Mac_files/datastart.c Mac_files/dataend.c \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h\
diff --git a/NT_MAKEFILE b/NT_MAKEFILE
index 580e1425..002f85c7 100644
--- a/NT_MAKEFILE
+++ b/NT_MAKEFILE
@@ -10,10 +10,10 @@ OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_r
all: gctest.exe cord\de.exe test_cpp.exe
.c.obj:
- $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -D__STDC__ -DGC_NOT_DLL $*.c /Fo$*.obj
+ $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -D__STDC__ -DGC_NOT_DLL -DGC_BUILD $*.c /Fo$*.obj
.cpp.obj:
- $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -DGC_NOT_DLL $*.CPP /Fo$*.obj
+ $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_BUILD $*.CPP /Fo$*.obj
$(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h
diff --git a/NT_STATIC_THREADS_MAKEFILE b/NT_STATIC_THREADS_MAKEFILE
new file mode 100644
index 00000000..2442117e
--- /dev/null
+++ b/NT_STATIC_THREADS_MAKEFILE
@@ -0,0 +1,59 @@
+# Makefile for Windows NT. Assumes Microsoft compiler, and a single thread.
+# DLLs are included in the root set under NT, but not under win32S.
+# Use "nmake nodebug=1 all" for optimized versions of library, gctest and editor.
+
+CPU= i386
+!include <ntwin32.mak>
+
+OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj malloc.obj stubborn.obj dyn_load.obj typd_mlc.obj ptr_chck.obj gc_cpp.obj mallocx.obj win32_threads.obj
+
+all: gctest.exe cord\de.exe test_cpp.exe
+
+.c.obj:
+ $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -D__STDC__ -DGC_NOT_DLL -DGC_WIN32_THREADS $*.c /Fo$*.obj
+
+.cpp.obj:
+ $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DSILENT -DALL_INTERIOR_POINTERS -DGC_NOT_DLL $*.CPP -DGC_WIN32_THREADS /Fo$*.obj
+
+$(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h
+
+gc.lib: $(OBJS)
+ lib /MACHINE:i386 /out:gc.lib $(OBJS)
+# The original NT SDK used lib32 instead of lib
+
+gctest.exe: tests\test.obj gc.lib
+# The following works for win32 debugging. For win32s debugging use debugtype:coff
+# and add mapsympe line.
+# This produces a "GUI" applications that opens no windows and writes to the log file
+# "gc.log". This is done to make the result runnable under win32s.
+ $(link) -debug:full -debugtype:cv $(guiflags) -stack:131072 -out:$*.exe tests\test.obj $(guilibs) gc.lib
+# mapsympe -n -o gctest.sym gctest.exe
+
+cord\de_win.rbj: cord\de_win.res
+ cvtres -$(CPU) cord\de_win.res -o cord\de_win.rbj
+
+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_win.res: cord\de_win.rc cord\de_win.h cord\de_cmds.h
+ $(rc) $(rcvars) -r -fo cord\de_win.res $(cvars) cord\de_win.rc
+
+# Cord/de is a real win32 gui application.
+cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj cord\de_win.rbj gc.lib
+ $(link) -debug:full -debugtype:cv $(guiflags) -stack:16384 -out:cord\de.exe cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj cord\de_win.rbj gc.lib $(guilibs)
+
+gc_cpp.obj: include\gc_cpp.h include\gc.h
+
+gc_cpp.cpp: gc_cpp.cc
+ copy gc_cpp.cc gc_cpp.cpp
+
+test_cpp.cpp: tests\test_cpp.cc
+ copy tests\test_cpp.cc test_cpp.cpp
+
+# This generates the C++ test executable. The executable expects
+# a single numeric argument, which is the number of iterations.
+# The output appears in the file "gc.log".
+test_cpp.exe: test_cpp.obj include\gc_cpp.h include\gc.h gc.lib
+ $(link) -debug:full -debugtype:cv $(guiflags) -stack:16384 -out:test_cpp.exe test_cpp.obj gc.lib $(guilibs)
+
+
+
diff --git a/aix_irix_threads.c b/aix_irix_threads.c
index d8ac3454..4a5383ce 100644
--- a/aix_irix_threads.c
+++ b/aix_irix_threads.c
@@ -422,12 +422,7 @@ void GC_thr_init()
struct sigaction act;
if (GC_thr_initialized) return;
-#if 0
- /* unfortunately, GC_init_inner calls us without the lock, so
- * this assertion is not always true. */
- /* Why doesn't GC_init_inner hold the lock? - HB */
GC_ASSERT(I_HOLD_LOCK());
-#endif
GC_thr_initialized = TRUE;
#ifndef GC_AIX_THREADS
(void) sigaction(SIG_SUSPEND, 0, &act);
diff --git a/configure b/configure
index 011e747e..47982d93 100755
--- a/configure
+++ b/configure
@@ -1,7 +1,7 @@
#! /bin/sh
# From configure.in Revision: 1.2 .
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.53 for gc 6.3alpha1.
+# Generated by GNU Autoconf 2.53 for gc 6.3alpha4.
#
# Report bugs to <Hans.Boehm@hp.com>.
#
@@ -416,8 +416,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='gc'
PACKAGE_TARNAME='gc'
-PACKAGE_VERSION='6.3alpha1'
-PACKAGE_STRING='gc 6.3alpha1'
+PACKAGE_VERSION='6.3alpha4'
+PACKAGE_STRING='gc 6.3alpha4'
PACKAGE_BUGREPORT='Hans.Boehm@hp.com'
ac_unique_file="gcj_mlc.c"
@@ -930,7 +930,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures gc 6.3alpha1 to adapt to many kinds of systems.
+\`configure' configures gc 6.3alpha4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -997,7 +997,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of gc 6.3alpha1:";;
+ short | recursive ) echo "Configuration of gc 6.3alpha4:";;
esac
cat <<\_ACEOF
@@ -1106,7 +1106,7 @@ fi
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
-gc configure 6.3alpha1
+gc configure 6.3alpha4
generated by GNU Autoconf 2.53
Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
@@ -1121,7 +1121,7 @@ cat >&5 <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by gc $as_me 6.3alpha1, which was
+It was created by gc $as_me 6.3alpha4, which was
generated by GNU Autoconf 2.53. Invocation command line was
$ $0 $@
@@ -1782,7 +1782,7 @@ fi
# Define the identity of the package.
PACKAGE=gc
- VERSION=6.3alpha1
+ VERSION=6.3alpha4
cat >>confdefs.h <<_ACEOF
@@ -3912,7 +3912,7 @@ echo "$as_me: WARNING: OpenBSD/Alpha without dlopen(). Shared library support is
alpha*-*-linux*)
machdep="alpha_mach_dep.lo"
;;
- i?86-*-solaris2.[89]*)
+ i?86-*-solaris2.[89] | i?86-*-solaris2.1?)
cat >>confdefs.h <<\_ACEOF
#define SOLARIS25_PROC_VDB_BUG_FIXED 1
_ACEOF
@@ -3946,7 +3946,7 @@ _ACEOF
sparc-*-netbsd*)
machdep="sparc_netbsd_mach_dep.lo"
;;
- sparc-sun-solaris2.3*)
+ sparc-sun-solaris2.3)
machdep="sparc_mach_dep.lo"
cat >>confdefs.h <<\_ACEOF
#define SUNOS53_SHARED_LIB 1
@@ -9273,7 +9273,7 @@ _ASBOX
} >&5
cat >&5 <<_CSEOF
-This file was extended by gc $as_me 6.3alpha1, which was
+This file was extended by gc $as_me 6.3alpha4, which was
generated by GNU Autoconf 2.53. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -9330,7 +9330,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-gc config.status 6.3alpha1
+gc config.status 6.3alpha4
configured by $0, generated by GNU Autoconf 2.53,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
diff --git a/configure.in b/configure.in
index eeb231a4..43c0d142 100644
--- a/configure.in
+++ b/configure.in
@@ -17,7 +17,7 @@ dnl Process this file with autoconf to produce configure.
# Initialization
# ==============
-AC_INIT(gc,6.3alpha1,Hans.Boehm@hp.com)
+AC_INIT(gc,6.3alpha4,Hans.Boehm@hp.com)
## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)?
AC_CONFIG_SRCDIR(gcj_mlc.c)
AC_CANONICAL_TARGET
@@ -260,7 +260,7 @@ case "$host" in
alpha*-*-linux*)
machdep="alpha_mach_dep.lo"
;;
- i?86-*-solaris2.[[89]]*)
+ i?86-*-solaris2.[[89]] | i?86-*-solaris2.1?)
AC_DEFINE(SOLARIS25_PROC_VDB_BUG_FIXED)
;;
mipstx39-*-elf*)
@@ -282,7 +282,7 @@ case "$host" in
sparc-*-netbsd*)
machdep="sparc_netbsd_mach_dep.lo"
;;
- sparc-sun-solaris2.3*)
+ sparc-sun-solaris2.3)
machdep="sparc_mach_dep.lo"
AC_DEFINE(SUNOS53_SHARED_LIB)
;;
diff --git a/cord/cordprnt.c b/cord/cordprnt.c
index 6ecc00e8..ad937b02 100644
--- a/cord/cordprnt.c
+++ b/cord/cordprnt.c
@@ -261,7 +261,7 @@ int CORD_vsprintf(CORD * out, CORD format, va_list args)
# ifdef __va_copy
__va_copy(vsprintf_args, args);
# else
-# if defined(__GNUC__) /* and probably in other cases */
+# if defined(__GNUC__) && !defined(__DJGPP__) /* and probably in other cases */
va_copy(vsprintf_args, args);
# else
vsprintf_args = args;
diff --git a/cord/cordtest.c b/cord/cordtest.c
index d54c65fe..0d06562f 100644
--- a/cord/cordtest.c
+++ b/cord/cordtest.c
@@ -116,7 +116,7 @@ void test_basics()
void test_extras()
{
-# if defined(__OS2__)
+# if defined(__OS2__) || defined(__DJGPP__)
# define FNAME1 "tmp1"
# define FNAME2 "tmp2"
# elif defined(AMIGA)
@@ -162,6 +162,10 @@ void test_extras()
x = CORD_cat(x,x);
}
if ((f = fopen(FNAME2, "w")) == 0) ABORT("2nd open failed");
+# ifdef __DJGPP__
+ /* FIXME: DJGPP workaround. Why does this help? */
+ if (fflush(f) != 0) ABORT("fflush failed");
+# endif
if (CORD_put(x,f) == EOF) ABORT("CORD_put failed");
if (fclose(f) == EOF) ABORT("fclose failed");
w = CORD_from_file(f2 = fopen(FNAME2, "rb"));
diff --git a/cord/cordxtra.c b/cord/cordxtra.c
index a5be10de..fe59e702 100644
--- a/cord/cordxtra.c
+++ b/cord/cordxtra.c
@@ -385,7 +385,7 @@ size_t CORD_str(CORD x, size_t start, CORD s)
mask <<= 8;
mask |= 0xff;
s_buf <<= 8;
- s_buf |= s_start[i];
+ s_buf |= (unsigned char)s_start[i];
x_buf <<= 8;
x_buf |= CORD_pos_fetch(xpos);
CORD_next(xpos);
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
index bc2247fa..02d751ed 100644
--- a/darwin_stop_world.c
+++ b/darwin_stop_world.c
@@ -2,8 +2,6 @@
# if defined(GC_DARWIN_THREADS)
-#define DEBUG_THREADS 0
-
/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
Page 49:
"The space beneath the stack pointer, where a new stack frame would normally
@@ -16,101 +14,241 @@
*/
#define PPC_RED_ZONE_SIZE 224
+/* Not 64-bit clean. Wait until Apple defines their 64-bit ABI */
+typedef struct StackFrame {
+ unsigned int savedSP;
+ unsigned int savedCR;
+ unsigned int savedLR;
+ unsigned int reserved[2];
+ unsigned int savedRTOC;
+} StackFrame;
+
+
+unsigned int FindTopOfStack(unsigned int stack_start) {
+ StackFrame *frame;
+
+ if (stack_start == 0) {
+ __asm__ volatile("lwz %0,0(r1)" : "=r" (frame));
+ } else {
+ frame = (StackFrame *)stack_start;
+ }
+
+# ifdef DEBUG_THREADS
+ /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
+# endif
+ do {
+ if (frame->savedSP == NULL) break;
+ /* if there are no more stack frames, stop */
+
+ frame = (StackFrame*)frame->savedSP;
+
+ /* we do these next two checks after going to the next frame
+ because the LR for the first stack frame in the loop
+ is not set up on purpose, so we shouldn't check it. */
+ if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
+ if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
+ } while (1);
+
+# ifdef DEBUG_THREADS
+ /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
+# endif
+
+ return (unsigned int)frame;
+}
+
void GC_push_all_stacks() {
int i;
kern_return_t r;
- GC_thread p;
- pthread_t me;
+ mach_port_t me;
ptr_t lo, hi;
-# if defined(POWERPC)
- ppc_thread_state_t state;
-# else
-# error FIXME for non-ppc OS X
-# endif
- mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
-
- me = pthread_self();
+ thread_act_array_t act_list = 0;
+ mach_msg_type_number_t listcount = 0;
+
+ me = mach_thread_self();
if (!GC_thr_initialized) GC_thr_init();
- for(i=0;i<THREAD_TABLE_SZ;i++) {
- for(p=GC_threads[i];p!=0;p=p->next) {
- if(p -> flags & FINISHED) continue;
- if(pthread_equal(p->id,me)) {
- lo = GC_approx_sp();
- } else {
- /* Get the thread state (registers, etc) */
- r = thread_get_state(
- p->stop_info.mach_thread,
- MACHINE_THREAD_STATE,
- (natural_t*)&state,
- &thread_state_count);
- if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
-
- #ifdef POWERPC
- lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
-
- GC_push_one(state.r0);
- GC_push_one(state.r2);
- GC_push_one(state.r3);
- GC_push_one(state.r4);
- GC_push_one(state.r5);
- GC_push_one(state.r6);
- GC_push_one(state.r7);
- GC_push_one(state.r8);
- GC_push_one(state.r9);
- GC_push_one(state.r10);
- GC_push_one(state.r11);
- GC_push_one(state.r12);
- GC_push_one(state.r13);
- GC_push_one(state.r14);
- GC_push_one(state.r15);
- GC_push_one(state.r16);
- GC_push_one(state.r17);
- GC_push_one(state.r18);
- GC_push_one(state.r19);
- GC_push_one(state.r20);
- GC_push_one(state.r21);
- GC_push_one(state.r22);
- GC_push_one(state.r23);
- GC_push_one(state.r24);
- GC_push_one(state.r25);
- GC_push_one(state.r26);
- GC_push_one(state.r27);
- GC_push_one(state.r28);
- GC_push_one(state.r29);
- GC_push_one(state.r30);
- GC_push_one(state.r31);
- #else
- # error FIXME for non-PPC darwin
- #endif /* !POWERPC */
- } /* p != me */
- if(p->flags & MAIN_THREAD)
- hi = GC_stackbottom;
- else
- hi = p->stack_end;
- #if DEBUG_THREADS
- GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
- (unsigned long) p -> id,
- (unsigned long) lo,
- (unsigned long) hi
- );
- #endif
- GC_push_all_stack(lo,hi);
- } /* for(p=GC_threads[i]...) */
- } /* for(i=0;i<THREAD_TABLE_SZ...) */
+ r = task_threads(current_task(), &act_list, &listcount);
+ if(r != KERN_SUCCESS) ABORT("task_threads failed");
+ for(i = 0; i < listcount; i++) {
+ thread_act_t thread = act_list[i];
+ if (thread == me) {
+ lo = GC_approx_sp();
+ hi = (ptr_t)FindTopOfStack(0);
+ } else {
+# ifdef POWERPC
+ ppc_thread_state_t info;
+ mach_msg_type_number_t outCount = THREAD_STATE_MAX;
+ r = thread_get_state(thread, MACHINE_THREAD_STATE,
+ (natural_t *)&info, &outCount);
+ if(r != KERN_SUCCESS) ABORT("task_get_state failed");
+
+ lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
+ hi = (ptr_t)FindTopOfStack(info.r1);
+
+ GC_push_one(info.r0);
+ GC_push_one(info.r2);
+ GC_push_one(info.r3);
+ GC_push_one(info.r4);
+ GC_push_one(info.r5);
+ GC_push_one(info.r6);
+ GC_push_one(info.r7);
+ GC_push_one(info.r8);
+ GC_push_one(info.r9);
+ GC_push_one(info.r10);
+ GC_push_one(info.r11);
+ GC_push_one(info.r12);
+ GC_push_one(info.r13);
+ GC_push_one(info.r14);
+ GC_push_one(info.r15);
+ GC_push_one(info.r16);
+ GC_push_one(info.r17);
+ GC_push_one(info.r18);
+ GC_push_one(info.r19);
+ GC_push_one(info.r20);
+ GC_push_one(info.r21);
+ GC_push_one(info.r22);
+ GC_push_one(info.r23);
+ GC_push_one(info.r24);
+ GC_push_one(info.r25);
+ GC_push_one(info.r26);
+ GC_push_one(info.r27);
+ GC_push_one(info.r28);
+ GC_push_one(info.r29);
+ GC_push_one(info.r30);
+ GC_push_one(info.r31);
+# else
+ /* FIXME: Remove after testing: */
+ WARN("This is completely untested and likely will not work\n", 0);
+ i386_thread_state_t info;
+ mach_msg_type_number_t outCount = THREAD_STATE_MAX;
+ r = thread_get_state(thread, MACHINE_THREAD_STATE,
+ (natural_t *)&info, &outCount);
+ if(r != KERN_SUCCESS) ABORT("task_get_state failed");
+
+ lo = (void*)info.esp;
+ hi = (ptr_t)FindTopOfStack(info.esp);
+
+ GC_push_one(info.eax);
+ GC_push_one(info.ebx);
+ GC_push_one(info.ecx);
+ GC_push_one(info.edx);
+ GC_push_one(info.edi);
+ GC_push_one(info.esi);
+ /* GC_push_one(info.ebp); */
+ /* GC_push_one(info.esp); */
+ GC_push_one(info.ss);
+ GC_push_one(info.eip);
+ GC_push_one(info.cs);
+ GC_push_one(info.ds);
+ GC_push_one(info.es);
+ GC_push_one(info.fs);
+ GC_push_one(info.gs);
+# endif /* !POWERPC */
+ }
+# if DEBUG_THREADS
+ GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
+ (unsigned long) thread,
+ (unsigned long) lo,
+ (unsigned long) hi
+ );
+# endif
+ GC_push_all_stack(lo, hi);
+ } /* for(p=GC_threads[i]...) */
+}
+
+static mach_port_t GC_mach_handler_thread;
+static int GC_use_mach_handler_thread = 0;
+
+static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
+static int GC_mach_threads_count;
+
+void GC_stop_init() {
+ int i;
+
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ GC_mach_threads[i].thread = 0;
+ GC_mach_threads[i].already_suspended = 0;
+ }
+ GC_mach_threads_count = 0;
+}
+
+/* returns true if there's a thread in act_list that wasn't in old_list */
+int GC_suspend_thread_list(thread_act_array_t act_list, int count,
+ thread_act_array_t old_list, int old_count) {
+ mach_port_t my_thread = mach_thread_self();
+ int i, j;
+
+ int changed = 0;
+
+ for(i = 0; i < count; i++) {
+ thread_act_t thread = act_list[i];
+# if DEBUG_THREADS
+ GC_printf1("Attempting to suspend thread %p\n", thread);
+# endif
+ /* find the current thread in the old list */
+ int found = 0;
+ for(j = 0; j < old_count; j++) {
+ thread_act_t old_thread = old_list[j];
+ if (old_thread == thread) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /* add it to the GC_mach_threads list */
+ GC_mach_threads[GC_mach_threads_count].thread = thread;
+ /* default is not suspended */
+ GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
+ changed = 1;
+ }
+
+ if (thread != my_thread &&
+ (!GC_use_mach_handler_thread
+ || (GC_use_mach_handler_thread
+ && GC_mach_handler_thread != thread))) {
+ struct thread_basic_info info;
+ mach_msg_type_number_t outCount = THREAD_INFO_MAX;
+ kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
+ (thread_info_t)&info, &outCount);
+ if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
+# if DEBUG_THREADS
+ GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
+# endif
+ if (!found) {
+ GC_mach_threads[GC_mach_threads_count].already_suspended =
+ (info.run_state != TH_STATE_RUNNING);
+ }
+ if (info.run_state != TH_STATE_RUNNING) continue;
+
+# if DEBUG_THREADS
+ GC_printf1("Suspending 0x%lx\n", thread);
+# endif
+ /* Suspend the thread */
+ kern_result = thread_suspend(thread);
+ if(kern_result != KERN_SUCCESS) ABORT("thread_suspend failed");
+ }
+ if (!found) GC_mach_threads_count++;
+ }
+ return changed;
}
+
/* Caller holds allocation lock. */
void GC_stop_world()
{
- int i;
+ int i, changes;
GC_thread p;
- pthread_t my_thread = pthread_self();
+ mach_port_t my_thread = mach_thread_self();
kern_return_t kern_result;
+ thread_act_array_t act_list, prev_list;
+ mach_msg_type_number_t listcount, prevcount;
- #if DEBUG_THREADS
- GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
- #endif
+# if DEBUG_THREADS
+ GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
+# endif
+
+ /* clear out the mach threads list table */
+ GC_stop_init();
/* Make sure all free list construction has stopped before we start. */
/* No new construction can start, since free list construction is */
@@ -122,43 +260,40 @@ void GC_stop_world()
/* 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) continue;
- if (p -> flags & FINISHED) continue;
- if (p -> thread_blocked) /* Will wait */ continue;
-
- #if DEBUG_THREADS
- GC_printf1("Suspending thread 0x%lx\n", p -> id);
- #endif
-
- /* Suspend the thread */
- kern_result = thread_suspend(p->stop_info.mach_thread);
- if(kern_result != KERN_SUCCESS) ABORT("thread_suspend failed");
-
- /* This is only needed if we are modifying the threads
- state. thread_abort_safely should also be used
- if this code is ever added in again.
-
- kern_result = thread_abort(p->stop_info.mach_thread);
- if(kern_result != KERN_SUCCESS)
- ABORT("thread_abort failed (%ul)",kern_result);
- */
- }
- }
-
+ /* Loop stopping threads until you have gone over the whole list
+ twice without a new one appearing. thread_create() won't
+ return (and thus the thread stop) until the new thread
+ exists, so there is no window whereby you could stop a
+ thread, recognise it is stopped, but then have a new thread
+ it created before stopping show up later.
+ */
+
+ changes = 1;
+ prev_list = NULL;
+ prevcount = 0;
+ do {
+ int result;
+ kern_result = task_threads(current_task(), &act_list, &listcount);
+ result = GC_suspend_thread_list(act_list, listcount,
+ prev_list, prevcount);
+ changes = result;
+ prev_list = act_list;
+ prevcount = listcount;
+ } while (changes);
+
+
# ifdef MPROTECT_VDB
- if(GC_incremental) {
+ if(GC_incremental) {
extern void GC_mprotect_stop();
GC_mprotect_stop();
- }
+ }
# endif
# ifdef PARALLEL_MARK
GC_release_mark_lock();
# endif
#if DEBUG_THREADS
- GC_printf1("World stopped from 0x%lx\n", pthread_self());
+ GC_printf1("World stopped from 0x%lx\n", my_thread);
#endif
}
@@ -166,44 +301,63 @@ void GC_stop_world()
/* the world stopped. */
void GC_start_world()
{
- pthread_t my_thread = pthread_self();
- int i;
- GC_thread p;
- kern_return_t kern_result;
-
+ mach_port_t my_thread = mach_thread_self();
+ int i, j;
+ GC_thread p;
+ kern_return_t kern_result;
+ thread_act_array_t act_list;
+ mach_msg_type_number_t listcount;
+
# if DEBUG_THREADS
GC_printf0("World starting\n");
# endif
# ifdef MPROTECT_VDB
- if(GC_incremental) {
+ if(GC_incremental) {
extern void GC_mprotect_resume();
GC_mprotect_resume();
- }
+ }
# endif
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> id == my_thread) continue;
- if (p -> flags & FINISHED) continue;
- if (p -> thread_blocked) continue;
-
- #if DEBUG_THREADS
- GC_printf1("Resuming 0x%lx\n", p -> id);
- #endif
-
- /* Resume the thread */
- kern_result = thread_resume(p->stop_info.mach_thread);
- if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
- }
+ kern_result = task_threads(current_task(), &act_list, &listcount);
+ for(i = 0; i < listcount; i++) {
+ thread_act_t thread = act_list[i];
+ if (thread != my_thread &&
+ (!GC_use_mach_handler_thread ||
+ (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
+ for(j = 0; j < GC_mach_threads_count; j++) {
+ if (thread == GC_mach_threads[j].thread) {
+ if (GC_mach_threads[j].already_suspended) {
+# if DEBUG_THREADS
+ GC_printf1("Not resuming already suspended thread %p\n", thread);
+# endif
+ continue;
+ }
+ struct thread_basic_info info;
+ mach_msg_type_number_t outCount = THREAD_INFO_MAX;
+ kern_result = thread_info(thread, THREAD_BASIC_INFO,
+ (thread_info_t)&info, &outCount);
+ if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
+# if DEBUG_THREADS
+ GC_printf2("Thread state for 0x%lx = %d\n", thread,
+ info.run_state);
+ GC_printf1("Resuming 0x%lx\n", thread);
+# endif
+ /* Resume the thread */
+ kern_result = thread_resume(thread);
+ if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
+ }
+ }
+ }
}
- #if DEBUG_THREADS
- GC_printf0("World started\n");
- #endif
+# if DEBUG_THREADS
+ GC_printf0("World started\n");
+# endif
}
-void GC_stop_init() {
-
+void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
+ GC_mach_handler_thread = thread;
+ GC_use_mach_handler_thread = 1;
}
#endif
diff --git a/doc/README b/doc/README
index 29d954f0..d2b427e1 100644
--- a/doc/README
+++ b/doc/README
@@ -28,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.3alpha1 of a conservative garbage collector for C and C++.
+This is version 6.3alpha4 of a conservative garbage collector for C and C++.
You might find a more recent version of this at
diff --git a/doc/README.arm.cross b/doc/README.arm.cross
index 96744eda..96744eda 100755..100644
--- a/doc/README.arm.cross
+++ b/doc/README.arm.cross
diff --git a/doc/README.changes b/doc/README.changes
index 619ea2e4..89109687 100644
--- a/doc/README.changes
+++ b/doc/README.changes
@@ -1836,7 +1836,7 @@ Since 6.2alpha4:
libgccpp.
Since 6.2alpha5:
- - There was extra underscore in the name of GC_save_registers_in_stack
+ - There was an 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
@@ -1894,6 +1894,103 @@ Since 6.2:
(Thanks to Roger Sayle for the patch.)
- Applied more AIX threads patches from Scott Ananian.
+Since 6.3alpha1:
+ - Reenabled I_HOLD_LOCK assertion in aix_irix_threads.h.
+ - Put back the WINABI qualifier for GC_CreateThread. (Thanks to
+ Danny Smith for the patch. 6.3alpha1 had the qualifier in one place
+ but not elsewhere, which was clearly wrong.)
+ - Sometimes explicitly define __private_extern__ before DARWIN dyld.h
+ include. (Thanks to Andreas Tobker for postting the patch.)
+ - Included signal.h from pthread_support.c. Removed GC_looping_handler,
+ which was dead code.
+ - GC_find_start was misdeclared by gc_pmark.h if PRINT_BLACK_LIST was
+ defined. (Thanks to Glauco Masotti for testing and reporting this.)
+ Changed GC_find_start to never just return 0. According to its
+ comment it doesn't, and it's unclear that's correct.
+ - GC_alloc_large had several largely compensating bugs in the
+ computation of GC_words_wasted. (It was confused about bytes vs.
+ words in two places.)
+ - Integrated Slava Sysoltev's patch to support more recent versions of
+ the Intel compiler on IA64/Linux.
+ - Changed win32 spinlock initialization to conditionally set a spin count.
+ (Emmanual Stumpf pointed out that enabling this makes a large performance
+ difference on win32 multiprocessors.) Also cleaned up the win32 spinlock
+ initialization code a bit.
+ - Fixed thread support for HP/UX/IA64. The register backing store base for
+ the main thread was sometimes not set correctly. (Thanks to Laurent
+ Morichetti.)
+ - Added -DEMPTY_GETENV_RESULTS flag to work around Wine problem.
+ - Declare GC_stack_alloc and GC_stack_free in solaris_threads.h to
+ avoid 64-bit size mismatches. (Thanks to Bernie Solomon.)
+ - Fixed GC_generic_push_regs to avoid a potential and very unfortunate
+ tail call optimization. This could lead to prematurely reclaimed
+ objects on configurations that used the generic routine and the new
+ build infrastructure (which potentially optimizes mach_dep.c).
+ This was a serious bug, but it's unclear whether it has resulted in
+ any real failures.
+ - Fixed CORD_str to deal with signed characters. (Thanks to Alexandr
+ Petrosian for noticing the problem and supplying the patch.)
+ - Merged a couple of NOSYS/ECOS tests into os_dep.c from gcj. (Thanks
+ to Anthony Green.)
+ - Partially merged a win32 patch from Ben Hutchings, and substantially
+ revised other parts of win32_threads.c. It had several problems.
+ Under MinGW with a statically linked library, the main thread was
+ not registered. Cygwin detached threads leaked thread descriptors.
+ There were several race conditions. For now, unfortunately the
+ static threads limit remains, though we increased it, and made table
+ traversal cost depend on the actual thread count.
+ There is also still some code duplication with pthread_support.c.
+ (Thread descriptors did become much smaller, since Ben Hutchings
+ removed the thread context from them.)
+ - Integrated a Solaris configure.in patch from Rainer Orth.
+ - Added GC_IGNORE_FB and associated warning to very partially address
+ the issue of the collector treating a mapped frame buffer as part
+ of the root set. (Thanks to David Peroutka for providing some
+ insight. More would be helpful. Is there anything that can be used
+ to at least partially identify such memory segments?)
+
+Since 6.3alpha2:
+ - Removed -DSMALL_CONFIG from BCC_MAKEFILE.
+ - Changed macros to test for an ARM processor (Patch from Richard Earnshaw.)
+ - Mostly applied a DJGPP patch from Doug Kaufman. Especially Makefile.dj
+ had suffered from serious bit rot.
+ - Rewrote GC_apply_to_maps, eliminating an off-by-one subscript error,
+ and a call to alloca (for lcc compatibility).
+ - Changed use_MUNMAP behavior on posixy platforms to immediately remap
+ the memory with PROT_NONE instead of unmapping it. The latter risks
+ an intervening mmap grabbing the address space out from underneath us.
+ Updated this code to reflect a cleaner patch from Ulrich Drepper.
+ - Replaced _T with _Tp in new_gc_alloc.h to avoid a MACOS X conflict.
+ (Patch from Andrew Begel.)
+ - Dynamically choose whether or not lock should spin on win32. (Thanks
+ to Maurizio Vairani for the patch.) This may be a significant performance
+ improvement for win32.
+ - Fix Makefile.direct to actually include NT_STATIC_THREADS_MAKEFILE
+ in the distribution. (Again thanks to Maurizio Vairani.)
+ - Maybe_install_looping_handler() was accidentally exported, violating
+ our name space convention.
+ - Made os_dep.c use sigsetjmp and SA_NODEFER for NetBSD. (Thanks to
+ Christian Limpach.) (I generalized the patch to use sigsetjmp on all
+ UNIX_LIKE platforms, admittedly a slightly risky move. But it may avoid
+ similar problems on some other platforms. I also cleaned up the defn
+ of UNIX_LIKE a bit. - Hans)
+ - Integrated Andrew Begel's Darwin threads patch, adjusted according to
+ some of Fergus Hendersons's comments. (Patch didn't apply cleanly,
+ errors are possible.)
+ - Added another test or two for the Intel 8.0 compiler to avoid
+ confusing it with gcc. The single-threaded collector should now build
+ with icc, at least on ia64.
+
+Since 6.3alpha3:
+ - USE_MMAP was broken by confusion in the code dealing with USE_MMAP_ANON.
+ (This was pointed out, and fixes were suggested by several other people.)
+ - Darwin supprt was broken in alpha3 as a result of my misintegration of
+ Andrew Begel's patches. Fixed with another patch from Andrew Begel.
+ - A new sanity check in pthread_stop_world.c:GC_push_all_stacks() was
+ overly aggressive. We may collect from an unregistered thread during
+ thread creation. Fixed by explicitly checking for that case. (Added
+ GC_in_thread_creation.)
+
To do:
- A dynamic libgc.so references dlopen unconditionally, but doesn't link
against libdl.
diff --git a/doc/README.darwin b/doc/README.darwin
index 3cd1b818..72d60406 100644
--- a/doc/README.darwin
+++ b/doc/README.darwin
@@ -1,5 +1,5 @@
-Darwin/MacOSX Support - July 22, 2003
-====================================
+Darwin/MacOSX Support - December 16, 2003
+=========================================
Important Usage Notes
=====================
@@ -15,7 +15,7 @@ run and perhaps called GC_malloc(), create an initialization routine
for each library to call GC_init():
#include <gc/gc.h>
-void my_library_init() { GC_init(); }
+extern "C" void my_library_init() { GC_init(); }
Compile this code into a my_library_init.o, and link it into your
dylib. When you link the dylib, pass the -init argument with
@@ -34,6 +34,12 @@ work reliably with workarounds for a few possible bugs in place however
these workaround may not work correctly in all cases. There may also
be additional problems that I have not found.
+Thread-local GC allocation will not work with threads that are not
+created using the GC-provided override of pthread_create(). Threads
+created without the GC-provided pthread_create() do not have the
+necessary data structures in the GC to store this data.
+
+
Implementation Information
==========================
Darwin/MacOSX support is nearly complete. Thread support is reliable on
@@ -42,11 +48,27 @@ Darwin versions (MacOSX 10.1). Shared library support had also been
added and the gc can be run from a shared library. There is currently only
support for Darwin/PPC although adding x86 support should be trivial.
-Thread support is implemented in terms of mach thread_suspend and
+Thread support is implemented in terms of mach thread_suspend and
thread_resume calls. These provide a very clean interface to thread
suspension. This implementation doesn't rely on pthread_kill so the
-code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop the
-world is located in darwin_stop_world.c.
+code works on Darwin < 6.0 (MacOSX 10.1). All the code to stop and
+start the world is located in darwin_stop_world.c.
+
+Since not all uses of the GC enable clients to override pthread_create()
+before threads have been created, the code for stopping the world has
+been rewritten to look for threads using Mach kernel calls. Each
+thread identified in this way is suspended and resumed as above. In
+addition, since Mach kernel threads do not contain pointers to their
+stacks, a stack-walking function has been written to find the stack
+limits. Given an initial stack pointer (for the current thread, a
+pointer to a stack-allocated local variable will do; for a non-active
+thread, we grab the value of register 1 (on PowerPC)), it
+will walk the PPC Mach-O-ABI compliant stack chain until it reaches the
+top of the stack. This appears to work correctly for GCC-compiled C,
+C++, Objective-C, and Objective-C++ code, as well as for Java
+programs that use JNI. If you run code that does not follow the stack
+layout or stack pointer conventions laid out in the PPC Mach-O ABI,
+then this will likely crash the garbage collector.
The original incremental collector support unfortunatelly no longer works
on recent Darwin versions. It also relied on some undocumented kernel
diff --git a/doc/README.environment b/doc/README.environment
index d1f3b5c0..971cc4d4 100644
--- a/doc/README.environment
+++ b/doc/README.environment
@@ -1,6 +1,6 @@
The garbage collector looks at a number of environment variables which are
then used to affect its operation. These are examined only on Un*x-like
-platforms.
+platforms and win32.
GC_INITIAL_HEAP_SIZE=<bytes> - Initial heap size in bytes. May speed up
process start-up.
@@ -86,6 +86,13 @@ GC_RETRY_SIGNALS, GC_NO_RETRY_SIGNALS - Try to compensate for lost
was turned into a runtime flag to enable last-minute
work-arounds.
+GC_IGNORE_FB - (Currently win32 only.) Try to avoid treating a mapped
+ frame buffer as part of the root set. Certain (higher end?)
+ graphics cards seems to result in the frame buffer mapped
+ into the user address space as writable memory.
+ Unfortunately, there seems to be no systematic way to
+ identify such memory.
+
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:
diff --git a/doc/README.win32 b/doc/README.win32
index a40b375f..02e2149b 100644
--- a/doc/README.win32
+++ b/doc/README.win32
@@ -1,34 +1,11 @@
-The collector has at various times been compiled under Windows 95 & NT,
-with the original Microsoft SDK, with Visual C++ 2.0, 4.0, and 6, with
-the GNU win32 environment, with Borland 4.5, with Watcom C, and recently
+The collector has at various times been compiled under Windows 95 & later, NT,
+and XP, with the original Microsoft SDK, with Visual C++ 2.0, 4.0, and 6, with
+the GNU win32 tools, with Borland 4.5, with Watcom C, and recently
with the Digital Mars compiler. It is likely that some of these have been
broken in the meantime. Patches are appreciated.
-It runs under both win32s and win32, but with different semantics.
-Under win32, all writable pages outside of the heaps and stack are
-scanned for roots. Thus the collector sees pointers in DLL data
-segments. Under win32s, only the main data segment is scanned.
-(The main data segment should always be scanned. Under some
-versions of win32s, other regions may also be scanned.)
-Thus all accessible objects should be accessible from local variables
-or variables in the main data segment. Alternatively, other data
-segments (e.g. in DLLs) may be registered with the collector by
-calling GC_init() and then GC_register_root_section(a), where
-a is the address of some variable inside the data segment. (Duplicate
-registrations are ignored, but not terribly quickly.)
-
-(There are two reasons for this. We didn't want to see many 16:16
-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,
+For historical reasons,
+the collector test program "gctest" is linked as a GUI application,
but does not open any windows. Its output appears in the file
"gc.log". It may be started from the file manager. The hour glass
cursor may appear as long as it's running. If it is started from the
@@ -60,11 +37,23 @@ This is currently incompatible with -DUSE_MUNMAP. (Thanks to Jonathan
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.)
+Microsoft Tools
+---------------
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.
+For historical reasons,
+the collector test program "gctest" is linked as a GUI application,
+but does not open any windows. Its output appears in the file
+"gc.log". It may be started from the file manager. The hour glass
+cursor may appear as long as it's running. If it is started from the
+command line, it will usually run in the background. Wait a few
+minutes (a few seconds on a modern machine) before you check the output.
+You should see either a failure indication or a "Collector appears to
+work" message.
+
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
@@ -75,13 +64,22 @@ 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).
+GNU Tools
+---------
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
+if you want to package the collector as a DLL.
+[Is the following sentence obsolete? -HB] The GNU-win32 port is
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.
+The collector should also be buildable under Cygwin with either the
+old standard Makefile, or with the "configure;make" machinery.
+
+Borland Tools
+-------------
+[Rarely tested.]
For Borland tools, use BCC_MAKEFILE. Note that
Borland's compiler defaults to 1 byte alignment in structures (-a1),
whereas Visual C++ appears to default to 8 byte alignment (/Zp8).
@@ -97,6 +95,8 @@ version, change the line near the top. By default, it does not
require the assembler. If you do have the assembler, I recommend
removing the -DUSE_GENERIC.
+Incremental Collection
+----------------------
There is some support for incremental collection. This is
currently pretty simple-minded. Pages are protected. Protection
faults are caught by a handler installed at the bottom of the handler
@@ -112,6 +112,8 @@ is called.)
Note that incremental collection is disabled with -DSMALL_CONFIG.
+Threads
+-------
James Clark has contributed the necessary code to support win32 threads.
Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE
to build this version. Note that this requires some files whose names
@@ -133,7 +135,24 @@ may be other issues. If you need solid support for win32 threads, you
might check with Geodesic Systems. Their collector must be licensed,
but they have invested far more time in win32-specific issues.
-Hans
+Since 6.3alpha2, threads are also better supported in static library builds
+with Microsoft tools (use NT_STATIC_THREADS_MAKEFILE) and with the GNU
+tools. In all cases,the collector must be built with GC_WIN32_THREADS
+defined, even if the Cygwin pthreads interface is used.
+(NT_STATIC_THREADS_MAKEFILE does this implicitly. Under Cygwin,
+./configure --enable-threads=posix defines GC_WIN32_THREADS.) Threads must be
+created with GC_CreateThread. This can be accomplished by
+including gc.h and then calling CreateThread, which is redefined
+by gc.h.
+
+We strongly advise against using the TerminateThread() win32 API call,
+especially with the garbage collector. Any use is likely to provoke a
+crash in the GC, since it makes it impossible for the collector to
+correctly track threads.
+
+
+Watcom compiler
+---------------
Ivan V. Demakov's README for the Watcom port:
@@ -167,4 +186,26 @@ important, otherwise resulting programs will not run.
Ivan Demakov (email: ivan@tgrad.nsk.su)
+Win32S
+------
+
+[The following is probably obsolete. The win32s support is still in the
+collector, but I doubt anyone cares, or has tested it recently.]
+
+The collector runs under both win32s and win32, but with different semantics.
+Under win32, all writable pages outside of the heaps and stack are
+scanned for roots. Thus the collector sees pointers in DLL data
+segments. Under win32s, only the main data segment is scanned.
+(The main data segment should always be scanned. Under some
+versions of win32s, other regions may also be scanned.)
+Thus all accessible objects should be accessible from local variables
+or variables in the main data segment. Alternatively, other data
+segments (e.g. in DLLs) may be registered with the collector by
+calling GC_init() and then GC_register_root_section(a), where
+a is the address of some variable inside the data segment. (Duplicate
+registrations are ignored, but not terribly quickly.)
+
+(There are two reasons for this. We didn't want to see many 16:16
+pointers. And the VirtualQuery call has different semantics under
+the two systems, and under different versions of win32s.)
diff --git a/doc/debugging.html b/doc/debugging.html
index 22273fed..7c65f2bb 100644
--- a/doc/debugging.html
+++ b/doc/debugging.html
@@ -47,6 +47,10 @@ The garbage collector generates warning messages of the form
<PRE>
Needed to allocate blacklisted block at 0x...
</pre>
+or
+<PRE>
+Repeated allocation of very large block ...
+</pre>
when it needs to allocate a block at a location that it knows to be
referenced by a false pointer. These false pointers can be either permanent
(<I>e.g.</i> a static integer variable that never changes) or temporary.
@@ -123,7 +127,8 @@ is commonly caused by data structures that are no longer being used,
but were not cleared, or by caches growing without bounds.
<LI> Pointer misidentification. The garbage collector is interpreting
integers or other data as pointers and retaining the "referenced"
-objects.
+objects. A common symptom is that GC_dump() shows much of the heap
+as black-listed.
<LI> Heap fragmentation. This should never result in unbounded growth,
but it may account for larger heaps. This is most commonly caused
by allocation of large objects. On some platforms it can be reduced
@@ -180,6 +185,12 @@ primitives is <TT>gc_typed.h</tt>, or separate out the pointerfree component.
<LI> Consider using <TT>GC_malloc_ignore_off_page()</tt>
to allocate large objects. (See <TT>gc.h</tt> and above for details.
Large means &gt; 100K in most environments.)
+<LI> If your heap size is larger than 100MB or so, build the collector with
+-DLARGE_CONFIG. This allows the collector to keep more precise black-list
+information.
+<LI> If you are using heaps close to, or larger than, a gigabyte on a 32-bit
+machine, you may want to consider moving to a platform with 64-bit pointers.
+This is very likely to resolve any false pointer issues.
</ol>
<H2>Prematurely Reclaimed Objects</h2>
The usual symptom of this is a segmentation fault, or an obviously overwritten
diff --git a/doc/gc.man b/doc/gc.man
index 5409e706..48fee3a5 100644
--- a/doc/gc.man
+++ b/doc/gc.man
@@ -1,12 +1,14 @@
-.TH GC_MALLOC 1L "12 February 1996"
+.TH GC_MALLOC 1L "2 October 2003"
.SH NAME
GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement
.SH SYNOPSIS
#include "gc.h"
.br
-# define malloc(n) GC_malloc(n)
+void * GC_malloc(size_t size);
.br
-... malloc(...) ...
+void GC_free(void *ptr);
+.br
+void * GC_realloc(void *ptr, size_t size);
.br
.sp
cc ... gc.a
@@ -23,6 +25,11 @@ will attempt to reclaim inaccessible space automatically by invoking a conservat
GC_malloc
or friends.
.LP
+In most cases it is preferable to call the macros GC_MALLOC, GC_FREE, etc.
+instead of calling GC_malloc and friends directly. This allows debugging
+versions of the routines to be substituted by defining GC_DEBUG before
+including gc.h.
+.LP
See the documentation in the include file gc_cpp.h for an alternate, C++ specific interface to the garbage collector.
.LP
Unlike the standard implementations of malloc,
@@ -77,4 +84,5 @@ Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment",
The malloc(3) man page.
.LP
.SH AUTHOR
-Hans-J. Boehm (boehm@parc.xerox.com). Some of the code was written by others, most notably Alan Demers.
+Hans-J. Boehm (Hans.Boehm@hp.com).
+Some of the code was written by others, most notably Alan Demers.
diff --git a/doc/gcinterface.html b/doc/gcinterface.html
index 7b336ec8..1716514b 100644
--- a/doc/gcinterface.html
+++ b/doc/gcinterface.html
@@ -5,31 +5,33 @@
<BODY>
<H1>C Interface</h1>
On many platforms, a single-threaded garbage collector library can be built
-to act as a plug-in malloc replacement. (Build with -DREDIRECT_MALLOC=GC_malloc
--DIGNORE_FREE.) This is often the best way to deal with third-party libraries
-which leak or prematurely free objects. -DREDIRECT_MALLOC is intended
+to act as a plug-in malloc replacement.
+(Build with <TT>-DREDIRECT_MALLOC=GC_malloc -DIGNORE_FREE</tt>.)
+This is often the best way to deal with third-party libraries
+which leak or prematurely free objects. <TT>-DREDIRECT_MALLOC</tt> is intended
primarily as an easy way to adapt old code, not for new development.
<P>
New code should use the interface discussed below.
<P>
Code must be linked against the GC library. On most UNIX platforms,
-this will be gc.a.
+depending on how the collector is built, this will be <TT>gc.a</tt>
+or <TT>libgc.{a,so}</tt>.
<P>
The following describes the standard C interface to the garbage collector.
It is not a complete definition of the interface. It describes only the
most commonly used functionality, approximately in decreasing order of
-frequency of use. The description assumes an ANSI C compiler.
+frequency of use.
The full interface is described in
<A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>
or <TT>gc.h</tt> in the distribution.
<P>
-Clients should include gc.h.
+Clients should include <TT>gc.h</tt>.
<P>
In the case of multithreaded code,
-gc.h should be included after the threads header file, and
-after defining the appropriate GC_XXXX_THREADS macro.
-(For 6.2alpha4 and later, simply defining GC_THREADS should suffice.)
-Gc.h must be included
+<TT>gc.h</tt> should be included after the threads header file, and
+after defining the appropriate <TT>GC_</tt><I>XXXX</i><TT>_THREADS</tt> macro.
+(For 6.2alpha4 and later, simply defining <TT>GC_THREADS</tt> should suffice.)
+The header file <TT>gc.h</tt> must be included
in files that use either GC or threads primitives, since threads primitives
will be redefined to cooperate with the GC on many platforms.
<DL>
@@ -39,9 +41,10 @@ Allocates and clears <I>nbytes</i> of storage.
Requires (amortized) time proportional to <I>nbytes</i>.
The resulting object will be automatically deallocated when unreferenced.
References from objects allocated with the system malloc are usually not
-considered by the collector. (See GC_MALLOC_UNCOLLECTABLE, however.)
-GC_MALLOC is a macro which invokes GC_malloc by default or, if GC_DEBUG
-is defined before gc.h is included, a debugging version that checks
+considered by the collector. (See <TT>GC_MALLOC_UNCOLLECTABLE</tt>, however.)
+<TT>GC_MALLOC</tt> is a macro which invokes <TT>GC_malloc</tt> by default or,
+if <TT>GC_DEBUG</tt>
+is defined before <TT>gc.h</tt> is included, a debugging version that checks
occasionally for overwrite errors, and the like.
<DT> <B>void * GC_MALLOC_ATOMIC(size_t <I>nbytes</i>)</b>
<DD>
@@ -57,60 +60,70 @@ collector using the interface in
<A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_typedh.txt">gc_typed.h</a> in the distribution.
<DT> <B>void * GC_MALLOC_UNCOLLECTABLE(size_t <I>nbytes</i>)</b>
<DD>
-Identical to GC_MALLOC, except that the resulting object is not automatically
+Identical to <TT>GC_MALLOC</tt>,
+except that the resulting object is not automatically
deallocated. Unlike the system-provided malloc, the collector does
scan the object for pointers to garbage-collectable memory, even if the
block itself does not appear to be reachable. (Objects allocated in this way
are effectively treated as roots by the collector.)
-<DT> <B> void * GC_REALLOC(void *old, size_t new_size) </b>
+<DT> <B> void * GC_REALLOC(void *<I>old</i>, size_t <I>new_size</i>) </b>
<DD>
Allocate a new object of the indicated size and copy (a prefix of) the
old object into the new object. The old object is reused in place if
-convenient. If the original object was allocated with GC_malloc_atomic,
+convenient. If the original object was allocated with
+<TT>GC_MALLOC_ATOMIC</tt>,
the new object is subject to the same constraints. If it was allocated
as an uncollectable object, then the new object is uncollectable, and
the old object (if different) is deallocated.
-(Use GC_REALLOC with GC_MALLOC, etc.)
-<DT> <B> void GC_FREE(void *dead) </b>
+<DT> <B> void GC_FREE(void *<I>dead</i>) </b>
<DD>
Explicitly deallocate an object. Typically not useful for small
-collectable objects. (Use GC_FREE with GC_MALLOC, etc.)
+collectable objects.
<DT> <B> void * GC_MALLOC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
<DD>
<DT> <B> void * GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
<DD>
-Analogous to GC_MALLOC and GC_MALLOC_ATOMIC, except that the client
+Analogous to <TT>GC_MALLOC</tt> and <TT>GC_MALLOC_ATOMIC</tt>,
+except that the client
guarantees that as long
as the resulting object is of use, a pointer is maintained to someplace
inside the first 512 bytes of the object. This pointer should be declared
volatile to avoid interference from compiler optimizations.
(Other nonvolatile pointers to the object may exist as well.)
This is the
-preferred way to allocate objects that are likely to be > 100KBytes in size.
+preferred way to allocate objects that are likely to be &gt; 100KBytes in size.
It greatly reduces the risk that such objects will be accidentally retained
when they are no longer needed. Thus space usage may be significantly reduced.
+<DT> <B> void GC_INIT(void) </b>
+<DD>
+On some platforms, it is necessary to invoke this
+<I>from the main executable, not from a dynamic library,</i> before
+the initial invocation of a GC routine. It is recommended that this be done
+in portable code, though we try to ensure that it expands to a no-op
+on as many platforms as possible.
<DT> <B> void GC_gcollect(void) </b>
<DD>
Explicitly force a garbage collection.
<DT> <B> void GC_enable_incremental(void) </b>
<DD>
Cause the garbage collector to perform a small amount of work
-every few invocations of GC_malloc or the like, instead of performing
+every few invocations of <TT>GC_MALLOC</tt> or the like, instead of performing
an entire collection at once. This is likely to increase total
running time. It will improve response on a platform that either has
-suitable support in the garbage collector (Irix and most other Unix
+suitable support in the garbage collector (Linux and most Unix
versions, win32 if the collector was suitably built) or if "stubborn"
-allocation is used (see <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>).
+allocation is used (see
+<A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>).
On many platforms this interacts poorly with system calls
that write to the garbage collected heap.
-<DT> <B> GC_warn_proc GC_set_warn_proc(GC_warn_proc p) </b>
+<DT> <B> GC_warn_proc GC_set_warn_proc(GC_warn_proc <I>p</i>) </b>
<DD>
Replace the default procedure used by the collector to print warnings.
The collector
may otherwise write to sterr, most commonly because GC_malloc was used
in a situation in which GC_malloc_ignore_off_page would have been more
appropriate. See <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a> for details.
-<DT> <B> void GC_register_finalizer(...) </b>
+<DT> <B> void GC_REGISTER_FINALIZER(...) </b>
<DD>
Register a function to be called when an object becomes inaccessible.
This is often useful as a backup method for releasing system resources
@@ -123,7 +136,8 @@ See <A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/finalization.html">her
of the design.
<P>
Note that an object may become inaccessible before client code is done
-operating on its fields. Suitable synchronization is usually required.
+operating on objects referenced by its fields.
+Suitable synchronization is usually required.
See <A HREF="http://portal.acm.org/citation.cfm?doid=604131.604153">here</a>
or <A HREF="http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html">here</a>
for details.
@@ -131,13 +145,13 @@ for details.
<P>
If you are concerned with multiprocessor performance and scalability,
you should consider enabling and using thread local allocation (<I>e.g.</i>
-GC_LOCAL_MALLOC, see <TT>gc_local_alloc.h</tt>. If your platform
+<TT>GC_LOCAL_MALLOC</tt>, see <TT>gc_local_alloc.h</tt>. If your platform
supports it, you should build the collector with parallel marking support
-(-DPARALLEL_MARK, or --enable-parallel-mark).
+(<TT>-DPARALLEL_MARK</tt>, or <TT>--enable-parallel-mark</tt>).
<P>
If the collector is used in an environment in which pointer location
information for heap objects is easily available, this can be passed on
-to the colllector using the interfaces in either <TT>gc_typed.h</tt>
+to the collector using the interfaces in either <TT>gc_typed.h</tt>
or <TT>gc_gcj.h</tt>.
<P>
The collector distribution also includes a <B>string package</b> that takes
@@ -145,7 +159,31 @@ advantage of the collector. For details see
<A HREF="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/cordh.txt">cord.h</a>
<H1>C++ Interface</h1>
-There are three distinct ways to use the collector from C++:
+Usage of the collector from C++ is complicated by the fact that there
+are many "standard" ways to allocate memory in C++. The default ::new
+operator, default malloc, and default STL allocators allocate memory
+that is not garbage collected, and is not normally "traced" by the
+collector. This means that any pointers in memory allocated by these
+default allocators will not be seen by the collector. Garbage-collectable
+memory referenced only by pointers stored in such default-allocated
+objects is likely to be reclaimed prematurely by the collector.
+<P>
+It is the programmers responsibility to ensure that garbage-collectable
+memory is referenced by pointers stored in one of
+<UL>
+<LI> Program variables
+<LI> Garbage-collected objects
+<LI> Uncollected but "traceable" objects
+</ul>
+"Traceable" objects are not necessarily reclaimed by the collector,
+but are scanned for pointers to collectable objects.
+They are allocated by <TT>GC_MALLOC_UNCOLLECTABLE</tt>, as described
+above, and through some interfaces described below.
+<P>
+The easiest way to ensure that collectable objects are properly referenced
+is to allocate only collectable objects. This requires that every
+allocation go through one of the following interfaces, each one of
+which replaces a standard C++ allocation mechanism:
<DL>
<DT> <B> STL allocators </b>
<DD>
@@ -170,7 +208,7 @@ multiple threads, but are faster.
For an example, click <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_alloc_exC.txt">here</a>.
<P>
Recent versions of the collector also include a more standard-conforming
-allocator implemention in <TT>gc_allocator.h</tt>. It defines
+allocator implementation in <TT>gc_allocator.h</tt>. It defines
<UL>
<LI> traceable_allocator
<LI> gc_allocator
@@ -179,9 +217,15 @@ Again the former allocates uncollectable but traced memory.
This should work with any fully standard-conforming C++ compiler.
<DT> <B> Class inheritance based interface </b>
<DD>
-Users may include gc_cpp.h and then cause members of certain classes to
-be allocated in garbage collectable memory by inheriting from class gc.
+Users may include gc_cpp.h and then cause members of classes to
+be allocated in garbage collectable memory by having those classes
+inherit from class gc.
For details see <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_cpph.txt">gc_cpp.h</a>.
+<P>
+Linking against libgccpp in addition to the gc library overrides
+::new (and friends) to allocate traceable memory but uncollectable
+memory, making it safe to refer to collectable objects from the resulting
+memory.
<DT> <B> C interface </b>
<DD>
It is also possible to use the C interface from
@@ -189,14 +233,15 @@ It is also possible to use the C interface from
On platforms which use malloc to implement ::new, it should usually be possible
to use a version of the collector that has been compiled as a malloc
replacement. It is also possible to replace ::new and other allocation
-functions suitably.
+functions suitably, as is done by libgccpp.
<P>
Note that user-implemented small-block allocation often works poorly with
an underlying garbage-collected large block allocator, since the collector
has to view all objects accessible from the user's free list as reachable.
-This is likely to cause problems if GC_malloc is used with something like
+This is likely to cause problems if <TT>GC_MALLOC</tt>
+is used with something like
the original HP version of STL.
-This approach works with the SGI versions of the STL only if the
+This approach works well with the SGI versions of the STL only if the
<TT>malloc_alloc</tt> allocator is used.
</dl>
</body>
diff --git a/dyn_load.c b/dyn_load.c
index 162f7dcb..30a57555 100644
--- a/dyn_load.c
+++ b/dyn_load.c
@@ -727,6 +727,57 @@ void GC_register_dynamic_libraries()
# define HAVE_REGISTER_MAIN_STATIC_DATA
+ /* Should [start, start+len) be treated as a frame buffer */
+ /* and ignored? */
+ /* Unfortunately, we currently have no real way to tell */
+ /* automatically, and rely largely on user input. */
+ /* FIXME: If we had more data on this phenomenon (e.g. */
+ /* is start aligned to a MB multiple?) we should be able to */
+ /* do better. */
+ static GC_bool is_frame_buffer(ptr_t start, size_t len)
+ {
+ static GC_bool initialized = FALSE;
+ static GC_bool ignore_fb;
+# define MB (1024*1024)
+
+ switch(len) {
+ case 16*MB:
+ case 32*MB:
+ case 48*MB:
+ case 64*MB:
+ case 128*MB:
+ case 256*MB:
+ case 512*MB:
+ case 1024*MB:
+ break;
+ default:
+ return FALSE;
+ }
+ if (!initialized) {
+ ignore_fb = (0 != GETENV("GC_IGNORE_FB"));
+ if (!ignore_fb) {
+ WARN("Possible frame buffer mapping at 0x%lx: \n"
+ "\tConsider setting GC_IGNORE_FB to improve performance.\n",
+ start);
+ }
+ initialized = TRUE;
+ }
+ return ignore_fb;
+ }
+
+# ifdef DEBUG_VIRTUALQUERY
+ void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
+ {
+ GC_printf4("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
+ buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
+ buf -> RegionSize);
+ GC_printf4("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
+ "Type = %lx\n",
+ buf -> AllocationProtect, buf -> State, buf -> Protect,
+ buf -> Type);
+ }
+# endif /* DEBUG_VIRTUALQUERY */
+
void GC_register_dynamic_libraries()
{
MEMORY_BASIC_INFORMATION buf;
@@ -763,7 +814,11 @@ void GC_register_dynamic_libraries()
if (buf.State == MEM_COMMIT
&& (protect == PAGE_EXECUTE_READWRITE
|| protect == PAGE_READWRITE)
- && !GC_is_heap_base(buf.AllocationBase)) {
+ && !GC_is_heap_base(buf.AllocationBase)
+ && !is_frame_buffer(p, buf.RegionSize)) {
+# ifdef DEBUG_VIRTUALQUERY
+ GC_dump_meminfo(&buf);
+# endif
if ((char *)p != limit) {
GC_cond_add_roots(base, limit);
base = p;
@@ -981,7 +1036,14 @@ void GC_register_dynamic_libraries()
#ifdef DARWIN
-#include <mach-o/dyld.h>
+/* __private_extern__ hack required for pre-3.4 gcc versions. */
+#ifndef __private_extern__
+# define __private_extern__ extern
+# include <mach-o/dyld.h>
+# undef __private_extern__
+#else
+# include <mach-o/dyld.h>
+#endif
#include <mach-o/getsect.h>
/*#define DARWIN_DEBUG*/
diff --git a/if_not_there.c b/if_not_there.c
index 42dd8034..8691e925 100644
--- a/if_not_there.c
+++ b/if_not_there.c
@@ -4,6 +4,9 @@
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
+#ifdef __DJGPP__
+#include <dirent.h>
+#endif /* __DJGPP__ */
int main(argc, argv, envp)
int argc;
@@ -11,12 +14,21 @@ char ** argv;
char ** envp;
{
FILE * f;
+#ifdef __DJGPP__
+ DIR * d;
+#endif /* __DJGPP__ */
if (argc < 3) goto Usage;
if ((f = fopen(argv[1], "rb")) != 0
|| (f = fopen(argv[1], "r")) != 0) {
fclose(f);
return(0);
}
+#ifdef __DJGPP__
+ if ((d = opendir(argv[1])) != 0) {
+ closedir(d);
+ return(0);
+ }
+#endif
printf("^^^^Starting command^^^^\n");
fflush(stdout);
execvp(argv[2], argv+2);
diff --git a/include/gc.h b/include/gc.h
index 2d536ca3..1aacdb38 100644
--- a/include/gc.h
+++ b/include/gc.h
@@ -238,6 +238,7 @@ GC_API unsigned long GC_time_limit;
* 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.
+ * For win32 threads, it needs to be called explicitly.
*/
GC_API void GC_init GC_PROTO((void));
@@ -886,7 +887,7 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */
* and does then use DllMain to keep track of thread creations. But new code
* should be built to call GC_CreateThread.
*/
- GC_API HANDLE GC_CreateThread(
+ GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
@@ -927,7 +928,7 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */
*/
# define GC_INIT() { GC_add_roots(DATASTART, DATAEND); }
# else
-# if defined(__APPLE__) && defined(__MACH__)
+# if defined(__APPLE__) && defined(__MACH__) || defined(GC_WIN32_THREADS)
# define GC_INIT() { GC_init(); }
# else
# define GC_INIT()
diff --git a/include/new_gc_alloc.h b/include/new_gc_alloc.h
index 20a2fabf..f2219b77 100644
--- a/include/new_gc_alloc.h
+++ b/include/new_gc_alloc.h
@@ -393,12 +393,12 @@ __STL_END_NAMESPACE
__STL_BEGIN_NAMESPACE
-template <class _T>
-struct _Alloc_traits<_T, gc_alloc >
+template <class _Tp>
+struct _Alloc_traits<_Tp, gc_alloc >
{
static const bool _S_instanceless = true;
- typedef simple_alloc<_T, gc_alloc > _Alloc_type;
- typedef __allocator<_T, gc_alloc > allocator_type;
+ typedef simple_alloc<_Tp, gc_alloc > _Alloc_type;
+ typedef __allocator<_Tp, gc_alloc > allocator_type;
};
inline bool operator==(const gc_alloc&,
@@ -413,12 +413,12 @@ inline bool operator!=(const gc_alloc&,
return false;
}
-template <class _T>
-struct _Alloc_traits<_T, single_client_gc_alloc >
+template <class _Tp>
+struct _Alloc_traits<_Tp, single_client_gc_alloc >
{
static const bool _S_instanceless = true;
- typedef simple_alloc<_T, single_client_gc_alloc > _Alloc_type;
- typedef __allocator<_T, single_client_gc_alloc > allocator_type;
+ typedef simple_alloc<_Tp, single_client_gc_alloc > _Alloc_type;
+ typedef __allocator<_Tp, single_client_gc_alloc > allocator_type;
};
inline bool operator==(const single_client_gc_alloc&,
@@ -433,12 +433,12 @@ inline bool operator!=(const single_client_gc_alloc&,
return false;
}
-template <class _T>
-struct _Alloc_traits<_T, traceable_alloc >
+template <class _Tp>
+struct _Alloc_traits<_Tp, traceable_alloc >
{
static const bool _S_instanceless = true;
- typedef simple_alloc<_T, traceable_alloc > _Alloc_type;
- typedef __allocator<_T, traceable_alloc > allocator_type;
+ typedef simple_alloc<_Tp, traceable_alloc > _Alloc_type;
+ typedef __allocator<_Tp, traceable_alloc > allocator_type;
};
inline bool operator==(const traceable_alloc&,
@@ -453,12 +453,12 @@ inline bool operator!=(const traceable_alloc&,
return false;
}
-template <class _T>
-struct _Alloc_traits<_T, single_client_traceable_alloc >
+template <class _Tp>
+struct _Alloc_traits<_Tp, single_client_traceable_alloc >
{
static const bool _S_instanceless = true;
- typedef simple_alloc<_T, single_client_traceable_alloc > _Alloc_type;
- typedef __allocator<_T, single_client_traceable_alloc > allocator_type;
+ typedef simple_alloc<_Tp, single_client_traceable_alloc > _Alloc_type;
+ typedef __allocator<_Tp, single_client_traceable_alloc > allocator_type;
};
inline bool operator==(const single_client_traceable_alloc&,
diff --git a/include/private/darwin_stop_world.h b/include/private/darwin_stop_world.h
index 9924297e..f6f5314e 100644
--- a/include/private/darwin_stop_world.h
+++ b/include/private/darwin_stop_world.h
@@ -12,4 +12,11 @@ struct thread_stop_info {
mach_port_t mach_thread;
};
+struct GC_mach_thread {
+ thread_act_t thread;
+ int already_suspended;
+};
+
+void GC_darwin_register_mach_handler_thread(mach_port_t thread);
+
#endif
diff --git a/include/private/gc_locks.h b/include/private/gc_locks.h
index a079ebf8..df8043b6 100644
--- a/include/private/gc_locks.h
+++ b/include/private/gc_locks.h
@@ -100,17 +100,29 @@
# define GC_TEST_AND_SET_DEFINED
# endif
# if defined(IA64)
+# if defined(__INTEL_COMPILER)
+# include <ia64intrin.h>
+# endif
inline static int GC_test_and_set(volatile unsigned int *addr) {
long oldval, n = 1;
+# ifndef __INTEL_COMPILER
__asm__ __volatile__("xchg4 %0=%1,%2"
: "=r"(oldval), "=m"(*addr)
: "r"(n), "1"(*addr) : "memory");
+# else
+ oldval = _InterlockedExchange(addr, n);
+# endif
return oldval;
}
# define GC_TEST_AND_SET_DEFINED
/* Should this handle post-increment addressing?? */
inline static void GC_clear(volatile unsigned int *addr) {
+# ifndef __INTEL_COMPILER
__asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr) : : "memory");
+# else
+ // there is no st4 but I can use xchg I hope
+ _InterlockedExchange(addr, 0);
+# endif
}
# define GC_CLEAR_DEFINED
# endif
diff --git a/include/private/gc_pmark.h b/include/private/gc_pmark.h
index c1097382..51981914 100644
--- a/include/private/gc_pmark.h
+++ b/include/private/gc_pmark.h
@@ -135,12 +135,7 @@ extern mse * GC_mark_stack;
/* Return a pointer to within 1st page of object. */
/* Set *new_hdr_p to corr. hdr. */
#ifdef __STDC__
-# ifdef PRINT_BLACK_LIST
- ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p,
- word source);
-# else
- ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p);
-# endif
+ ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p);
#else
ptr_t GC_find_start();
#endif
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index eb477c03..4c74b0a0 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -448,7 +448,19 @@ extern GC_warn_proc GC_current_warn_proc;
/* Get environment entry */
#if !defined(NO_GETENV)
-# define GETENV(name) getenv(name)
+# if defined(EMPTY_GETENV_RESULTS)
+ /* Workaround for a reputed Wine bug. */
+ static inline char * fixed_getenv(const char *name)
+ {
+ char * tmp = getenv(name);
+ if (tmp == 0 || strlen(tmp) == 0)
+ return 0;
+ return tmp;
+ }
+# define GETENV(name) fixed_getenv(name)
+# else
+# define GETENV(name) getenv(name)
+# endif
#else
# define GETENV(name) 0
#endif
@@ -954,7 +966,7 @@ struct _GC_arrays {
# endif
# else
# ifdef SMALL_CONFIG
-# define MAX_HEAP_SECTS 128 /* Roughly 1GB */
+# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */
# else
# define MAX_HEAP_SECTS 384 /* Roughly 3GB */
# endif
diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h
index 51d8e8da..a3cc0959 100644
--- a/include/private/gcconfig.h
+++ b/include/private/gcconfig.h
@@ -60,9 +60,9 @@
# endif
/* Determine the machine type: */
-# if defined(__XSCALE__)
+# if defined(__arm__) || defined(__thumb__)
# define ARM32
-# if !defined(LINUX)
+# if !defined(LINUX) && !defined(NETBSD)
# define NOSYS
# define mach_type_known
# endif
@@ -588,7 +588,8 @@
* USE_GENERIC_PUSH_REGS the preferred approach for marking from registers.
*/
# if defined(__GNUC__) && ((__GNUC__ >= 3) || \
- (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) \
+ && !defined(__INTEL_COMPILER)
# define HAVE_BUILTIN_UNWIND_INIT
# endif
@@ -1042,7 +1043,7 @@
/* possibly because Linux threads is itself a malloc client */
/* and can't deal with the signals. */
# endif
-# define HEAP_START 0x1000
+# define HEAP_START (ptr_t)0x1000
/* This encourages mmap to give us low addresses, */
/* thus allowing the heap to grow to ~3GB */
# ifdef __ELF__
@@ -1564,6 +1565,7 @@
/* first putenv call. */
extern char ** environ;
# define STACKBOTTOM ((ptr_t)environ)
+# define HPUX_STACKBOTTOM
# define DYNAMIC_LOADING
# include <unistd.h>
# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
@@ -1573,9 +1575,9 @@
/* address minus one page. */
# define BACKING_STORE_DISPLACEMENT 0x1000000
# define BACKING_STORE_ALIGNMENT 0x1000
-# define BACKING_STORE_BASE \
- (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) \
- & ~(BACKING_STORE_ALIGNMENT - 1))
+ extern ptr_t GC_register_stackbottom;
+# define BACKING_STORE_BASE GC_register_stackbottom
+ /* Known to be wrong for recent HP/UX versions!!! */
# endif
# ifdef LINUX
# define CPP_WORDSZ 64
@@ -1593,8 +1595,8 @@
/* constants: */
# define BACKING_STORE_ALIGNMENT 0x100000
# define BACKING_STORE_DISPLACEMENT 0x80000000
- extern char * GC_register_stackbottom;
-# define BACKING_STORE_BASE ((ptr_t)GC_register_stackbottom)
+ extern ptr_t GC_register_stackbottom;
+# define BACKING_STORE_BASE GC_register_stackbottom
# define SEARCH_FOR_DATA_START
# ifdef __GNUC__
# define DYNAMIC_LOADING
@@ -1608,12 +1610,22 @@
extern int _end[];
# define DATAEND (_end)
# ifdef __GNUC__
-# define PREFETCH(x) \
- __asm__ (" lfetch [%0]": : "r"((void *)(x)))
-# define PREFETCH_FOR_WRITE(x) \
- __asm__ (" lfetch.excl [%0]": : "r"((void *)(x)))
-# define CLEAR_DOUBLE(x) \
- __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x)))
+# ifndef __INTEL_COMPILER
+# define PREFETCH(x) \
+ __asm__ (" lfetch [%0]": : "r"((void *)(x)))
+# define PREFETCH_FOR_WRITE(x) \
+ __asm__ (" lfetch.excl [%0]": : "r"((void *)(x)))
+# define CLEAR_DOUBLE(x) \
+ __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x)))
+# else
+# include <ia64intrin.h>
+# define PREFETCH(x) \
+ __lfetch(__lfhint_none, (void*)(x))
+# define PREFETCH_FOR_WRITE(x) \
+ __lfetch(__lfhint_nta, (void*)(x))
+# define CLEAR_DOUBLE(x) \
+ __stf_spill((void *)(x), 0)
+# endif // __INTEL_COMPILER
# endif
# endif
# endif
@@ -1691,8 +1703,13 @@
# 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
# define USE_GENERIC_PUSH_REGS
# endif
# ifdef LINUX
@@ -1805,6 +1822,12 @@
# endif
# endif
+#if defined(LINUX) && defined(USE_MMAP)
+ /* The kernel may do a somewhat better job merging mappings etc. */
+ /* with anonymous mappings. */
+# define USE_MMAP_ANON
+#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 */
@@ -1859,9 +1882,9 @@
# define SUNOS5SIGS
# endif
-# if defined(SVR4) || defined(LINUX) || defined(IRIX) || defined(HPUX) \
+# if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \
|| defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \
- || defined(DGUX) || defined(BSD) \
+ || defined(DGUX) || defined(BSD) || defined(SUNOS4) \
|| defined(_AIX) || defined(DARWIN) || defined(OSF1)
# define UNIX_LIKE /* Basic Unix-like system calls work. */
# endif
diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h
index 0ef917e7..b65a877e 100644
--- a/include/private/pthread_support.h
+++ b/include/private/pthread_support.h
@@ -93,5 +93,10 @@ GC_thread GC_lookup_thread(pthread_t id);
void GC_stop_init();
+extern GC_bool GC_in_thread_creation;
+ /* Currently in thread creation. Protected by allocation lock. */
+ /* While in thread creation, it is OK to run GC from unknown */
+ /* thread. */
+
#endif /* GC_PTHREADS && !GC_SOLARIS_THREADS.... etc */
#endif /* GC_PTHREAD_SUPPORT_H */
diff --git a/include/private/solaris_threads.h b/include/private/solaris_threads.h
index 7d49c298..b1f62620 100644
--- a/include/private/solaris_threads.h
+++ b/include/private/solaris_threads.h
@@ -30,6 +30,8 @@
extern size_t GC_min_stack_sz;
extern size_t GC_page_sz;
extern void GC_thr_init(void);
+ extern ptr_t GC_stack_alloc(size_t * stack_size);
+ extern void GC_stack_free(ptr_t stack, size_t size);
# endif /* GC_SOLARIS_THREADS */
diff --git a/mach_dep.c b/mach_dep.c
index 3dc5f0b2..327e11ca 100644
--- a/mach_dep.c
+++ b/mach_dep.c
@@ -405,6 +405,8 @@ void GC_generic_push_regs(cold_gc_frame)
ptr_t cold_gc_frame;
{
{
+ word dummy;
+
# ifdef HAVE_BUILTIN_UNWIND_INIT
/* This was suggested by Richard Henderson as the way to */
/* force callee-save registers and register windows onto */
@@ -429,6 +431,9 @@ ptr_t cold_gc_frame;
(void) setjmp(regs);
# else
(void) _setjmp(regs);
+ /* We don't want to mess with signals. According to */
+ /* SUSV3, setjmp() may or may not save signal mask. */
+ /* _setjmp won't, but is less portable. */
# endif
# endif /* !HAVE_BUILTIN_UNWIND_INIT */
# if (defined(SPARC) && !defined(HAVE_BUILTIN_UNWIND_INIT)) \
@@ -448,6 +453,10 @@ ptr_t cold_gc_frame;
}
# endif
GC_push_current_stack(cold_gc_frame);
+ /* Strongly discourage the compiler from treating the above */
+ /* as a tail-call, since that would pop the register */
+ /* contents before we get a chance to look at them. */
+ GC_noop1((word)(&dummy));
}
}
#endif /* USE_GENERIC_PUSH_REGS */
diff --git a/malloc.c b/malloc.c
index f5c8f06b..edcbbc5e 100644
--- a/malloc.c
+++ b/malloc.c
@@ -62,14 +62,14 @@ unsigned flags;
if (h == 0) {
result = 0;
} else {
- int total_bytes = BYTES_TO_WORDS(n_blocks * HBLKSIZE);
+ int total_bytes = n_blocks * HBLKSIZE;
if (n_blocks > 1) {
- GC_large_allocd_bytes += n_blocks * HBLKSIZE;
+ GC_large_allocd_bytes += total_bytes;
if (GC_large_allocd_bytes > GC_max_large_allocd_bytes)
GC_max_large_allocd_bytes = GC_large_allocd_bytes;
}
result = (ptr_t) (h -> hb_body);
- GC_words_wasted += total_bytes - lw;
+ GC_words_wasted += BYTES_TO_WORDS(total_bytes) - lw;
}
return result;
}
@@ -311,6 +311,19 @@ DCL_LOCK_STATE;
}
# ifdef REDIRECT_MALLOC
+
+/* Avoid unnecessary nested procedure calls here, by #defining some */
+/* malloc replacements. Otherwise we end up saving a */
+/* meaningless return address in the object. It also speeds things up, */
+/* but it is admittedly quite ugly. */
+# ifdef GC_ADD_CALLER
+# define RA GC_RETURN_ADDR,
+# else
+# define RA
+# endif
+# define GC_debug_malloc_replacement(lb) \
+ GC_debug_malloc(lb, RA "unknown", 0)
+
# ifdef __STDC__
GC_PTR malloc(size_t lb)
# else
@@ -363,6 +376,8 @@ DCL_LOCK_STATE;
/* and thus the right thing will happen even without overriding it. */
/* This seems to be true on most Linux systems. */
+#undef GC_debug_malloc_replacement
+
# endif /* REDIRECT_MALLOC */
/* Explicitly deallocate an object p. */
diff --git a/mallocx.c b/mallocx.c
index 06f45622..7c0bdcfe 100644
--- a/mallocx.c
+++ b/mallocx.c
@@ -147,6 +147,16 @@ int obj_kind;
# endif
# ifdef REDIRECT_REALLOC
+
+/* As with malloc, avoid two levels of extra calls here. */
+# ifdef GC_ADD_CALLER
+# define RA GC_RETURN_ADDR,
+# else
+# define RA
+# endif
+# define GC_debug_realloc_replacement(p, lb) \
+ GC_debug_realloc(p, lb, RA "unknown", 0)
+
# ifdef __STDC__
GC_PTR realloc(GC_PTR p, size_t lb)
# else
@@ -157,6 +167,8 @@ int obj_kind;
{
return(REDIRECT_REALLOC(p, lb));
}
+
+# undef GC_debug_realloc_replacement
# endif /* REDIRECT_REALLOC */
diff --git a/mark.c b/mark.c
index ca947290..4b654683 100644
--- a/mark.c
+++ b/mark.c
@@ -558,8 +558,8 @@ register hdr *hhdr, **new_hdr_p;
current = current - HBLKSIZE*(word)hhdr;
hhdr = HDR(current);
} while(IS_FORWARDING_ADDR_OR_NIL(hhdr));
- /* current points to the start of the large object */
- if (hhdr -> hb_flags & IGNORE_OFF_PAGE) return(0);
+ /* current points to near the start of the large object */
+ if (hhdr -> hb_flags & IGNORE_OFF_PAGE) return(orig);
if ((word *)orig - (word *)current
>= (ptrdiff_t)(hhdr->hb_sz)) {
/* Pointer past the end of the block */
diff --git a/misc.c b/misc.c
index 814fa41a..8abaf225 100644
--- a/misc.c
+++ b/misc.c
@@ -470,7 +470,17 @@ void GC_init()
DISABLE_SIGNALS();
#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
- if (!GC_is_initialized) InitializeCriticalSection(&GC_allocate_ml);
+ if (!GC_is_initialized) {
+ BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
+ HMODULE hK32 = GetModuleHandle("kernel32.dll");
+ if (hK32)
+ (FARPROC) pfn = GetProcAddress(hK32,
+ "InitializeCriticalSectionAndSpinCount");
+ if (pfn)
+ pfn(&GC_allocate_ml, 4000);
+ else
+ InitializeCriticalSection (&GC_allocate_ml);
+ }
#endif /* MSWIN32 */
LOCK();
@@ -537,7 +547,7 @@ int sig;
static GC_bool installed_looping_handler = FALSE;
-void maybe_install_looping_handler()
+static void maybe_install_looping_handler()
{
/* Install looping handler before the write fault handler, so we */
/* handle write faults correctly. */
@@ -638,7 +648,8 @@ void GC_init_inner()
# if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)
GC_init_netbsd_elf();
# endif
-# if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)
+# if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \
+ || defined(GC_WIN32_THREADS)
GC_thr_init();
# endif
# ifdef GC_SOLARIS_THREADS
@@ -649,14 +660,14 @@ void GC_init_inner()
|| defined(GC_SOLARIS_THREADS)
if (GC_stackbottom == 0) {
GC_stackbottom = GC_get_stack_base();
-# if defined(LINUX) && defined(IA64)
+# if (defined(LINUX) || defined(HPUX)) && defined(IA64)
GC_register_stackbottom = GC_get_register_stack_base();
# endif
} else {
-# if defined(LINUX) && defined(IA64)
+# if (defined(LINUX) || defined(HPUX)) && 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 */
+ /* The following may fail, since we may rely on */
/* alignment properties that may not hold with a user set */
/* GC_stackbottom. */
GC_register_stackbottom = GC_get_register_stack_base();
@@ -753,7 +764,7 @@ void GC_init_inner()
}
# endif /* !SMALL_CONFIG */
COND_DUMP;
- /* Get black list set up and/or incrmental GC started */
+ /* Get black list set up and/or incremental GC started */
if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner();
GC_is_initialized = TRUE;
# ifdef STUBBORN_ALLOC
@@ -1002,6 +1013,9 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
{
GC_warn_proc result;
+# ifdef GC_WIN32_THREADS
+ GC_ASSERT(GC_is_initialized);
+# endif
LOCK();
result = GC_current_warn_proc;
GC_current_warn_proc = p;
diff --git a/os_dep.c b/os_dep.c
index cb8d0b6b..019220fa 100644
--- a/os_dep.c
+++ b/os_dep.c
@@ -115,21 +115,23 @@
# include <sys/types.h>
# include <sys/mman.h>
# include <sys/stat.h>
+# include <errno.h>
#endif
#ifdef UNIX_LIKE
# include <fcntl.h>
-#endif
-
-#if defined(SUNOS5SIGS) || defined (HURD) || defined(LINUX)
# ifdef SUNOS5SIGS
# include <sys/siginfo.h>
# endif
-# undef setjmp
-# undef longjmp
-# define setjmp(env) sigsetjmp(env, 1)
-# define longjmp(env, val) siglongjmp(env, val)
-# define jmp_buf sigjmp_buf
+ /* Define SETJMP and friends to be the version that restores */
+ /* the signal mask. */
+# define SETJMP(env) sigsetjmp(env, 1)
+# define LONGJMP(env, val) siglongjmp(env, val)
+# define JMP_BUF sigjmp_buf
+#else
+# define SETJMP(env) setjmp(env)
+# define LONGJMP(env, val) longjmp(env, val)
+# define JMP_BUF jmp_buf
#endif
#ifdef DARWIN
@@ -183,45 +185,41 @@ ssize_t GC_repeat_read(int fd, char *buf, size_t count)
/*
* Apply fn to a buffer containing the contents of /proc/self/maps.
* Return the result of fn or, if we failed, 0.
+ * We currently do nothing to /proc/self/maps other than simply read
+ * it. This code could be simplified if we could determine its size
+ * ahead of time.
*/
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;
+ size_t maps_size = 4000; /* Initial guess. */
+ static char init_buf[1];
+ static char *maps_buf = init_buf;
+ static size_t maps_buf_sz = 1;
+
+ /* Read /proc/self/maps, growing maps_buf as necessary. */
+ /* Note that we may not allocate conventionally, and */
+ /* thus can't use stdio. */
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);
+ if (maps_size >= maps_buf_sz) {
+ /* Grow only by powers of 2, since we leak "too small" buffers. */
+ while (maps_size >= maps_buf_sz) maps_buf_sz *= 2;
+ maps_buf = GC_scratch_alloc(maps_buf_sz);
+ if (maps_buf == 0) return 0;
+ }
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';
+ maps_size = 0;
+ do {
+ result = GC_repeat_read(f, maps_buf, maps_buf_sz-1);
+ if (result <= 0) return 0;
+ maps_size += result;
+ } while (result == maps_buf_sz-1);
+ close(f);
+ } while (maps_size >= maps_buf_sz);
+ maps_buf[maps_size] = '\0';
/* Apply fn to result. */
return fn(maps_buf);
@@ -352,7 +350,8 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end,
# endif /* ECOS_GC_MEMORY_SIZE */
// setjmp() function, as described in ANSI para 7.6.1.1
-#define setjmp( __env__ ) hal_setjmp( __env__ )
+#undef SETJMP
+#define SETJMP( __env__ ) hal_setjmp( __env__ )
// FIXME: This is a simple way of allocating memory which is
// compatible with ECOS early releases. Later releases use a more
@@ -688,9 +687,11 @@ ptr_t GC_get_stack_base()
typedef void (*handler)();
# endif
-# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) || defined(HURD)
+# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
+ || defined(HURD) || defined(NETBSD)
static struct sigaction old_segv_act;
-# if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) || defined(HURD)
+# if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
+ || defined(HURD) || defined(NETBSD)
static struct sigaction old_bus_act;
# endif
# else
@@ -705,11 +706,11 @@ ptr_t GC_get_stack_base()
# endif
{
# if defined(SUNOS5SIGS) || defined(IRIX5) \
- || defined(OSF1) || defined(HURD)
+ || defined(OSF1) || defined(HURD) || defined(NETBSD)
struct sigaction act;
act.sa_handler = h;
-# ifdef SUNOS5SIGS
+# if defined(SUNOS5SIGS) || defined(NETBSD)
act.sa_flags = SA_RESTART | SA_NODEFER;
# else
act.sa_flags = SA_RESTART;
@@ -729,7 +730,7 @@ ptr_t GC_get_stack_base()
# else
(void) sigaction(SIGSEGV, &act, &old_segv_act);
# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
- || defined(HPUX) || defined(HURD)
+ || defined(HPUX) || defined(HURD) || defined(NETBSD)
/* Under Irix 5.x or HP/UX, we may get SIGBUS. */
/* Pthreads doesn't exist under Irix 5.x, so we */
/* don't have to worry in the threads case. */
@@ -754,7 +755,7 @@ ptr_t GC_get_stack_base()
void GC_fault_handler(sig)
int sig;
{
- longjmp(GC_jmp_buf, 1);
+ LONGJMP(GC_jmp_buf, 1);
}
void GC_setup_temporary_fault_handler()
@@ -765,10 +766,10 @@ ptr_t GC_get_stack_base()
void GC_reset_fault_handler()
{
# if defined(SUNOS5SIGS) || defined(IRIX5) \
- || defined(OSF1) || defined(HURD)
+ || defined(OSF1) || defined(HURD) || defined(NETBSD)
(void) sigaction(SIGSEGV, &old_segv_act, 0);
# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
- || defined(HPUX) || defined(HURD)
+ || defined(HPUX) || defined(HURD) || defined(NETBSD)
(void) sigaction(SIGBUS, &old_bus_act, 0);
# endif
# else
@@ -794,7 +795,7 @@ ptr_t GC_get_stack_base()
GC_setup_temporary_fault_handler();
- if (setjmp(GC_jmp_buf) == 0) {
+ if (SETJMP(GC_jmp_buf) == 0) {
result = (ptr_t)(((word)(p))
& ~(MIN_PAGE_SIZE-1));
for (;;) {
@@ -821,6 +822,29 @@ ptr_t GC_get_stack_base()
}
#endif
+#ifdef HPUX_STACKBOTTOM
+
+#include <sys/param.h>
+#include <sys/pstat.h>
+
+ ptr_t GC_get_register_stack_base(void)
+ {
+ struct pst_vm_status vm_status;
+
+ int i = 0;
+ while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) {
+ if (vm_status.pst_type == PS_RSESTACK) {
+ return (ptr_t) vm_status.pst_vaddr;
+ }
+ }
+
+ /* old way to get the register stackbottom */
+ return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1)
+ & ~(BACKING_STORE_ALIGNMENT - 1));
+ }
+
+#endif /* HPUX_STACK_BOTTOM */
+
#ifdef LINUX_STACKBOTTOM
#include <sys/types.h>
@@ -963,7 +987,7 @@ ptr_t GC_get_stack_base()
#endif /* FREEBSD_STACKBOTTOM */
#if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \
- && !defined(MSWINCE) && !defined(OS2)
+ && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS)
ptr_t GC_get_stack_base()
{
@@ -1021,7 +1045,7 @@ ptr_t GC_get_stack_base()
# endif /* STACKBOTTOM */
}
-# endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS */
+# endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS */
/*
* Register static data segment(s) as roots.
@@ -1328,7 +1352,7 @@ int * etext_addr;
/* max_page_size to &etext if &etext is at a page boundary */
GC_setup_temporary_fault_handler();
- if (setjmp(GC_jmp_buf) == 0) {
+ if (SETJMP(GC_jmp_buf) == 0) {
/* Try writing to the address. */
*result = *result;
GC_reset_fault_handler();
@@ -1362,7 +1386,7 @@ int * etext_addr;
& ~((word)max_page_size - 1);
VOLATILE ptr_t result = (ptr_t)text_end;
GC_setup_temporary_fault_handler();
- if (setjmp(GC_jmp_buf) == 0) {
+ 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)
@@ -1512,6 +1536,18 @@ word bytes;
# define HEAP_START 0
#endif
+#ifdef USE_MMAP_ANON
+# define zero_fd -1
+# if defined(MAP_ANONYMOUS)
+# define OPT_MAP_ANON MAP_ANONYMOUS
+# else
+# define OPT_MAP_ANON MAP_ANON
+# endif
+#else
+ static int zero_fd;
+# define OPT_MAP_ANON 0
+#endif
+
ptr_t GC_unix_get_mem(bytes)
word bytes;
{
@@ -1520,23 +1556,17 @@ word bytes;
# 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);
+ zero_fd = open("/dev/zero", O_RDONLY);
+ fcntl(zero_fd, F_SETFD, FD_CLOEXEC);
initialized = TRUE;
}
# endif
if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg");
-# 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
+ result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
+ GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */);
if (result == MAP_FAILED) return(0);
last_addr = (ptr_t)result + bytes + GC_page_size - 1;
last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1));
@@ -1794,7 +1824,15 @@ void GC_unmap(ptr_t start, word bytes)
len -= free_len;
}
# else
- if (munmap(start_addr, len) != 0) ABORT("munmap failed");
+ /* We immediately remap it to prevent an intervening mmap from */
+ /* accidentally grabbing the same address space. */
+ {
+ void * result;
+ result = mmap(start_addr, len, PROT_NONE,
+ MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
+ zero_fd, 0/* offset */);
+ if (result != (void *)start_addr) ABORT("mmap(...PROT_NONE...) failed");
+ }
GC_unmapped_bytes += len;
# endif
}
@@ -1802,13 +1840,13 @@ void GC_unmap(ptr_t start, word bytes)
void GC_remap(ptr_t start, word bytes)
{
- static int zero_descr = -1;
ptr_t start_addr = GC_unmap_start(start, bytes);
ptr_t end_addr = GC_unmap_end(start, bytes);
word len = end_addr - start_addr;
- ptr_t result;
# if defined(MSWIN32) || defined(MSWINCE)
+ ptr_t result;
+
if (0 == start_addr) return;
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
@@ -1828,13 +1866,17 @@ void GC_remap(ptr_t start, word bytes)
len -= alloc_len;
}
# else
- if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR);
- fcntl(zero_descr, F_SETFD, FD_CLOEXEC);
+ /* It was already remapped with PROT_NONE. */
+ int result;
+
if (0 == start_addr) return;
- result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE, zero_descr, 0);
- if (result != start_addr) {
- ABORT("mmap remapping failed");
+ result = mprotect(start_addr, len,
+ PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
+ if (result != 0) {
+ GC_err_printf3(
+ "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
+ start_addr, len, errno);
+ ABORT("Mprotect remapping failed");
}
GC_unmapped_bytes -= len;
# endif
@@ -3431,6 +3473,8 @@ static void *GC_mprotect_thread(void *arg) {
} msg;
mach_msg_id_t id;
+
+ GC_darwin_register_mach_handler_thread(mach_thread_self());
for(;;) {
r = mach_msg(
@@ -4023,6 +4067,9 @@ struct callinfo info[NFRAMES];
# define RESULT_SZ 200
static char result_buf[RESULT_SZ];
size_t result_len;
+ char *old_preload;
+# define PRELOAD_SZ 200
+ char preload_buf[PRELOAD_SZ];
static GC_bool found_exe_name = FALSE;
static GC_bool will_fail = FALSE;
int ret_code;
@@ -4044,7 +4091,20 @@ struct callinfo info[NFRAMES];
/* isn't time critical. */
sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name,
(unsigned long)info[i].ci_pc);
+ old_preload = getenv ("LD_PRELOAD");
+ if (0 != old_preload) {
+ if (strlen (old_preload) >= PRELOAD_SZ) {
+ will_fail = TRUE;
+ goto out;
+ }
+ strcpy (preload_buf, old_preload);
+ unsetenv ("LD_PRELOAD");
+ }
pipe = popen(cmd_buf, "r");
+ if (0 != old_preload
+ && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) {
+ WARN("Failed to reset LD_PRELOAD\n", 0);
+ }
if (pipe == NULL
|| (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe))
== 0) {
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 5dfd26d3..1cdda21a 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -186,6 +186,7 @@ void GC_restart_handler(int sig)
/* world is stopped. Should not fail if it isn't. */
void GC_push_all_stacks()
{
+ GC_bool found_me = FALSE;
int i;
GC_thread p;
ptr_t lo, hi;
@@ -206,6 +207,7 @@ void GC_push_all_stacks()
# else
lo = GC_approx_sp();
# endif
+ found_me = TRUE;
IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
} else {
lo = p -> stop_info.stack_ptr;
@@ -232,6 +234,11 @@ void GC_push_all_stacks()
GC_push_all_stack(lo, hi);
# endif
# ifdef IA64
+# if DEBUG_THREADS
+ GC_printf3("Reg stack for thread 0x%lx = [%lx,%lx)\n",
+ (unsigned long) p -> id,
+ (unsigned long) bs_lo, (unsigned long) bs_hi);
+# endif
if (pthread_equal(p -> id, me)) {
GC_push_all_eager(bs_lo, bs_hi);
} else {
@@ -240,6 +247,8 @@ void GC_push_all_stacks()
# endif
}
}
+ if (!found_me && !GC_in_thread_creation)
+ ABORT("Collecting from unknown thread.");
}
/* There seems to be a very rare thread stopping problem. To help us */
diff --git a/pthread_support.c b/pthread_support.c
index b302817b..85962a91 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -99,6 +99,7 @@
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
+# include <signal.h>
#if defined(GC_DARWIN_THREADS)
# include "private/darwin_semaphore.h"
@@ -500,19 +501,6 @@ static __inline__ void start_mark_threads()
#endif /* !PARALLEL_MARK */
-/* 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;
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
@@ -622,7 +610,7 @@ void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
GC_INTERNAL_FREE(p);
}
-/* Return a GC_thread corresponding to a given thread_t. */
+/* Return a GC_thread corresponding to a given pthread_t. */
/* Returns 0 if it's not there. */
/* Caller holds allocation lock or otherwise inhibits */
/* updates. */
@@ -1115,6 +1103,8 @@ WRAP_FUNC(pthread_detach)(pthread_t thread)
return result;
}
+GC_bool GC_in_thread_creation = FALSE;
+
void * GC_start_routine(void * arg)
{
int dummy;
@@ -1132,7 +1122,9 @@ void * GC_start_routine(void * arg)
GC_printf1("sp = 0x%lx\n", (long) &arg);
# endif
LOCK();
+ GC_in_thread_creation = TRUE;
me = GC_new_thread(my_pthread);
+ GC_in_thread_creation = FALSE;
#ifdef GC_DARWIN_THREADS
me -> stop_info.mach_thread = mach_thread_self();
#else
@@ -1301,12 +1293,12 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread,
void GC_pause()
{
int i;
-# ifndef __GNUC__
- volatile word dummy = 0;
-# endif
+# if !defined(__GNUC__) || defined(__INTEL_COMPILER)
+ volatile word dummy = 0;
+# endif
for (i = 0; i < 10; ++i) {
-# ifdef __GNUC__
+# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
__asm__ __volatile__ (" " : : : "memory");
# else
/* Something that's unlikely to be optimized away. */
diff --git a/tests/trace_test.c b/tests/trace_test.c
index be9cb6ce..870e3872 100644
--- a/tests/trace_test.c
+++ b/tests/trace_test.c
@@ -10,6 +10,7 @@ struct treenode {
struct treenode * mktree(int i) {
struct treenode * r = GC_MALLOC(sizeof(struct treenode));
if (0 == i) return 0;
+ if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode));
r -> x = mktree(i-1);
r -> y = mktree(i-1);
return r;
diff --git a/version.h b/version.h
index 9d858b2a..f7ad22ad 100644
--- a/version.h
+++ b/version.h
@@ -3,7 +3,7 @@
/* 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
+#define GC_TMP_ALPHA_VERSION 4
#ifndef GC_NOT_ALPHA
# define GC_NOT_ALPHA 0xff
diff --git a/win32_threads.c b/win32_threads.c
index ff1d0662..7a1dbe27 100755
--- a/win32_threads.c
+++ b/win32_threads.c
@@ -14,36 +14,205 @@
# 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
+/* The type of the first argument to InterlockedExchange. */
+/* Documented to be LONG volatile *, but at least gcc likes */
+/* this better. */
+typedef LONG * IE_t;
+
#ifndef MAX_THREADS
-# define MAX_THREADS 64
+# define MAX_THREADS 256
+ /* FIXME: */
+ /* Things may get quite slow for large numbers of threads, */
+ /* since we look them up with sequential search. */
#endif
-struct thread_entry {
- LONG in_use;
+GC_bool GC_thr_initialized = FALSE;
+
+DWORD GC_main_thread = 0;
+
+struct GC_thread_Rep {
+ LONG in_use; /* Updated without lock. */
+ /* We assert that unused */
+ /* entries have invalid ids of */
+ /* zero and zero stack fields. */
DWORD id;
HANDLE handle;
- void *stack; /* The cold end of the stack. */
+ ptr_t stack_base; /* The cold end of the stack. */
/* 0 ==> entry not valid. */
- /* !in_use ==> stack == 0 */
- CONTEXT context;
+ /* !in_use ==> stack_base == 0 */
GC_bool suspended;
# ifdef CYGWIN32
void *status; /* hold exit value until join in case it's a pointer */
pthread_t pthread_id;
+ short flags; /* Protected by GC lock. */
+# define FINISHED 1 /* Thread has exited. */
+# define DETACHED 2 /* Thread is intended to be detached. */
# endif
-
};
+typedef volatile struct GC_thread_Rep * GC_thread;
+
+/*
+ * We generally assume that volatile ==> memory ordering, at least among
+ * volatiles.
+ */
+
volatile GC_bool GC_please_stop = FALSE;
-volatile struct thread_entry thread_table[MAX_THREADS];
+volatile struct GC_thread_Rep thread_table[MAX_THREADS];
+
+volatile LONG GC_max_thread_index = 0; /* Largest index in thread_table */
+ /* that was ever used. */
+
+extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
+
+/*
+ * This may be called from DllMain, and hence operates under unusual
+ * constraints.
+ */
+static GC_thread GC_new_thread(void) {
+ 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 */
+ /* initialization 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((IE_t)&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");
+ }
+ /* Update GC_max_thread_index if necessary. The following is safe, */
+ /* and unlike CompareExchange-based solutions seems to work on all */
+ /* Windows95 and later platforms. */
+ /* Unfortunately, GC_max_thread_index may be temporarily out of */
+ /* bounds, so readers have to compensate. */
+ while (i > GC_max_thread_index) {
+ InterlockedIncrement((IE_t)&GC_max_thread_index);
+ }
+ if (GC_max_thread_index >= MAX_THREADS) {
+ /* We overshot due to simultaneous increments. */
+ /* Setting it to MAX_THREADS-1 is always safe. */
+ GC_max_thread_index = MAX_THREADS - 1;
+ }
+
+# 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_base = GC_get_stack_base();
+ /* Up until this point, GC_psuh_all_stacks considers this thread */
+ /* invalid. */
+ if (thread_table[i].stack_base == NULL)
+ ABORT("Failed to find stack base in GC_new_thread");
+ /* Up until this point, this entry is viewed as reserved but invalid */
+ /* by GC_delete_thread. */
+ thread_table[i].id = GetCurrentThreadId();
+ /* 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);
+ return thread_table + i;
+}
+
+/*
+ * GC_max_thread_index may temporarily be larger than MAX_THREADS.
+ * To avoid subscript errors, we check on access.
+ */
+#ifdef __GNUC__
+__inline__
+#endif
+LONG GC_get_max_thread_index()
+{
+ LONG my_max = GC_max_thread_index;
+
+ if (my_max >= MAX_THREADS) return MAX_THREADS-1;
+ return my_max;
+}
+
+/* This is intended to be lock-free, though that */
+/* assumes that the CloseHandle becomes visible before the */
+/* in_use assignment. */
+static void GC_delete_gc_thread(GC_thread thr)
+{
+ CloseHandle(thr->handle);
+ /* cast away volatile qualifier */
+ thr->stack_base = 0;
+ thr->id = 0;
+# ifdef CYGWIN32
+ thr->pthread_id = 0;
+# endif /* CYGWIN32 */
+ thr->in_use = FALSE;
+}
+
+static void GC_delete_thread(DWORD thread_id) {
+ int i;
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0;
+ i <= my_max &&
+ (!thread_table[i].in_use || thread_table[i].id != thread_id);
+ /* Must still be in_use, since nobody else can store our thread_id. */
+ i++) {}
+ if (i > my_max) {
+ WARN("Removing nonexisiting thread %ld\n", (GC_word)thread_id);
+ } else {
+ GC_delete_gc_thread(thread_table+i);
+ }
+}
+
+
+#ifdef CYGWIN32
+
+/* Return a GC_thread corresponding to a given pthread_t. */
+/* Returns 0 if it's not there. */
+/* We assume that this is only called for pthread ids that */
+/* have not yet terminated or are still joinable. */
+static GC_thread GC_lookup_thread(pthread_t id)
+{
+ int i;
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0;
+ i <= my_max &&
+ (!thread_table[i].in_use || thread_table[i].pthread_id != id
+ || !thread_table[i].in_use);
+ /* Must still be in_use, since nobody else can store our thread_id. */
+ i++);
+ if (i > my_max) return 0;
+ return thread_table + i;
+}
+
+#endif /* CYGWIN32 */
void GC_push_thread_structures GC_PROTO((void))
{
@@ -52,8 +221,12 @@ void GC_push_thread_structures GC_PROTO((void))
/* 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));
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0; i <= my_max; i++)
+ if (thread_table[i].in_use)
+ GC_push_all((ptr_t)&(thread_table[i].status),
+ (ptr_t)(&(thread_table[i].status)+1));
}
# endif
}
@@ -63,13 +236,11 @@ 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
+ for (i = 0; i <= GC_get_max_thread_index(); i++)
+ if (thread_table[i].stack_base != 0
&& thread_table[i].id != thread_id) {
# ifdef MSWINCE
/* SuspendThread will fail if thread is running kernel code */
@@ -84,13 +255,12 @@ void GC_stop_world()
DWORD exitCode;
if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
exitCode != STILL_ACTIVE) {
- thread_table[i].stack = 0; /* prevent stack from being pushed */
+ thread_table[i].stack_base = 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));
# endif
continue;
}
@@ -105,8 +275,10 @@ void GC_start_world()
{
DWORD thread_id = GetCurrentThreadId();
int i;
- for (i = 0; i < MAX_THREADS; i++)
- if (thread_table[i].stack != 0 && thread_table[i].suspended
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0; i <= my_max; i++)
+ if (thread_table[i].stack_base != 0 && thread_table[i].suspended
&& thread_table[i].id != thread_id) {
if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
ABORT("ResumeThread failed");
@@ -122,9 +294,11 @@ ptr_t GC_current_stackbottom()
{
DWORD thread_id = GetCurrentThreadId();
int i;
- for (i = 0; i < MAX_THREADS; i++)
- if (thread_table[i].stack && thread_table[i].id == thread_id)
- return thread_table[i].stack;
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0; i <= my_max; i++)
+ if (thread_table[i].stack_base && thread_table[i].id == thread_id)
+ return thread_table[i].stack_base;
ABORT("no thread table entry for current thread");
}
# ifdef _MSC_VER
@@ -135,10 +309,10 @@ ptr_t GC_current_stackbottom()
/* The VirtualQuery calls below won't work properly on WinCE, but */
/* since each stack is restricted to an aligned 64K region of */
/* virtual memory we can just take the next lowest multiple of 64K. */
-# define GC_get_lo_stack_addr(s) \
+# define GC_get_stack_min(s) \
((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
# else
- static ptr_t GC_get_lo_stack_addr(ptr_t s)
+ static ptr_t GC_get_stack_min(ptr_t s)
{
ptr_t bottom;
MEMORY_BASIC_INFORMATION info;
@@ -155,197 +329,76 @@ ptr_t GC_current_stackbottom()
void GC_push_all_stacks()
{
DWORD thread_id = GetCurrentThreadId();
+ GC_bool found_me = FALSE;
int i;
- for (i = 0; i < MAX_THREADS; i++)
- if (thread_table[i].stack) {
- ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
- if (thread_table[i].id == thread_id)
- GC_push_all_stack((ptr_t)&i, thread_table[i].stack);
- else {
- thread_table[i].context.ContextFlags
- = (CONTEXT_INTEGER|CONTEXT_CONTROL);
- if (!GetThreadContext(thread_table[i].handle,
- /* cast away volatile qualifier */
- (LPCONTEXT)&thread_table[i].context))
+ int dummy;
+ ptr_t sp, stack_min;
+ GC_thread thread;
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0; i <= my_max; i++) {
+ thread = thread_table + i;
+ if (thread -> in_use && thread -> stack_base) {
+ if (thread -> id == thread_id) {
+ sp = (ptr_t) &dummy;
+ found_me = TRUE;
+ } else {
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
+ if (!GetThreadContext(thread_table[i].handle, &context))
ABORT("GetThreadContext failed");
-# ifdef I386
- GC_push_one ((word) thread_table[i].context.Edi);
- GC_push_one ((word) thread_table[i].context.Esi);
- GC_push_one ((word) thread_table[i].context.Ebp);
- GC_push_one ((word) thread_table[i].context.Ebx);
- GC_push_one ((word) thread_table[i].context.Edx);
- GC_push_one ((word) thread_table[i].context.Ecx);
- GC_push_one ((word) thread_table[i].context.Eax);
- if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
- || thread_table[i].context.Esp < (DWORD)bottom) {
- WARN("Thread stack pointer 0x%lx out of range, pushing everything",
- thread_table[i].context.Esp);
- GC_push_all_stack((char *) bottom, thread_table[i].stack);
- } else {
- GC_push_all_stack((char *) thread_table[i].context.Esp,
- thread_table[i].stack);
- }
-# else
-# ifdef ARM32
- if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
- || thread_table[i].context.Sp < (DWORD)bottom)
- ABORT("Thread stack pointer out of range");
- GC_push_one ((word) thread_table[i].context.R0);
- GC_push_one ((word) thread_table[i].context.R1);
- GC_push_one ((word) thread_table[i].context.R2);
- GC_push_one ((word) thread_table[i].context.R3);
- GC_push_one ((word) thread_table[i].context.R4);
- GC_push_one ((word) thread_table[i].context.R5);
- GC_push_one ((word) thread_table[i].context.R6);
- GC_push_one ((word) thread_table[i].context.R7);
- GC_push_one ((word) thread_table[i].context.R8);
- GC_push_one ((word) thread_table[i].context.R9);
- GC_push_one ((word) thread_table[i].context.R10);
- GC_push_one ((word) thread_table[i].context.R11);
- GC_push_one ((word) thread_table[i].context.R12);
- GC_push_all_stack((char *) thread_table[i].context.Sp,
- thread_table[i].stack);
-# else
-# ifdef SHx
- if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack
- || thread_table[i].context.R15 < (DWORD)bottom)
- ABORT("Thread stack pointer out of range");
- GC_push_one ((word) thread_table[i].context.R0);
- GC_push_one ((word) thread_table[i].context.R1);
- GC_push_one ((word) thread_table[i].context.R2);
- GC_push_one ((word) thread_table[i].context.R3);
- GC_push_one ((word) thread_table[i].context.R4);
- GC_push_one ((word) thread_table[i].context.R5);
- GC_push_one ((word) thread_table[i].context.R6);
- GC_push_one ((word) thread_table[i].context.R7);
- GC_push_one ((word) thread_table[i].context.R8);
- GC_push_one ((word) thread_table[i].context.R9);
- GC_push_one ((word) thread_table[i].context.R10);
- GC_push_one ((word) thread_table[i].context.R11);
- GC_push_one ((word) thread_table[i].context.R12);
- GC_push_one ((word) thread_table[i].context.R13);
- GC_push_one ((word) thread_table[i].context.R14);
- GC_push_all_stack((char *) thread_table[i].context.R15,
- thread_table[i].stack);
+
+ /* Push all registers that might point into the heap. Frame */
+ /* pointer registers are included in case client code was */
+ /* compiled with the 'omit frame pointer' optimisation. */
+# define PUSH1(reg) GC_push_one((word)context.reg)
+# define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2)
+# define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4)
+# if defined(I386)
+ PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp);
+ sp = (ptr_t)context.Esp;
+# elif defined(ARM32)
+ PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11),PUSH1(R12);
+ sp = (ptr_t)context.Sp;
+# elif defined(SHx)
+ PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11);
+ PUSH2(R12,R13), PUSH1(R14);
+ sp = (ptr_t)context.R15;
+# elif defined(MIPS)
+ PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0);
+ PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0);
+ PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8);
+ PUSH4(IntT9,IntK0,IntK1,IntS8);
+ sp = (ptr_t)context.IntSp;
+# elif defined(PPC)
+ PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9);
+ PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18);
+ PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26);
+ PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31);
+ sp = (ptr_t)context.Gpr1;
+# elif defined(ALPHA)
+ PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6);
+ PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp);
+ PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9);
+ PUSH4(IntT10,IntT11,IntT12,IntAt);
+ sp = (ptr_t)context.IntSp;
# else
-# ifdef MIPS
- if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
- || thread_table[i].context.IntSp < (DWORD)bottom)
- ABORT("Thread stack pointer out of range");
- GC_push_one ((word) thread_table[i].context.IntAt);
- GC_push_one ((word) thread_table[i].context.IntV0);
- GC_push_one ((word) thread_table[i].context.IntV1);
- GC_push_one ((word) thread_table[i].context.IntA0);
- GC_push_one ((word) thread_table[i].context.IntA1);
- GC_push_one ((word) thread_table[i].context.IntA2);
- GC_push_one ((word) thread_table[i].context.IntA3);
- GC_push_one ((word) thread_table[i].context.IntT0);
- GC_push_one ((word) thread_table[i].context.IntT1);
- GC_push_one ((word) thread_table[i].context.IntT2);
- GC_push_one ((word) thread_table[i].context.IntT3);
- GC_push_one ((word) thread_table[i].context.IntT4);
- GC_push_one ((word) thread_table[i].context.IntT5);
- GC_push_one ((word) thread_table[i].context.IntT6);
- GC_push_one ((word) thread_table[i].context.IntT7);
- GC_push_one ((word) thread_table[i].context.IntS0);
- GC_push_one ((word) thread_table[i].context.IntS1);
- GC_push_one ((word) thread_table[i].context.IntS2);
- GC_push_one ((word) thread_table[i].context.IntS3);
- GC_push_one ((word) thread_table[i].context.IntS4);
- GC_push_one ((word) thread_table[i].context.IntS5);
- GC_push_one ((word) thread_table[i].context.IntS6);
- GC_push_one ((word) thread_table[i].context.IntS7);
- GC_push_one ((word) thread_table[i].context.IntT8);
- GC_push_one ((word) thread_table[i].context.IntT9);
- GC_push_one ((word) thread_table[i].context.IntK0);
- GC_push_one ((word) thread_table[i].context.IntK1);
- GC_push_one ((word) thread_table[i].context.IntS8);
- GC_push_all_stack((char *) thread_table[i].context.IntSp,
- thread_table[i].stack);
-# else
-# ifdef PPC
- if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack
- || thread_table[i].context.Gpr1 < (DWORD)bottom)
- ABORT("Thread stack pointer out of range");
- GC_push_one ((word) thread_table[i].context.Gpr0);
- /* Gpr1 is stack pointer */
- /* Gpr2 is global pointer */
- GC_push_one ((word) thread_table[i].context.Gpr3);
- GC_push_one ((word) thread_table[i].context.Gpr4);
- GC_push_one ((word) thread_table[i].context.Gpr5);
- GC_push_one ((word) thread_table[i].context.Gpr6);
- GC_push_one ((word) thread_table[i].context.Gpr7);
- GC_push_one ((word) thread_table[i].context.Gpr8);
- GC_push_one ((word) thread_table[i].context.Gpr9);
- GC_push_one ((word) thread_table[i].context.Gpr10);
- GC_push_one ((word) thread_table[i].context.Gpr11);
- GC_push_one ((word) thread_table[i].context.Gpr12);
- /* Gpr13 is reserved for the kernel */
- GC_push_one ((word) thread_table[i].context.Gpr14);
- GC_push_one ((word) thread_table[i].context.Gpr15);
- GC_push_one ((word) thread_table[i].context.Gpr16);
- GC_push_one ((word) thread_table[i].context.Gpr17);
- GC_push_one ((word) thread_table[i].context.Gpr18);
- GC_push_one ((word) thread_table[i].context.Gpr19);
- GC_push_one ((word) thread_table[i].context.Gpr20);
- GC_push_one ((word) thread_table[i].context.Gpr21);
- GC_push_one ((word) thread_table[i].context.Gpr22);
- GC_push_one ((word) thread_table[i].context.Gpr23);
- GC_push_one ((word) thread_table[i].context.Gpr24);
- GC_push_one ((word) thread_table[i].context.Gpr25);
- GC_push_one ((word) thread_table[i].context.Gpr26);
- GC_push_one ((word) thread_table[i].context.Gpr27);
- GC_push_one ((word) thread_table[i].context.Gpr28);
- GC_push_one ((word) thread_table[i].context.Gpr29);
- GC_push_one ((word) thread_table[i].context.Gpr30);
- GC_push_one ((word) thread_table[i].context.Gpr31);
- GC_push_all_stack((char *) thread_table[i].context.Gpr1,
- thread_table[i].stack);
-# else
-# ifdef ALPHA
- if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
- || thread_table[i].context.IntSp < (DWORD)bottom)
- ABORT("Thread stack pointer out of range");
- GC_push_one ((word) thread_table[i].context.IntV0);
- GC_push_one ((word) thread_table[i].context.IntT0);
- GC_push_one ((word) thread_table[i].context.IntT1);
- GC_push_one ((word) thread_table[i].context.IntT2);
- GC_push_one ((word) thread_table[i].context.IntT3);
- GC_push_one ((word) thread_table[i].context.IntT4);
- GC_push_one ((word) thread_table[i].context.IntT5);
- GC_push_one ((word) thread_table[i].context.IntT6);
- GC_push_one ((word) thread_table[i].context.IntT7);
- GC_push_one ((word) thread_table[i].context.IntS0);
- GC_push_one ((word) thread_table[i].context.IntS1);
- GC_push_one ((word) thread_table[i].context.IntS2);
- GC_push_one ((word) thread_table[i].context.IntS3);
- GC_push_one ((word) thread_table[i].context.IntS4);
- GC_push_one ((word) thread_table[i].context.IntS5);
- GC_push_one ((word) thread_table[i].context.IntFp);
- GC_push_one ((word) thread_table[i].context.IntA0);
- GC_push_one ((word) thread_table[i].context.IntA1);
- GC_push_one ((word) thread_table[i].context.IntA2);
- GC_push_one ((word) thread_table[i].context.IntA3);
- GC_push_one ((word) thread_table[i].context.IntA4);
- GC_push_one ((word) thread_table[i].context.IntA5);
- GC_push_one ((word) thread_table[i].context.IntT8);
- GC_push_one ((word) thread_table[i].context.IntT9);
- GC_push_one ((word) thread_table[i].context.IntT10);
- GC_push_one ((word) thread_table[i].context.IntT11);
- GC_push_one ((word) thread_table[i].context.IntT12);
- GC_push_one ((word) thread_table[i].context.IntAt);
- GC_push_all_stack((char *) thread_table[i].context.IntSp,
- thread_table[i].stack);
-# else
- --> architecture not supported
-# endif /* !ALPHA */
-# endif /* !PPC */
-# endif /* !MIPS */
-# endif /* !SHx */
-# endif /* !ARM32 */
-# endif /* !I386 */
+# error "architecture is not supported"
+# endif
+ }
+
+ stack_min = GC_get_stack_min(thread->stack_base);
+
+ if (sp >= stack_min && sp < thread->stack_base)
+ GC_push_all_stack(sp, thread->stack_base);
+ else {
+ WARN("Thread stack pointer 0x%lx out of range, pushing everything\n",
+ (unsigned long)sp);
+ GC_push_all_stack(stack_min, thread->stack_base);
}
}
+ }
+ if (!found_me) ABORT("Collecting from unknown thread.");
}
void GC_get_next_stack(char *start, char **lo, char **hi)
@@ -353,9 +406,10 @@ void GC_get_next_stack(char *start, char **lo, char **hi)
int i;
# define ADDR_LIMIT (char *)(-1L)
char * current_min = ADDR_LIMIT;
-
- for (i = 0; i < MAX_THREADS; i++) {
- char * s = (char *)thread_table[i].stack;
+ LONG my_max = GC_get_max_thread_index();
+
+ for (i = 0; i <= my_max; i++) {
+ char * s = (char *)thread_table[i].stack_base;
if (0 != s && s > start && s < current_min) {
current_min = s;
@@ -366,7 +420,7 @@ void GC_get_next_stack(char *start, char **lo, char **hi)
*lo = ADDR_LIMIT;
return;
}
- *lo = GC_get_lo_stack_addr(current_min);
+ *lo = GC_get_stack_min(current_min);
if (*lo < start) *lo = start;
}
@@ -376,7 +430,7 @@ void GC_get_next_stack(char *start, char **lo, char **hi)
/* We register threads from DllMain */
-GC_API HANDLE GC_CreateThread(
+GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
@@ -391,94 +445,39 @@ GC_API HANDLE GC_CreateThread(
/* must properly intercept thread creation. */
typedef struct {
- HANDLE child_ready_h, parent_ready_h;
- volatile struct thread_entry * entry;
LPTHREAD_START_ROUTINE start;
LPVOID param;
} thread_args;
-DWORD WINAPI thread_start(LPVOID arg);
+static DWORD WINAPI thread_start(LPVOID arg);
-HANDLE WINAPI GC_CreateThread(
+GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
{
HANDLE thread_h = NULL;
- HANDLE child_ready_h, parent_ready_h;
- int i;
- thread_args args;
+ thread_args *args;
- /* allocate thread slot */
- LOCK();
- for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
- ;
- if (i != MAX_THREADS) {
- thread_table[i].in_use = TRUE;
+ if (!GC_is_initialized) GC_init();
+ /* make sure GC is initialized (i.e. main thread is attached) */
+
+ args = GC_malloc_uncollectable(sizeof(thread_args));
+ /* Handed off to and deallocated by child thread. */
+ if (0 == args) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
}
- UNLOCK();
- if (i != MAX_THREADS) {
-
- /* create unnamed unsignalled events */
- if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
- if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
-
- /* set up thread arguments */
- args.child_ready_h = child_ready_h;
- args.parent_ready_h = parent_ready_h;
- args.entry = &thread_table[i];
- args.start = lpStartAddress;
- args.param = lpParameter;
-
- thread_h = CreateThread(lpThreadAttributes,
- dwStackSize, thread_start,
- &args,
- dwCreationFlags & ~CREATE_SUSPENDED,
- lpThreadId);
-
- if (thread_h) {
-
- /* fill in ID and handle; tell child this is done */
- thread_table[i].id = *lpThreadId;
- 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 */
- WaitForSingleObject (child_ready_h, INFINITE);
-
- /* suspend the child if requested */
- if (dwCreationFlags & CREATE_SUSPENDED)
- SuspendThread (thread_h);
-
- /* let child call given function now (or when resumed) */
- SetEvent (parent_ready_h);
-
- } else {
- CloseHandle (parent_ready_h);
- }
- }
- }
-
- CloseHandle (child_ready_h);
-
- if (thread_h == NULL)
- thread_table[i].in_use = FALSE;
+ /* set up thread arguments */
+ args -> start = lpStartAddress;
+ args -> param = lpParameter;
- } else { /* no thread slot found */
- SetLastError (ERROR_TOO_MANY_TCBS);
- }
+ thread_h = CreateThread(lpThreadAttributes,
+ dwStackSize, thread_start,
+ args, dwCreationFlags,
+ lpThreadId);
return thread_h;
}
@@ -486,19 +485,9 @@ HANDLE WINAPI GC_CreateThread(
static DWORD WINAPI thread_start(LPVOID arg)
{
DWORD ret = 0;
- thread_args args = *(thread_args *)arg;
-
- /* wait for parent to fill in ID and handle */
- WaitForSingleObject (args.parent_ready_h, INFINITE);
- ResetEvent (args.parent_ready_h);
+ thread_args *args = (thread_args *)arg;
- /* fill in stack; tell parent this is done */
- args.entry->stack = GC_get_stack_base();
- SetEvent (args.child_ready_h);
-
- /* wait for parent to tell us to go (in case it needs to suspend us) */
- WaitForSingleObject (args.parent_ready_h, INFINITE);
- CloseHandle (args.parent_ready_h);
+ GC_new_thread();
/* Clear the thread entry even if we exit with an exception. */
/* This is probably pointless, since an uncaught exception is */
@@ -506,16 +495,12 @@ static DWORD WINAPI thread_start(LPVOID arg)
#ifndef __GNUC__
__try {
#endif /* __GNUC__ */
- ret = args.start (args.param);
+ ret = args->start (args->param);
#ifndef __GNUC__
} __finally {
#endif /* __GNUC__ */
- LOCK();
- args.entry->stack = 0;
- args.entry->in_use = FALSE;
- /* cast away volatile qualifier */
- BZERO((void *) &args.entry->context, sizeof(CONTEXT));
- UNLOCK();
+ GC_free(args);
+ GC_delete_thread(GetCurrentThreadId());
#ifndef __GNUC__
}
#endif /* __GNUC__ */
@@ -549,7 +534,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
DWORD thread_id;
/* initialize everything */
- InitializeCriticalSection(&GC_allocate_ml);
GC_init();
/* start the main thread */
@@ -579,126 +563,44 @@ DWORD WINAPI main_thread_start(LPVOID arg)
# else /* !MSWINCE */
-LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
-
-/* 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_main_thread = GetCurrentThreadId();
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();
+ GC_new_thread();
}
+#ifdef CYGWIN32
+
struct start_info {
void *(*start_routine)(void *);
void *arg;
+ GC_bool detached;
};
int GC_pthread_join(pthread_t pthread_id, void **retval) {
int result;
int i;
+ GC_thread me;
# if DEBUG_CYGWIN_THREADS
- GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(),
- GetCurrentThreadId(), (int)pthread_id);
+ 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 */
+ /* Thread being joined might not have registered itself yet. */
+ /* After the join,thread id may have been recycled. */
+ /* FIXME: It would be better if this worked more like */
+ /* pthread_support.c. */
+
+ while ((me = GC_lookup_thread(pthread_id)) == 0) Sleep(10);
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);
+ GC_delete_gc_thread(me);
# if DEBUG_CYGWIN_THREADS
GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
@@ -729,10 +631,15 @@ GC_pthread_create(pthread_t *new_thread,
si -> start_routine = start_routine;
si -> arg = arg;
+ if (attr != 0 &&
+ pthread_attr_getdetachstate(attr, &si->detached)
+ == PTHREAD_CREATE_DETACHED) {
+ si->detached = TRUE;
+ }
# if DEBUG_CYGWIN_THREADS
- GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(),
- GetCurrentThreadId);
+ 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);
@@ -750,6 +657,8 @@ void * GC_start_routine(void * arg)
void *(*start)(void *);
void *start_arg;
pthread_t pthread_id;
+ GC_thread me;
+ GC_bool detached;
int i;
# if DEBUG_CYGWIN_THREADS
@@ -764,17 +673,19 @@ void * GC_start_routine(void * arg)
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();
+ me = GC_new_thread();
UNLOCK();
start = si -> start_routine;
start_arg = si -> arg;
- pthread_id = pthread_self();
+ if (si-> detached) me -> flags |= DETACHED;
+ me -> pthread_id = pthread_id = pthread_self();
GC_free(si); /* was allocated uncollectable */
- pthread_cleanup_push(GC_thread_exit_proc, pthread_id);
+ pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
result = (*start)(start_arg);
+ me -> status = result;
pthread_cleanup_pop(0);
# if DEBUG_CYGWIN_THREADS
@@ -782,20 +693,12 @@ void * GC_start_routine(void * arg)
(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;
+ GC_thread me = (GC_thread)arg;
int i;
# if DEBUG_CYGWIN_THREADS
@@ -804,25 +707,41 @@ void GC_thread_exit_proc(void *arg)
# endif
LOCK();
- for (i = 0; thread_table[i].pthread_id != pthread_id; i++) {
- if (i == MAX_THREADS - 1)
- ABORT("thread not found on exit");
+ if (me -> flags & DETACHED) {
+ GC_delete_thread(GetCurrentThreadId());
+ } else {
+ /* deallocate it as part of join */
+ me -> flags |= FINISHED;
}
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);
+
+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_gc_id);
+ }
+ UNLOCK();
+ }
+ return result;
}
+
#else /* !CYGWIN32 */
/*
@@ -834,15 +753,17 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
{
switch (reason) {
case DLL_PROCESS_ATTACH:
- InitializeCriticalSection(&GC_allocate_ml);
GC_init(); /* Force initialization before thread attach. */
/* fall through */
case DLL_THREAD_ATTACH:
- threadAttach();
+ GC_ASSERT(GC_thr_initialized);
+ if (GC_main_thread != GetCurrentThreadId()) {
+ GC_new_thread();
+ } /* o.w. we already did it during GC_thr_init(), called by GC_init() */
break;
case DLL_THREAD_DETACH:
- threadDetach(GetCurrentThreadId());
+ GC_delete_thread(GetCurrentThreadId());
break;
case DLL_PROCESS_DETACH:
@@ -850,15 +771,10 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
int i;
LOCK();
- for (i = 0; i < MAX_THREADS; ++i)
+ for (i = 0; i <= GC_get_max_thread_index(); ++i)
{
if (thread_table[i].in_use)
- {
- thread_table[i].stack = 0;
- thread_table[i].in_use = FALSE;
- CloseHandle(thread_table[i].handle);
- BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
- }
+ GC_delete_gc_thread(thread_table + i);
}
UNLOCK();