# -DGC_MACOSX_THREADS enables support for Mac OS X pthreads. Untested.
# -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads.
# See README.DGUX386.
+# -DGC_WIN32_THREADS enables support for win32 threads. That makes sense
+# for this Makefile only under Cygwin.
# -DGC_THREADS should set the appropriate one of the above macros.
# It assumes pthreads for Solaris.
# -DALL_INTERIOR_POINTERS allows all pointers to the interior
# makes incremental collection easier. Was enabled by default until 6.0.
# Rarely used, to my knowledge.
# -DHANDLE_FORK attempts to make GC_malloc() work in a child process fork()ed
-# from a multithreaded parent. Currently only supported by linux_threads.c.
+# from a multithreaded parent. Currently only supported by pthread_support.c.
# (Similar code should work on Solaris or Irix, but it hasn't been tried.)
# -DTEST_WITH_SYSTEM_MALLOC causes gctest to allocate (and leak) large chunks
# of memory with the standard system malloc. This will cause the root
RANLIB= ranlib
-OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.o
+OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o aix_irix_threads.o pthread_support.o pthread_stop_world.o darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.o win32_threads.o
-CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.c
+CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c aix_irix_threads.c pthread_support.c pthread_stop_world.c darwin_stop_world.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.c win32_threads.c
CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c include/cord.h include/ec.h include/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC
include/private/solaris_threads.h include/gc_backptr.h \
hpux_test_and_clear.s include/gc_gcj.h \
include/gc_local_alloc.h include/private/dbg_mlc.h \
- include/private/specific.h powerpc_macosx_mach_dep.s \
+ include/private/specific.h powerpc_darwin_mach_dep.s \
include/leak_detector.h include/gc_amiga_redirects.h \
include/gc_pthread_redirects.h ia64_save_regs_in_stack.s \
- include/gc_config_macros.h $(CORD_SRCS)
+ include/gc_config_macros.h include/private/pthread_support.h \
+ include/private/pthread_stop_world.h include/private/darwin_semaphore.h \
+ include/private/darwin_stop_world.h $(CORD_SRCS)
DOC_FILES= README.QUICK doc/README.Mac doc/README.MacOSX doc/README.OS2 \
doc/README.amiga doc/README.cords doc/debugging.html \
MacProjects.sit.hqx MacOS.c \
Mac_files/datastart.c Mac_files/dataend.c \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \
- add_gc_prefix.c gc_cpp.cpp win32_threads.c \
+ add_gc_prefix.c gc_cpp.cpp \
version.h AmigaOS.c \
$(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES)
mark.o typd_mlc.o finalize.o ptr_chck.o: $(srcdir)/include/gc_mark.h $(srcdir)/include/private/gc_pmark.h
-specific.o linux_threads.o: $(srcdir)/include/private/specific.h
+specific.o pthread_support.o: $(srcdir)/include/private/specific.h
solaris_threads.o solaris_pthreads.o: $(srcdir)/include/private/solaris_threads.h
mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S \
$(srcdir)/mips_ultrix_mach_dep.s \
- $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_macosx_mach_dep.s \
+ $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_darwin_mach_dep.s \
$(srcdir)/sparc_mach_dep.S $(srcdir)/sparc_sunos4_mach_dep.s \
$(srcdir)/ia64_save_regs_in_stack.s \
$(srcdir)/sparc_netbsd_mach_dep.s $(UTILS)
| ./if_mach MIPS IRIX5 grep -v "^\#" > $(srcdir)/mips_sgi_mach_dep.s
./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
- ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_macosx_mach_dep.s
+ ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_darwin_mach_dep.s
./if_mach ALPHA LINUX $(CC) -c -o mach_dep.o $(srcdir)/alpha_mach_dep.S
./if_mach SPARC SUNOS5 $(CC) -c -o mach_dep.o $(srcdir)/sparc_mach_dep.S
./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s
EXTRA_DIST =
## more items will be succesively added below
-lib_LTLIBRARIES = libgc.la @addlibs@
-
-EXTRA_LTLIBRARIES = libgccpp.la
+if CPLUSPLUS
+extra = libgccpp.la
+else
+extra =
+endif
+lib_LTLIBRARIES = libgc.la $(extra)
include_HEADERS = include/gc.h include/gc_local_alloc.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
EXTRA_HEADERS = include/gc_cpp.h include/gc_allocator.h
+if POWERPC_DARWIN
+asm_libgc_sources = powerpc_darwin_mach_dep.s
+else
+asm_libgc_sources =
+endif
+
libgc_la_SOURCES = allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \
-dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c irix_threads.c \
-linux_threads.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \
+dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c aix_irix_threads.c \
+malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \
obj_map.c os_dep.c pcr_interface.c ptr_chck.c real_malloc.c reclaim.c \
solaris_pthreads.c solaris_threads.c specific.c stubborn.c typd_mlc.c \
-backgraph.c win32_threads.c
+backgraph.c win32_threads.c \
+pthread_support.c pthread_stop_world.c darwin_stop_world.c \
+$(asm_libgc_sources)
# Include THREADLIBS here to ensure that the correct versions of
# linuxthread semaphore functions get linked:
libgc_la_LDFLAGS = -version-info 1:2:0
EXTRA_libgc_la_SOURCES = alpha_mach_dep.S \
- mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \
+ mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_darwin_mach_dep.s \
rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \
sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s
AM_CXXFLAGS = @GC_CFLAGS@
AM_CFLAGS = @GC_CFLAGS@
-check_PROGRAMS = gctest @addtests@
-EXTRA_PROGRAMS = test_cpp
+if CPLUSPLUS
+extra_checks = test_cpp
+else
+extra_checks =
+endif
+
+check_PROGRAMS = gctest $(extra_checks)
test.o: $(srcdir)/tests/test.c
$(COMPILE) -c $(srcdir)/tests/test.c
test_cpp_SOURCES = tests/test_cpp.cc
test_cpp_LDADD = ./libgc.la ./libgccpp.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
-TESTS = gctest @addtests@
-
-test_cpp$(EXEEXT): test_cpp.o
- $(LIBTOOL) --mode=link $(CXX) $(AM_CFLAGS) $(MY_CFLAGS) \
- test_cpp.o $(test_cpp_LDADD) $(LDFLAGS) -o $@
+TESTS = gctest $(extra_checks)
## FIXME: relies on internal code generated by automake.
all_objs = @addobjs@ $(libgc_la_OBJECTS)
# -DGC_MACOSX_THREADS enables support for Mac OS X pthreads. Untested.
# -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads.
# See README.DGUX386.
+# -DGC_WIN32_THREADS enables support for win32 threads. That makes sense
+# for this Makefile only under Cygwin.
# -DGC_THREADS should set the appropriate one of the above macros.
# It assumes pthreads for Solaris.
# -DALL_INTERIOR_POINTERS allows all pointers to the interior
# makes incremental collection easier. Was enabled by default until 6.0.
# Rarely used, to my knowledge.
# -DHANDLE_FORK attempts to make GC_malloc() work in a child process fork()ed
-# from a multithreaded parent. Currently only supported by linux_threads.c.
+# from a multithreaded parent. Currently only supported by pthread_support.c.
# (Similar code should work on Solaris or Irix, but it hasn't been tried.)
# -DTEST_WITH_SYSTEM_MALLOC causes gctest to allocate (and leak) large chunks
# of memory with the standard system malloc. This will cause the root
RANLIB= ranlib
-OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.o
+OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o aix_irix_threads.o pthread_support.o pthread_stop_world.o darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o gcj_mlc.o specific.o gc_dlopen.o backgraph.o win32_threads.o
-CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.c
+CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c aix_irix_threads.c pthread_support.c pthread_stop_world.c darwin_stop_world.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c gcj_mlc.c specific.c gc_dlopen.c backgraph.c win32_threads.c
CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c include/cord.h include/ec.h include/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC
include/private/solaris_threads.h include/gc_backptr.h \
hpux_test_and_clear.s include/gc_gcj.h \
include/gc_local_alloc.h include/private/dbg_mlc.h \
- include/private/specific.h powerpc_macosx_mach_dep.s \
+ include/private/specific.h powerpc_darwin_mach_dep.s \
include/leak_detector.h include/gc_amiga_redirects.h \
include/gc_pthread_redirects.h ia64_save_regs_in_stack.s \
- include/gc_config_macros.h $(CORD_SRCS)
+ include/gc_config_macros.h include/private/pthread_support.h \
+ include/private/pthread_stop_world.h include/private/darwin_semaphore.h \
+ include/private/darwin_stop_world.h $(CORD_SRCS)
DOC_FILES= README.QUICK doc/README.Mac doc/README.MacOSX doc/README.OS2 \
doc/README.amiga doc/README.cords doc/debugging.html \
MacProjects.sit.hqx MacOS.c \
Mac_files/datastart.c Mac_files/dataend.c \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \
- add_gc_prefix.c gc_cpp.cpp win32_threads.c \
+ add_gc_prefix.c gc_cpp.cpp \
version.h AmigaOS.c \
$(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES)
mark.o typd_mlc.o finalize.o ptr_chck.o: $(srcdir)/include/gc_mark.h $(srcdir)/include/private/gc_pmark.h
-specific.o linux_threads.o: $(srcdir)/include/private/specific.h
+specific.o pthread_support.o: $(srcdir)/include/private/specific.h
solaris_threads.o solaris_pthreads.o: $(srcdir)/include/private/solaris_threads.h
mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S \
$(srcdir)/mips_ultrix_mach_dep.s \
- $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_macosx_mach_dep.s \
+ $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_darwin_mach_dep.s \
$(srcdir)/sparc_mach_dep.S $(srcdir)/sparc_sunos4_mach_dep.s \
$(srcdir)/ia64_save_regs_in_stack.s \
$(srcdir)/sparc_netbsd_mach_dep.s $(UTILS)
| ./if_mach MIPS IRIX5 grep -v "^\#" > $(srcdir)/mips_sgi_mach_dep.s
./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
- ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_macosx_mach_dep.s
+ ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_darwin_mach_dep.s
./if_mach ALPHA LINUX $(CC) -c -o mach_dep.o $(srcdir)/alpha_mach_dep.S
./if_mach SPARC SUNOS5 $(CC) -c -o mach_dep.o $(srcdir)/sparc_mach_dep.S
./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s
include/private/solaris_threads.h include/gc_backptr.h \
hpux_test_and_clear.s include/gc_gcj.h \
include/gc_local_alloc.h include/private/dbg_mlc.h \
- include/private/specific.h powerpc_macosx_mach_dep.s \
+ include/private/specific.h powerpc_darwin_mach_dep.s \
include/leak_detector.h $(CORD_SRCS)
OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \
ln liblinuxgc.so libgc.so
mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S $(srcdir)/mips_ultrix_mach_dep.s \
- $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_macosx_mach_dep.s $(UTILS)
+ $(srcdir)/rs6000_mach_dep.s $(srcdir)/powerpc_darwin_mach_dep.s $(UTILS)
rm -f mach_dep.o
./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_mach_dep.S
./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s
./if_mach RS6000 "" $(AS) -o mach_dep.o $(srcdir)/rs6000_mach_dep.s
- ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_macosx_mach_dep.s
+ ./if_mach POWERPC MACOSX $(AS) -o mach_dep.o $(srcdir)/powerpc_darwin_mach_dep.s
./if_mach ALPHA "" $(AS) -o mach_dep.o $(srcdir)/alpha_mach_dep.S
./if_mach SPARC SUNOS5 $(AS) -o mach_dep.o $(srcdir)/sparc_mach_dep.S
./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s
cord/de_win.h cord/de_win.RC\
libtool.m4
-lib_LTLIBRARIES = libgc.la @addlibs@
-
-EXTRA_LTLIBRARIES = libgccpp.la
+@CPLUSPLUS_TRUE@extra = libgccpp.la
+@CPLUSPLUS_FALSE@extra =
+lib_LTLIBRARIES = libgc.la $(extra)
include_HEADERS = include/gc.h include/gc_local_alloc.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
EXTRA_HEADERS = include/gc_cpp.h include/gc_allocator.h
+@POWERPC_DARWIN_TRUE@asm_libgc_sources = powerpc_darwin_mach_dep.s
+@POWERPC_DARWIN_FALSE@asm_libgc_sources =
+
libgc_la_SOURCES = allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \
-dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c irix_threads.c \
-linux_threads.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \
+dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c aix_irix_threads.c \
+malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \
obj_map.c os_dep.c pcr_interface.c ptr_chck.c real_malloc.c reclaim.c \
solaris_pthreads.c solaris_threads.c specific.c stubborn.c typd_mlc.c \
-backgraph.c win32_threads.c
+backgraph.c win32_threads.c \
+pthread_support.c pthread_stop_world.c darwin_stop_world.c \
+$(asm_libgc_sources)
# Include THREADLIBS here to ensure that the correct versions of
libgc_la_LDFLAGS = -version-info 1:2:0
EXTRA_libgc_la_SOURCES = alpha_mach_dep.S \
- mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_macosx_mach_dep.s \
+ mips_sgi_mach_dep.S mips_ultrix_mach_dep.s powerpc_darwin_mach_dep.s \
rs6000_mach_dep.s sparc_mach_dep.S sparc_netbsd_mach_dep.s \
sparc_sunos4_mach_dep.s ia64_save_regs_in_stack.s
AM_CXXFLAGS = @GC_CFLAGS@
AM_CFLAGS = @GC_CFLAGS@
-check_PROGRAMS = gctest @addtests@
-EXTRA_PROGRAMS = test_cpp
+@CPLUSPLUS_TRUE@extra_checks = test_cpp
+@CPLUSPLUS_FALSE@extra_checks =
+
+check_PROGRAMS = gctest $(extra_checks)
# gctest_OBJECTS = test.o
gctest_SOURCES = tests/test.c
test_cpp_SOURCES = tests/test_cpp.cc
test_cpp_LDADD = ./libgc.la ./libgccpp.la $(THREADLIBS) $(EXTRA_TEST_LIBS)
-TESTS = gctest @addtests@
+TESTS = gctest $(extra_checks)
all_objs = @addobjs@ $(libgc_la_OBJECTS)
CONFIG_CLEAN_FILES =
LTLIBRARIES = $(lib_LTLIBRARIES)
+@POWERPC_DARWIN_TRUE@am__objects_1 = powerpc_darwin_mach_dep.lo
+@POWERPC_DARWIN_FALSE@am__objects_1 =
am_libgc_la_OBJECTS = allchblk.lo alloc.lo blacklst.lo checksums.lo \
dbg_mlc.lo dyn_load.lo finalize.lo gc_dlopen.lo gcj_mlc.lo \
- headers.lo irix_threads.lo linux_threads.lo malloc.lo \
- mallocx.lo mark.lo mark_rts.lo misc.lo new_hblk.lo obj_map.lo \
- os_dep.lo pcr_interface.lo ptr_chck.lo real_malloc.lo \
- reclaim.lo solaris_pthreads.lo solaris_threads.lo specific.lo \
- stubborn.lo typd_mlc.lo backgraph.lo win32_threads.lo
+ headers.lo aix_irix_threads.lo malloc.lo mallocx.lo mark.lo \
+ mark_rts.lo misc.lo new_hblk.lo obj_map.lo os_dep.lo \
+ pcr_interface.lo ptr_chck.lo real_malloc.lo reclaim.lo \
+ solaris_pthreads.lo solaris_threads.lo specific.lo stubborn.lo \
+ typd_mlc.lo backgraph.lo win32_threads.lo pthread_support.lo \
+ pthread_stop_world.lo darwin_stop_world.lo $(am__objects_1)
libgc_la_OBJECTS = $(am_libgc_la_OBJECTS)
libgccpp_la_DEPENDENCIES =
am_libgccpp_la_OBJECTS = gc_cpp.lo
libgccpp_la_OBJECTS = $(am_libgccpp_la_OBJECTS)
-EXTRA_PROGRAMS = test_cpp$(EXEEXT)
-check_PROGRAMS = gctest$(EXEEXT) @addtests@
+@CPLUSPLUS_TRUE@check_PROGRAMS = gctest$(EXEEXT) test_cpp$(EXEEXT)
+@CPLUSPLUS_FALSE@check_PROGRAMS = gctest$(EXEEXT)
am_gctest_OBJECTS = test.$(OBJEXT)
gctest_OBJECTS = $(am_gctest_OBJECTS)
gctest_DEPENDENCIES = ./libgc.la
LIBS = @LIBS@
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
-@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/allchblk.Plo ./$(DEPDIR)/alloc.Plo \
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/aix_irix_threads.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/allchblk.Plo ./$(DEPDIR)/alloc.Plo \
@AMDEP_TRUE@ ./$(DEPDIR)/backgraph.Plo ./$(DEPDIR)/blacklst.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/checksums.Plo ./$(DEPDIR)/dbg_mlc.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/dyn_load.Plo ./$(DEPDIR)/finalize.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/gc_cpp.Plo ./$(DEPDIR)/gc_dlopen.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/gcj_mlc.Plo ./$(DEPDIR)/headers.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/irix_threads.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/linux_threads.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/malloc.Plo ./$(DEPDIR)/mallocx.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/mark.Plo ./$(DEPDIR)/mark_rts.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/misc.Plo ./$(DEPDIR)/new_hblk.Plo \
-@AMDEP_TRUE@ ./$(DEPDIR)/obj_map.Plo ./$(DEPDIR)/os_dep.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/checksums.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/darwin_stop_world.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/dbg_mlc.Plo ./$(DEPDIR)/dyn_load.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/finalize.Plo ./$(DEPDIR)/gc_cpp.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/gc_dlopen.Plo ./$(DEPDIR)/gcj_mlc.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/headers.Plo ./$(DEPDIR)/malloc.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/mallocx.Plo ./$(DEPDIR)/mark.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/mark_rts.Plo ./$(DEPDIR)/misc.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/new_hblk.Plo ./$(DEPDIR)/obj_map.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/os_dep.Plo \
@AMDEP_TRUE@ ./$(DEPDIR)/pcr_interface.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/pthread_stop_world.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/pthread_support.Plo \
@AMDEP_TRUE@ ./$(DEPDIR)/ptr_chck.Plo \
@AMDEP_TRUE@ ./$(DEPDIR)/real_malloc.Plo ./$(DEPDIR)/reclaim.Plo \
@AMDEP_TRUE@ ./$(DEPDIR)/solaris_pthreads.Plo \
libgc.la: $(libgc_la_OBJECTS) $(libgc_la_DEPENDENCIES)
$(LINK) -rpath $(libdir) $(libgc_la_LDFLAGS) $(libgc_la_OBJECTS) $(libgc_la_LIBADD) $(LIBS)
libgccpp.la: $(libgccpp_la_OBJECTS) $(libgccpp_la_DEPENDENCIES)
- $(CXXLINK) $(libgccpp_la_LDFLAGS) $(libgccpp_la_OBJECTS) $(libgccpp_la_LIBADD) $(LIBS)
+ $(CXXLINK) -rpath $(libdir) $(libgccpp_la_LDFLAGS) $(libgccpp_la_OBJECTS) $(libgccpp_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; for p in $$list; do \
@rm -f gctest$(EXEEXT)
$(LINK) $(gctest_LDFLAGS) $(gctest_OBJECTS) $(gctest_LDADD) $(LIBS)
test_cpp.$(OBJEXT): tests/test_cpp.cc
+test_cpp$(EXEEXT): $(test_cpp_OBJECTS) $(test_cpp_DEPENDENCIES)
+ @rm -f test_cpp$(EXEEXT)
+ $(CXXLINK) $(test_cpp_LDFLAGS) $(test_cpp_OBJECTS) $(test_cpp_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT) core *.core
distclean-compile:
-rm -f *.tab.c
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aix_irix_threads.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allchblk.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backgraph.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blacklst.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checksums.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/darwin_stop_world.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbg_mlc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dyn_load.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/finalize.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gc_dlopen.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gcj_mlc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/headers.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irix_threads.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linux_threads.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mallocx.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mark.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obj_map.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_dep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcr_interface.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pthread_stop_world.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pthread_support.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptr_chck.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/real_malloc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reclaim.Plo@am__quote@
# Using $< in the above seems to fail with the HP/UX on Itanium make.
test_cpp.o: $(srcdir)/tests/test_cpp.cc
$(COMPILE) -c $(srcdir)/tests/test_cpp.cc
-
-test_cpp$(EXEEXT): test_cpp.o
- $(LIBTOOL) --mode=link $(CXX) $(AM_CFLAGS) $(MY_CFLAGS) \
- test_cpp.o $(test_cpp_LDADD) $(LDFLAGS) -o $@
$(all_objs) : include/private/gcconfig.h include/private/gc_priv.h \
include/private/gc_hdrs.h include/gc.h include/gc_gcj.h \
include/gc_pthread_redirects.h include/gc_config_macros.h \
/*
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
* Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
- * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
+ * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
* modified is included with the above copyright notice.
*/
/*
- * Support code for Irix (>=6.2) Pthreads. This relies on properties
+ * Support code for Irix (>=6.2) Pthreads and for AIX pthreads.
+ * This relies on properties
* not guaranteed by the Pthread standard. It may or may not be portable
* to other implementations.
*
- * This now also includes an initial attempt at thread support for
- * HP/UX 11.
+ * Note that there is a lot of code duplication between this file and
+ * (pthread_support.c, pthread_stop_world.c). They should be merged.
+ * Pthread_support.c should be directly usable.
*
- * Note that there is a lot of code duplication between linux_threads.c
- * and irix_threads.c; any changes made here may need to be reflected
- * there too.
+ * Please avoid adding new ports here; use the generic pthread support
+ * as a base instead.
*/
-# if defined(GC_IRIX_THREADS)
+# if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
# include "private/gc_priv.h"
# include <pthread.h>
+# include <assert.h>
# include <semaphore.h>
# include <time.h>
# include <errno.h>
#undef pthread_create
#undef pthread_sigmask
#undef pthread_join
-#undef pthread_detach
-#ifdef HANDLE_FORK
- --> Not yet supported. Try porting the code from linux_threads.c.
+#if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
+#define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
#endif
void GC_thr_init();
* The only way to suspend threads given the pthread interface is to send
* signals. Unfortunately, this means we have to reserve
* a signal, and intercept client calls to change the signal mask.
- * We use SIG_SUSPEND, defined in gc_priv.h.
*/
+#if 0 /* DOB: 6.1 */
+# if defined(GC_AIX_THREADS)
+# define SIG_SUSPEND SIGUSR1
+# else
+# define SIG_SUSPEND (SIGRTMIN + 6)
+# endif
+#endif
pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
/* Number of threads stopped so far */
size_t GC_min_stack_sz;
+size_t GC_page_sz;
+
# define N_FREE_LISTS 25
ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
/* GC_stack_free_lists[i] is free list for stacks of */
if (result != 0) {
GC_stack_free_lists[index] = *(ptr_t *)result;
} else {
- result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_size);
- result = (ptr_t)(((word)result + GC_page_size) & ~(GC_page_size - 1));
+ result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
+ result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
/* Protect hottest page to detect overflow. */
# ifdef STACK_GROWS_UP
- /* mprotect(result + search_sz, GC_page_size, PROT_NONE); */
+ /* mprotect(result + search_sz, GC_page_sz, PROT_NONE); */
# else
- /* mprotect(result, GC_page_size, PROT_NONE); */
- result += GC_page_size;
+ /* mprotect(result, GC_page_sz, PROT_NONE); */
+ result += GC_page_sz;
# endif
}
*stack_size = search_sz;
/* Dont acquire allocation lock, since we may already hold it. */
} else {
result = (struct GC_Thread_Rep *)
- GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
+ GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
}
if (result == 0) return(0);
result -> id = id;
return(p);
}
+#if defined(GC_AIX_THREADS)
+void GC_stop_world()
+{
+ pthread_t my_thread = pthread_self();
+ register int i;
+ register GC_thread p;
+ register int result;
+ struct timespec timeout;
+
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> id != my_thread) {
+ pthread_suspend_np(p->id);
+ }
+ }
+ }
+ /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
+}
+
+void GC_start_world()
+{
+ GC_thread p;
+ unsigned i;
+ pthread_t my_thread = pthread_self();
+
+ /* GC_printf0("World starting\n"); */
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> id != my_thread) {
+ pthread_continue_np(p->id);
+ }
+ }
+ }
+}
+
+#else /* GC_AIX_THREADS */
/* Caller holds allocation lock. */
void GC_stop_world()
pthread_cond_broadcast(&GC_continue_cv);
}
+#endif /* GC_AIX_THREADS */
+
# ifdef MMAP_STACKS
--> not really supported yet.
int GC_is_thread_stack(ptr_t addr)
if (pthread_equal(p -> id, me)) {
hot = GC_approx_sp();
} else {
- hot = p -> stack_ptr;
+#ifdef GC_AIX_THREADS
+ /* AIX doesn't use signals to suspend, so we need to get an accurate hot stack pointer */
+ pthread_t id = p -> id;
+ struct __pthrdsinfo pinfo;
+ int val = 255;
+ char regbuf[255];
+ int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo, sizeof(pinfo), regbuf, &val);
+ if (retval != 0) { printf("ERROR: pthread_getthrds_np() failed in GC\n"); abort(); }
+ hot = (ptr_t)(unsigned long)pinfo.__pi_ustk;
+ if ((p -> stack_size != 0 &&
+ (pinfo.__pi_stackend != ((ptr_t)p -> stack) + p -> stack_size ||
+ p -> stack_ptr < p -> stack ||
+ p -> stack_ptr > ((ptr_t)p -> stack) + p -> stack_size))) {
+ printf("ERROR in GC_push_all_stacks() stack state:\n"
+ "p->stack: 0x%08x\n"
+ "p->stack_size: 0x%08x\n"
+ "p->stack_ptr: 0x%08x\n"
+ "(p->stack+p->stack_size): 0x%08x\n"
+ "pinfo.__pi_stackaddr: 0x%08x\n"
+ "pinfo.__pi_stacksize: 0x%08x\n"
+ "pinfo.__pi_stackend: 0x%08x\n"
+ "GC_stackbottom: 0x%08x\n"
+ ,
+ (uintptr_t)p->stack, (uintptr_t)p->stack_size,
+ (uintptr_t)p->stack_ptr, (uintptr_t)(((ptr_t)(p->stack))+p->stack_size),
+ (uintptr_t)pinfo.__pi_stackaddr, (uintptr_t)pinfo.__pi_stacksize, (uintptr_t)pinfo.__pi_stackend,
+ (uintptr_t)GC_stackbottom
+ );
+ }
+ /* push the registers too, because they won't be on stack */
+ GC_push_all_eager((ptr_t)&pinfo.__pi_context, (ptr_t)((&pinfo.__pi_context)+1));
+ GC_push_all_eager((ptr_t)regbuf, (ptr_t)®buf[val]);
+#else
+ hot = p -> stack_ptr;
+#endif
}
if (p -> stack_size != 0) {
# ifdef STACK_GROWS_UP
# ifdef STACK_GROWS_UP
GC_push_all_stack(cold, hot);
# else
+ /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
GC_push_all_stack(hot, cold);
# endif
}
if (GC_thr_initialized) return;
GC_thr_initialized = TRUE;
GC_min_stack_sz = HBLKSIZE;
+ GC_page_sz = sysconf(_SC_PAGESIZE);
+#ifndef GC_AIX_THREADS
(void) sigaction(SIG_SUSPEND, 0, &act);
if (act.sa_handler != SIG_DFL)
ABORT("Previously installed SIG_SUSPEND handler");
(void) sigemptyset(&act.sa_mask);
if (0 != sigaction(SIG_SUSPEND, &act, 0))
ABORT("Failed to install SIG_SUSPEND handler");
+#endif
/* Add the initial thread, so we can stop it. */
t = GC_new_thread(pthread_self());
t -> stack_size = 0;
{
sigset_t fudged_set;
+#ifdef GC_AIX_THREADS
+ return(pthread_sigmask(how, set, oset));
+#endif
+
if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
fudged_set = *set;
sigdelset(&fudged_set, SIG_SUSPEND);
word flags;
ptr_t stack;
size_t stack_size;
- sem_t registered; /* 1 ==> in our thread table, but */
- /* parent hasn't yet noticed. */
+ sem_t registered;
};
void GC_thread_exit_proc(void *arg)
/* Some versions of the Irix pthreads library can erroneously */
/* return EINTR when the call succeeds. */
if (EINTR == result) result = 0;
- if (result == 0) {
- LOCK();
- /* Here the pthread thread id may have been recycled. */
- GC_delete_gc_thread(thread, thread_gc_id);
- UNLOCK();
- }
- return result;
-}
-
-int GC_pthread_detach(pthread_t thread)
-{
- int result;
- GC_thread thread_gc_id;
-
LOCK();
- thread_gc_id = GC_lookup_thread(thread);
+ /* Here the pthread thread id may have been recycled. */
+ GC_delete_gc_thread(thread, thread_gc_id);
UNLOCK();
- result = pthread_detach(thread);
- if (result == 0) {
- LOCK();
- thread_gc_id -> flags |= DETACHED;
- /* Here the pthread thread id may have been recycled. */
- if (thread_gc_id -> flags & FINISHED) {
- GC_delete_gc_thread(thread, thread_gc_id);
- }
- UNLOCK();
- }
return result;
}
me -> flags = si -> flags;
me -> stack = si -> stack;
me -> stack_size = si -> stack_size;
+#ifdef STACK_GROWS_UP
me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
+#else
+ /* stack_ptr needs to point to the hot part of the stack (or conservatively, past it) */
+ me -> stack_ptr = (ptr_t)si -> stack;
+#endif
UNLOCK();
start = si -> start_routine;
start_arg = si -> arg;
return(result);
}
-# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
+# if defined(GC_AIX_THREADS)
+ /* pthread_attr_t is not a structure, thus a simple structure copy */
+ /* won't work. */
+ static void copy_attr(pthread_attr_t * pa_ptr,
+ const pthread_attr_t * source) {
+ int tmp;
+ size_t stmp;
+ void * vtmp;
+ struct sched_param sp_tmp;
+#ifndef GC_AIX_THREADS
+ pthread_spu_t ps_tmp;
+#endif
+ (void) pthread_attr_init(pa_ptr);
+ (void) pthread_attr_getdetachstate(source, &tmp);
+ (void) pthread_attr_setdetachstate(pa_ptr, tmp);
+ (void) pthread_attr_getinheritsched(source, &tmp);
+ (void) pthread_attr_setinheritsched(pa_ptr, tmp);
+ (void) pthread_attr_getschedpolicy(source, &tmp);
+ (void) pthread_attr_setschedpolicy(pa_ptr, tmp);
+ (void) pthread_attr_getstacksize(source, &stmp);
+ (void) pthread_attr_setstacksize(pa_ptr, stmp);
+ (void) pthread_attr_getguardsize(source, &stmp);
+ (void) pthread_attr_setguardsize(pa_ptr, stmp);
+ (void) pthread_attr_getstackaddr(source, &vtmp);
+ (void) pthread_attr_setstackaddr(pa_ptr, vtmp);
+ (void) pthread_attr_getscope(source, &tmp);
+ (void) pthread_attr_setscope(pa_ptr, tmp);
+ (void) pthread_attr_getschedparam(source, &sp_tmp);
+ (void) pthread_attr_setschedparam(pa_ptr, &sp_tmp);
+#ifndef GC_AIX_THREADS
+ (void) pthread_attr_getprocessor_np(source, &ps_tmp, &tmp);
+ (void) pthread_attr_setprocessor_np(pa_ptr, ps_tmp, tmp);
+#endif
+ }
+# else
+# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
+# endif
int
GC_pthread_create(pthread_t *new_thread,
int detachstate;
word my_flags = 0;
struct start_info * si = GC_malloc(sizeof(struct start_info));
- /* This is otherwise saved only in an area mmapped by the thread */
- /* library, which isn't visible to the collector. */
+
+ /* This is otherwise saved only in an area mmapped by the thread */
+ /* library, which isn't visible to the collector. */
if (0 == si) return(ENOMEM);
if (0 != sem_init(&(si -> registered), 0, 0)) {
si -> start_routine = start_routine;
si -> arg = arg;
LOCK();
- if (!GC_is_initialized) GC_init();
+ if (!GC_thr_initialized) GC_thr_init();
+
if (NULL == attr) {
stack = 0;
(void) pthread_attr_init(&new_attr);
}
pthread_attr_getstacksize(&new_attr, &stacksize);
pthread_attr_getdetachstate(&new_attr, &detachstate);
- if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
- if (0 == stack) {
- stack = (void *)GC_stack_alloc(&stacksize);
- if (0 == stack) {
- UNLOCK();
- return(ENOMEM);
- }
- pthread_attr_setstackaddr(&new_attr, stack);
+#ifdef GC_AIX_THREADS
+ GC_min_stack_sz = 5*1048576;
+ if (stacksize < GC_min_stack_sz) {
+ stacksize = GC_min_stack_sz;
+ }
+ { int alignment = 16*1024; /* size must be multiple of 16KB greater than 56KB */
+ int minval = 56*1024;
+ if ((stacksize - minval) % alignment != 0) {
+ stacksize = minval + alignment * ((stacksize-minval)/alignment + 1);
+ }
+ }
+#endif
+ if (0 == stack) {
+ stack = (void *)GC_stack_alloc(&stacksize);
+ if (0 == stack) {
+ UNLOCK();
+ return(ENOMEM);
+ }
+ pthread_attr_setstacksize(&new_attr, stacksize);
+#ifdef GC_AIX_THREADS
+ pthread_attr_setstackaddr(&new_attr, ((char *)stack)+stacksize);
+#else
+ pthread_attr_setstackaddr(&new_attr, stack);
+#endif
} else {
- my_flags |= CLIENT_OWNS_STACK;
+ my_flags |= CLIENT_OWNS_STACK;
}
+
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
si -> flags = my_flags;
si -> stack = stack;
si -> stack_size = stacksize;
- result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
+ result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
+
if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
GC_stack_free(stack, stacksize);
- }
+ }
UNLOCK();
/* Wait until child has been added to the thread table. */
/* This also ensures that we hold onto si until the child is done */
}
}
sem_destroy(&(si -> registered));
- pthread_attr_destroy(&new_attr); /* Probably unnecessary under Irix */
+ pthread_attr_destroy(&new_attr);
+
return(result);
}
-VOLATILE GC_bool GC_collecting = 0;
- /* A hint that we're in the collector and */
+/* For now we use the pthreads locking primitives on HP/UX */
+
+VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and */
/* holding the allocation lock for an */
/* extended period. */
#define SLEEP_THRESHOLD 3
-unsigned long GC_allocate_lock = 0;
-# define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
-# define GC_LOCK_TAKEN GC_allocate_lock
+volatile unsigned int GC_allocate_lock = 0;
+#define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
+#define GC_LOCK_TAKEN GC_allocate_lock
void GC_lock()
{
}
}
-# else
+# else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
#ifndef LINT
int GC_no_Irix_threads;
#endif
-# endif /* GC_IRIX_THREADS */
+# endif /* IRIX_THREADS */
#! /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.2alpha5.
+# Generated by GNU Autoconf 2.53 for gc 6.2alpha6.
#
# Report bugs to <Hans.Boehm@hp.com>.
#
# Identity of this package.
PACKAGE_NAME='gc'
PACKAGE_TARNAME='gc'
-PACKAGE_VERSION='6.2alpha5'
-PACKAGE_STRING='gc 6.2alpha5'
+PACKAGE_VERSION='6.2alpha6'
+PACKAGE_STRING='gc 6.2alpha6'
PACKAGE_BUGREPORT='Hans.Boehm@hp.com'
ac_unique_file="gcj_mlc.c"
# 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.2alpha5 to adapt to many kinds of systems.
+\`configure' configures gc 6.2alpha6 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of gc 6.2alpha5:";;
+ short | recursive ) echo "Configuration of gc 6.2alpha6:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
-gc configure 6.2alpha5
+gc configure 6.2alpha6
generated by GNU Autoconf 2.53
Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
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.2alpha5, which was
+It was created by gc $as_me 6.2alpha6, which was
generated by GNU Autoconf 2.53. Invocation command line was
$ $0 $@
# Define the identity of the package.
PACKAGE=gc
- VERSION=6.2alpha5
+ VERSION=6.2alpha6
cat >>confdefs.h <<_ACEOF
cat >>confdefs.h <<\_ACEOF
#define _REENTRANT 1
+_ACEOF
+
+ ;;
+ *-*-aix*)
+ cat >>confdefs.h <<\_ACEOF
+#define GC_AIX_THREADS 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define _REENTRANT 1
_ACEOF
;;
;;
*-*-darwin*)
cat >>confdefs.h <<\_ACEOF
-#define GC_MACOSX_THREADS 1
+#define GC_DARWIN_THREADS 1
_ACEOF
+ cat >>confdefs.h <<\_ACEOF
+#define THREAD_LOCAL_ALLOC 1
+_ACEOF
+
+ if test "${enable_parallel_mark}" = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define PARALLEL_MARK 1
+_ACEOF
+
+ fi
;;
*-*-osf*)
cat >>confdefs.h <<\_ACEOF
esac
-# I'm not too familiar with autoconf, is this the best way to do this? -Brian
+case "$host" in
+ powerpc-*-darwin*)
+ powerpc_darwin=true
+ ;;
+esac
+
+
+if test x$powerpc_darwin = xtrue; then
+ POWERPC_DARWIN_TRUE=
+ POWERPC_DARWIN_FALSE='#'
+else
+ POWERPC_DARWIN_TRUE='#'
+ POWERPC_DARWIN_FALSE=
+fi
+
+
+# We never want libdl on darwin. It is a fake libdl that just ends up making
+# dyld calls anyway
case "$host" in
*-*-darwin*) ;;
*)
esac
if test "${enable_cplusplus}" = yes; then
- addlibs="$addlibs libgccpp.la"
addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h"
addtests="$addtests test_cpp"
fi
+if test "${enable_cplusplus}" = yes; then
+ CPLUSPLUS_TRUE=
+ CPLUSPLUS_FALSE='#'
+else
+ CPLUSPLUS_TRUE='#'
+ CPLUSPLUS_FALSE=
+fi
+
+
+
+
case $host in
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5347 "configure"' > conftest.$ac_ext
+ echo '#line 5394 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -o out/conftest2.$ac_objext"
compiler_c_o=no
-if { (eval echo configure:5883: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
+if { (eval echo configure:5930: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s out/conftest.err; then
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 7676 "configure"
+#line 7723 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 7774 "configure"
+#line 7821 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
Usually this means the macro was only invoked conditionally." >&2;}
{ (exit 1); exit 1; }; }
fi
+if test -z "${POWERPC_DARWIN_TRUE}" && test -z "${POWERPC_DARWIN_FALSE}"; then
+ { { echo "$as_me:$LINENO: error: conditional \"POWERPC_DARWIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"POWERPC_DARWIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${CPLUSPLUS_TRUE}" && test -z "${CPLUSPLUS_FALSE}"; then
+ { { echo "$as_me:$LINENO: error: conditional \"CPLUSPLUS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"CPLUSPLUS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
if test -z "${USE_LIBDIR_TRUE}" && test -z "${USE_LIBDIR_FALSE}"; then
{ { echo "$as_me:$LINENO: error: conditional \"USE_LIBDIR\" was never defined.
Usually this means the macro was only invoked conditionally." >&5
} >&5
cat >&5 <<_CSEOF
-This file was extended by gc $as_me 6.2alpha5, which was
+This file was extended by gc $as_me 6.2alpha6, which was
generated by GNU Autoconf 2.53. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-gc config.status 6.2alpha5
+gc config.status 6.2alpha6
configured by $0, generated by GNU Autoconf 2.53,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
s,@MAINT@,$MAINT,;t t
s,@GC_CFLAGS@,$GC_CFLAGS,;t t
s,@THREADLIBS@,$THREADLIBS,;t t
+s,@POWERPC_DARWIN_TRUE@,$POWERPC_DARWIN_TRUE,;t t
+s,@POWERPC_DARWIN_FALSE@,$POWERPC_DARWIN_FALSE,;t t
s,@EXTRA_TEST_LIBS@,$EXTRA_TEST_LIBS,;t t
s,@target_all@,$target_all,;t t
+s,@CPLUSPLUS_TRUE@,$CPLUSPLUS_TRUE,;t t
+s,@CPLUSPLUS_FALSE@,$CPLUSPLUS_FALSE,;t t
s,@INCLUDES@,$INCLUDES,;t t
s,@CXXINCLUDES@,$CXXINCLUDES,;t t
s,@addobjs@,$addobjs,;t t
# Initialization
# ==============
-AC_INIT(gc,6.2alpha5,Hans.Boehm@hp.com)
+AC_INIT(gc,6.2alpha6,Hans.Boehm@hp.com)
## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)?
AC_CONFIG_SRCDIR(gcj_mlc.c)
AC_CANONICAL_TARGET
AC_DEFINE(GC_LINUX_THREADS)
AC_DEFINE(_REENTRANT)
;;
+ *-*-aix*)
+ AC_DEFINE(GC_AIX_THREADS)
+ AC_DEFINE(_REENTRANT)
+ ;;
*-*-hpux*)
AC_MSG_WARN("Only HP/UX 11 threads are supported.")
AC_DEFINE(GC_HPUX_THREADS)
AC_DEFINE(GC_WIN32_THREADS)
;;
*-*-darwin*)
- AC_DEFINE(GC_MACOSX_THREADS)
+ AC_DEFINE(GC_DARWIN_THREADS)
+ AC_DEFINE(THREAD_LOCAL_ALLOC)
+ if test "${enable_parallel_mark}" = yes; then
+ AC_DEFINE(PARALLEL_MARK)
+ fi
;;
*-*-osf*)
AC_DEFINE(GC_OSF1_THREADS)
esac
AC_SUBST(THREADLIBS)
-# I'm not too familiar with autoconf, is this the best way to do this? -Brian
+case "$host" in
+ powerpc-*-darwin*)
+ powerpc_darwin=true
+ ;;
+esac
+AM_CONDITIONAL(POWERPC_DARWIN,test x$powerpc_darwin = xtrue)
+
+# We never want libdl on darwin. It is a fake libdl that just ends up making
+# dyld calls anyway
case "$host" in
*-*-darwin*) ;;
*)
esac
if test "${enable_cplusplus}" = yes; then
- addlibs="$addlibs libgccpp.la"
addincludes="$addincludes include/gc_cpp.h include/gc_allocator.h"
addtests="$addtests test_cpp"
fi
+AM_CONDITIONAL(CPLUSPLUS, test "${enable_cplusplus}" = yes)
+
AC_SUBST(CXX)
AC_SUBST(INCLUDES)
--- /dev/null
+#include "private/pthread_support.h"
+
+# 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
+ be allocated, is called the red zone. This area as shown in Figure 3-2 may
+ be used for any purpose as long as a new stack frame does not need to be
+ added to the stack."
+
+ Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
+ it must set up a stack frame just like routines that call other routines."
+*/
+#define PPC_RED_ZONE_SIZE 224
+
+void GC_push_all_stacks() {
+ int i;
+ kern_return_t r;
+ GC_thread p;
+ pthread_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();
+ 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...) */
+}
+
+/* Caller holds allocation lock. */
+void GC_stop_world()
+{
+ int i;
+ GC_thread p;
+ pthread_t my_thread = pthread_self();
+ kern_return_t kern_result;
+
+ #if DEBUG_THREADS
+ GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
+ #endif
+
+ /* Make sure all free list construction has stopped before we start. */
+ /* No new construction can start, since free list construction is */
+ /* required to acquire and release the GC lock before it starts, */
+ /* and we have the lock. */
+# ifdef PARALLEL_MARK
+ GC_acquire_mark_lock();
+ GC_ASSERT(GC_fl_builder_count == 0);
+ /* We should have previously waited for it to become zero. */
+# endif /* PARALLEL_MARK */
+
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> id == my_thread) 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");
+
+ /* abort any mach calls */
+ kern_result = thread_abort(p->stop_info.mach_thread);
+ /* This shouldn't really be fatal, I don't think. The documentation is kind of unclear */
+ if(kern_result != KERN_SUCCESS) GC_printf1("thread_abort_safely failed (%ul)",kern_result);
+ }
+ }
+
+# ifdef MPROTECT_VDB
+ 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());
+ #endif
+}
+
+/* Caller holds allocation lock, and has held it continuously since */
+/* the world stopped. */
+void GC_start_world()
+{
+ pthread_t my_thread = pthread_self();
+ int i;
+ GC_thread p;
+ kern_return_t kern_result;
+
+# if DEBUG_THREADS
+ GC_printf0("World starting\n");
+# endif
+
+# ifdef MPROTECT_VDB
+ 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");
+ }
+ }
+ #if DEBUG_THREADS
+ GC_printf0("World started\n");
+ #endif
+}
+
+void GC_stop_init() {
+
+}
+
+#endif
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.2alpha5 of a conservative garbage collector for C and C++.
+This is version 6.2alpha6 of a conservative garbage collector for C and C++.
You might find a more recent version of this at
-While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested
-it on MacOS X Server.
-I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is
-no longer necessary. Incremental collection is supported via mprotect/signal.
-The current solution isn't really optimal because the signal handler must decode
-the faulting PPC machine instruction in order to find the correct heap address.
-Further, it must poke around in the register state which the kernel saved away
-in some obscure register state structure before it calls the signal handler -
-needless to say the layout of this structure is no where documented.
-Threads and dynamic libraries are not yet supported (adding dynamic library
-support via the low-level dyld API shouldn't be that hard).
-
-The original MacOS X port was brought to you by Andrew Stone.
-
-
-June, 1 2000
-
-Dietmar Planitzer
-dave.pl@ping.at
-
-Note from Andrew Begel:
-
-One more fix to enable gc.a to link successfully into a shared library for
-MacOS X. You have to add -fno-common to the CFLAGS in the Makefile. MacOSX
-disallows common symbols in anything that eventually finds its way into a
-shared library. (I don't completely understand why, but -fno-common seems to
-work and doesn't mess up the garbage collector's functionality).
-
-Feb 26, 2003
-
-Jeff Sturm and Jesse Rosenstock provided a patch that adds thread support.
-GC_MACOSX_THREADS should be defined in the build and in clients. Real
-dynamic library support is still missing, i.e. dynamic library data segments
-are still not scanned. Code that stores pointers to the garbage collected
-heap in statically allocated variables should not reside in a dynamic
-library. This still doesn't appear to be 100% reliable.
-
-Mar 10, 2003
-Brian Alliet contributed dynamic library support for MacOSX. It could also
-use more testing.
+See README.darwin for the latest Darwin/MacOSX information.
Since there are unavoidable problems with C programs linking against a
dynamic library that includes C++ code, I separated out the c++ code into
libgccpp.
+
+Since 6.2alpha5:
+ - There was extra underscore in the name of GC_save_registers_in_stack
+ for NetBSD/SPARC. (Thanks to Jaap Boender for the patch.)
+ - Integrated Brian Alliet's patch for Darwin. This restructured the
+ linuxthreads/pthreads support to separate generic pthreads support
+ from more the system-dependent thread-stopping code. I believe this
+ should make it easier to eliminate the code duplication between
+ pthreads platforms in the future. The patch included some other
+ code cleanups.
+ - Integrated Dan Bonachea's patch to support AIX threads. This required
+ substantial manual integration, mostly due to conflicts with other
+ recent threads changes. It may take another iteration to
+ get it to work.
+ - Removed HPUX/PA-RISC support from aix_irix_threads.c. It wasn't used
+ anyway and it cluttered up the code. And anything we can do to migrate
+ towards generic pthreads support is a good thing.
+ - Added a more explicit test for tracing of function arguments to test.c.
+ (Thanks to Dan Grayson.)
+ - Added Akira Tagoh's PowerPC64 patch.
+ - Fixed some bit rot in the Cygwin port. (Thanks to Dan Bonachea for
+ pointing it out.) Gc.h now includes just windows.h, not winbase.h.
+ - Declared GC_save_regs_in_stack() in gc_priv.h. Remove other declarations.
+ - Changed --enable-cplusplus to use automake consitionals. The old way
+ confused libtool. "Make install" didn't work correctly for the old version.
+ Previously --enable-cplusplus was broken on cygwin.
+ - Changed the C version of GC_push_regs to fail at compile time if it is
+ generated with an empty body. This seems to have been the cause of one
+ or two subtle failures on unusual platforms. Those failures should
+ now occur at build time and be easily fixable.
To do:
- - MacOSX thread support still appears to be a bit unreliable.
- A dynamic libgc.so references dlopen unconditionally, but doesn't link
against libdl.
- GC_proc_fd for Solaris is not correctly updated in response to a
!defined(RS6000) && !defined(SCO_ELF) && !defined(DGUX) && \
!(defined(FREEBSD) && defined(__ELF__)) && \
!(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
- !defined(MACOSX)
+ !defined(DARWIN)
--> We only know how to find data segments of dynamic libraries for the
--> above. Additional SVR4 variants might not be too
--> hard to add.
len = ldi->ldinfo_next;
GC_add_roots_inner(
ldi->ldinfo_dataorg,
- (unsigned long)ldi->ldinfo_dataorg
+ (ptr_t)(unsigned long)ldi->ldinfo_dataorg
+ ldi->ldinfo_datasize,
TRUE);
ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
}
#endif /* RS6000 */
-#ifdef MACOSX
+#ifdef DARWIN
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
-/*#define MACOSX_DEBUG */
+/*#define DARWIN_DEBUG*/
-void GC_register_dynamic_libraries()
-{
- unsigned long image_count;
- const struct mach_header *mach_header;
- const struct section *sec;
- unsigned long slide;
- unsigned long filetype;
- int i,j;
- unsigned long start;
- unsigned long end;
-
- static struct {
+const static struct {
const char *seg;
const char *sect;
- } sections[] = {
+} GC_dyld_sections[] = {
{ SEG_DATA, SECT_DATA },
{ SEG_DATA, SECT_BSS },
{ SEG_DATA, SECT_COMMON }
- };
+};
- image_count = _dyld_image_count();
- for(i=0;i<image_count;i++)
- {
- mach_header = _dyld_get_image_header(i);
- slide = _dyld_get_image_vmaddr_slide(i);
- filetype = mach_header->filetype;
+#ifdef DARWIN_DEBUG
+static const char *GC_dyld_name_for_hdr(struct mach_header *hdr) {
+ unsigned long i,c;
+ c = _dyld_image_count();
+ for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
+ return _dyld_get_image_name(i);
+ return NULL;
+}
+#endif
- for(j=0;j<sizeof(sections)/sizeof(sections[0]);j++) {
- sec = getsectbynamefromheader(mach_header,sections[j].seg,sections[j].sect);
+/* This should never be called by a thread holding the lock */
+static void GC_dyld_image_add(struct mach_header* hdr, unsigned long slide) {
+ unsigned long start,end,i;
+ const struct section *sec;
+ for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
+ sec = getsectbynamefromheader(
+ hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
if(sec == NULL || sec->size == 0) continue;
start = slide + sec->addr;
end = start + sec->size;
-# ifdef MACOSX_DEBUG
+# ifdef DARWIN_DEBUG
GC_printf4("Adding section at %p-%p (%lu bytes) from image %s\n",
- start,end,sec->size,_dyld_get_image_name(i));
+ start,end,sec->size,GC_dyld_name_for_hdr(hdr));
# endif
-
- GC_add_roots_inner((char*)start,(char*)end,
- filetype == MH_EXECUTE ? FALSE : TRUE);
+ GC_add_roots((char*)start,(char*)end);
}
+# ifdef DARWIN_DEBUG
+ GC_print_static_roots();
+# endif
+}
+
+/* This should never be called by a thread holding the lock */
+static void GC_dyld_image_remove(struct mach_header* hdr, unsigned long slide) {
+ unsigned long start,end,i;
+ const struct section *sec;
+ for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
+ sec = getsectbynamefromheader(
+ hdr,GC_dyld_sections[i].seg,GC_dyld_sections[i].sect);
+ if(sec == NULL || sec->size == 0) continue;
+ start = slide + sec->addr;
+ end = start + sec->size;
+# ifdef DARWIN_DEBUG
+ GC_printf4("Removing section at %p-%p (%lu bytes) from image %s\n",
+ start,end,sec->size,GC_dyld_name_for_hdr(hdr));
+# endif
+ GC_remove_roots((char*)start,(char*)end);
}
+# ifdef DARWIN_DEBUG
+ GC_print_static_roots();
+# endif
+}
+
+void GC_register_dynamic_libraries() {
+ /* Currently does nothing. The callbacks are setup by GC_init_dyld()
+ The dyld library takes it from there. */
+}
+
+/* The _dyld_* functions have an internal lock so no _dyld functions
+ can be called while the world is stopped without the risk of a deadlock.
+ Because of this we MUST setup callbacks BEFORE we ever stop the world.
+ This should be called BEFORE any thread in created and WITHOUT the
+ allocation lock held. */
+
+void GC_init_dyld() {
+ static GC_bool initialized = FALSE;
+
+ if(initialized) return;
+
+# ifdef DARWIN_DEBUG
+ GC_printf0("Forcing full bind of GC code...\n");
+# endif
+ if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
+ GC_abort("_dyld_bind_fully_image_containing_addres failed");
+
+# ifdef DARWIN_DEBUG
+ GC_printf0("Registering dyld callbacks...\n");
+# endif
+
+ /* Apple's Documentation:
+ When you call _dyld_register_func_for_add_image, the dynamic linker runtime
+ calls the specified callback (func) once for each of the images that is
+ currently loaded into the program. When a new image is added to the program,
+ your callback is called again with the mach_header for the new image, and the virtual memory slide amount of the new image.
+
+ This WILL properly register existing and all future libraries
+ */
+
+ _dyld_register_func_for_add_image(GC_dyld_image_add);
+ _dyld_register_func_for_remove_image(GC_dyld_image_remove);
+ initialized = TRUE;
}
#define HAVE_REGISTER_MAIN_STATIC_DATA
GC_bool GC_register_main_static_data()
{
+ /* Already done through dyld callbacks */
return FALSE;
}
-#endif /* MACOSX */
+#endif /* DARWIN */
#else /* !DYNAMIC_LOADING */
#include "private/gc_priv.h"
-# if (defined(GC_PTHREADS) && !defined(GC_MACOSX_THREADS)) \
+# if (defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS)) \
|| defined(GC_SOLARIS_THREADS)
# if defined(dlopen) && !defined(GC_USE_LD_WRAP)
private/gc_pmark.h private/gc_locks.h \
private/solaris_threads.h private/dbg_mlc.h \
private/specific.h private/cord_pos.h \
+ private/pthread_support.h private/pthread_stop_world.h \
+ private/darwin_semaphore.h private/darwin_stop_world.h \
cord.h ec.h javaxfc.h
private/gc_pmark.h private/gc_locks.h \
private/solaris_threads.h private/dbg_mlc.h \
private/specific.h private/cord_pos.h \
+ private/pthread_support.h private/pthread_stop_world.h \
+ private/darwin_semaphore.h private/darwin_stop_world.h \
cord.h ec.h javaxfc.h
subdir = include
GC_API void GC_add_roots GC_PROTO((char * low_address,
char * high_address_plus_1));
+/* Remove a root segment. Wizards only. */
+GC_API void GC_remove_roots GC_PROTO((char * low_address,
+ char * high_address_plus_1));
+
/* Add a displacement to the set of those considered valid by the */
/* collector. GC_register_displacement(n) means that if p was returned */
/* by GC_malloc, then (char *)p + n will be considered to be a valid */
#endif /* THREADS && !SRC_M3 */
-#if defined(GC_WIN32_THREADS)
+#if defined(GC_WIN32_THREADS) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)
# include <windows.h>
-# include <winbase.h>
/*
* All threads must be created using GC_CreateThread, so that they will be
# endif
# endif /* defined(_WIN32_WCE) */
-#endif /* defined(GC_WIN32_THREADS) */
+#endif /* defined(GC_WIN32_THREADS) && !cygwin */
/*
* If you are planning on putting
*/
# define GC_INIT() { GC_add_roots(DATASTART, DATAEND); }
# else
+# if defined(__APPLE__) && defined(__MACH__)
+# define GC_INIT() { GC_init(); }
+# else
# define GC_INIT()
+# endif
# endif
#endif
# define GC_DGUX386_THREADS
# endif
#endif
+#if defined(AIX_THREADS)
+# define GC_AIX_THREADS
+#endif
#if defined(HPUX_THREADS)
# define GC_HPUX_THREADS
#endif
#if !defined(_REENTRANT) && (defined(GC_SOLARIS_THREADS) \
|| defined(GC_SOLARIS_PTHREADS) \
|| defined(GC_HPUX_THREADS) \
+ || defined(GC_AIX_THREADS) \
|| defined(GC_LINUX_THREADS))
# define _REENTRANT
/* Better late than never. This fails if system headers that */
# if defined(GC_SOLARIS_PTHREADS) || defined(GC_FREEBSD_THREADS) || \
defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) || \
defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) || \
- defined(GC_DGUX386_THREADS) || defined(GC_MACOSX_THREADS) || \
+ defined(GC_DGUX386_THREADS) || defined(GC_DARWIN_THREADS) || \
+ defined(GC_AIX_THREADS) || \
(defined(GC_WIN32_THREADS) && defined(__CYGWIN32__))
# define GC_PTHREADS
# endif
# define GC_PTHREADS
# endif
# if defined(__APPLE__) && defined(__MACH__) && defined(__ppc__)
-# define GC_MACOSX_THREADS
+# define GC_DARWIN_THREADS
# define GC_PTHREADS
# endif
# if !defined(GC_PTHREADS) && defined(__FreeBSD__)
int GC_pthread_create(pthread_t *new_thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
+#ifndef GC_DARWIN_THREADS
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
+#endif
int GC_pthread_join(pthread_t thread, void **retval);
int GC_pthread_detach(pthread_t thread);
# define pthread_create GC_pthread_create
+#ifndef GC_DARWIN_THREADS
# define pthread_sigmask GC_pthread_sigmask
+#endif
# define pthread_join GC_pthread_join
# define pthread_detach GC_pthread_detach
+#ifndef GC_DARWIN_THREADS
# define dlopen GC_dlopen
+#endif
#endif /* GC_xxxxx_THREADS */
--- /dev/null
+#ifndef GC_DARWIN_SEMAPHORE_H
+#define GC_DARWIN_SEMAPHORE_H
+
+#if !defined(GC_DARWIN_THREADS)
+#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
+#endif
+
+/*
+ This is a very simple semaphore implementation for darwin. It
+ is implemented in terms of pthreads calls so it isn't async signal
+ safe. This isn't a problem because signals aren't used to
+ suspend threads on darwin.
+*/
+
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int value;
+} sem_t;
+
+static int sem_init(sem_t *sem, int pshared, int value) {
+ int ret;
+ if(pshared)
+ GC_abort("sem_init with pshared set");
+ sem->value = value;
+
+ ret = pthread_mutex_init(&sem->mutex,NULL);
+ if(ret < 0) return -1;
+ ret = pthread_cond_init(&sem->cond,NULL);
+ if(ret < 0) return -1;
+ return 0;
+}
+
+static int sem_post(sem_t *sem) {
+ if(pthread_mutex_lock(&sem->mutex) < 0)
+ return -1;
+ sem->value++;
+ if(pthread_cond_signal(&sem->cond) < 0) {
+ pthread_mutex_unlock(&sem->mutex);
+ return -1;
+ }
+ if(pthread_mutex_unlock(&sem->mutex) < 0)
+ return -1;
+ return 0;
+}
+
+static int sem_wait(sem_t *sem) {
+ if(pthread_mutex_lock(&sem->mutex) < 0)
+ return -1;
+ while(sem->value == 0) {
+ pthread_cond_wait(&sem->cond,&sem->mutex);
+ }
+ sem->value--;
+ if(pthread_mutex_unlock(&sem->mutex) < 0)
+ return -1;
+ return 0;
+}
+
+static int sem_destroy(sem_t *sem) {
+ int ret;
+ ret = pthread_cond_destroy(&sem->cond);
+ if(ret < 0) return -1;
+ ret = pthread_mutex_destroy(&sem->mutex);
+ if(ret < 0) return -1;
+ return 0;
+}
+
+#endif
--- /dev/null
+#ifndef GC_DARWIN_STOP_WORLD_H
+#define GC_DARWIN_STOP_WORLD_H
+
+#if !defined(GC_DARWIN_THREADS)
+#error darwin_stop_world.h included without GC_DARWIN_THREADS defined
+#endif
+
+#include <mach/mach.h>
+#include <mach/thread_act.h>
+
+struct thread_stop_info {
+ mach_port_t mach_thread;
+};
+
+#endif
"\tbne 2f\n" /* non-zero, return already set */
"\tstwcx. %2,0,%1\n" /* else store conditional */
"\tbne- 1b\n" /* retry if lost reservation */
+ "\tsync\n" /* import barrier */
"2:\t\n" /* oldval is zero if we set */
: "=&r"(oldval), "=p"(addr)
: "r"(temp), "1"(addr)
- : "memory");
+ : "cr0","memory");
return oldval;
}
# define GC_TEST_AND_SET_DEFINED
# elif __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \
|| !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700
# ifdef __GNUC__
-# define GC_test_and_set(addr) _test_and_set(addr,1)
+# define GC_test_and_set(addr) _test_and_set((void *)addr,1)
# else
-# define GC_test_and_set(addr) test_and_set(addr,1)
+# define GC_test_and_set(addr) test_and_set((void *)addr,1)
# endif
# else
-# define GC_test_and_set(addr) __test_and_set(addr,1)
+# define GC_test_and_set(addr) __test_and_set32((void *)addr,1)
# define GC_clear(addr) __lock_release(addr);
# define GC_CLEAR_DEFINED
# endif
# define GC_TEST_AND_SET_DEFINED
# endif /* MIPS */
+# if defined(_AIX)
+# include <sys/atomic_op.h>
+# if (defined(_POWER) || defined(_POWERPC))
+# if defined(__GNUC__)
+ inline static void GC_memsync() {
+ __asm__ __volatile__ ("sync" : : : "memory");
+ }
+# else
+# ifndef inline
+# define inline __inline
+# endif
+# pragma mc_func GC_memsync { \
+ "7c0004ac" /* sync (same opcode used for dcs)*/ \
+ }
+# endif
+# else
+# error dont know how to memsync
+# endif
+ inline static int GC_test_and_set(volatile unsigned int * addr) {
+ int oldvalue = 0;
+ if (compare_and_swap((void *)addr, &oldvalue, 1)) {
+ GC_memsync();
+ return 0;
+ } else return 1;
+ }
+# define GC_TEST_AND_SET_DEFINED
+ inline static void GC_clear(volatile unsigned int *addr) {
+ GC_memsync();
+ *(addr) = 0;
+ }
+# define GC_CLEAR_DEFINED
+
+# endif
# if 0 /* defined(HP_PA) */
/* The official recommendation seems to be to not use ldcw from */
/* user mode. Since multithreaded incremental collection doesn't */
__asm__ __volatile__("" : : : "memory");
}
# endif /* I386 */
+
+# if defined(POWERPC)
+# if !defined(GENERIC_COMPARE_AND_SWAP)
+ /* Returns TRUE if the comparison succeeded. */
+ inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
+ GC_word old, GC_word new_val)
+ {
+ int result, dummy;
+ __asm__ __volatile__(
+ "1:\tlwarx %0,0,%5\n"
+ "\tcmpw %0,%4\n"
+ "\tbne 2f\n"
+ "\tstwcx. %3,0,%2\n"
+ "\tbne- 1b\n"
+ "\tsync\n"
+ "\tli %1, 1\n"
+ "\tb 3f\n"
+ "2:\tli %1, 0\n"
+ "3:\t\n"
+ : "=&r" (dummy), "=r" (result), "=p" (addr)
+ : "r" (new_val), "r" (old), "2"(addr)
+ : "cr0","memory");
+ return (GC_bool) result;
+ }
+# endif /* !GENERIC_COMPARE_AND_SWAP */
+ inline static void GC_memory_barrier()
+ {
+ __asm__ __volatile__("sync" : : : "memory");
+ }
+# endif /* POWERPC */
+
# if defined(IA64)
# if !defined(GENERIC_COMPARE_AND_SWAP)
inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr,
{ GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
pthread_mutex_unlock(&GC_allocate_ml); }
# else /* !GC_ASSERTIONS */
+# if defined(NO_PTHREAD_TRYLOCK)
+# define LOCK() GC_lock();
+# else /* !defined(NO_PTHREAD_TRYLOCK) */
# define LOCK() \
{ if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); }
+# endif
# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
# endif /* !GC_ASSERTIONS */
# endif /* USE_PTHREAD_LOCKS */
/* on Irix anymore. */
# include <mutex.h>
- extern unsigned long GC_allocate_lock;
+ extern volatile unsigned int GC_allocate_lock;
/* This is not a mutex because mutexes that obey the (optional) */
/* POSIX scheduling rules are subject to convoys in high contention */
/* applications. This is basically a spin lock. */
# include <sys/resource.h>
#endif /* BSD_TIME */
-# ifndef GC_H
-# include "gc.h"
+# ifndef _GC_H
+# include "../gc.h"
# endif
# ifndef GC_MARK_H
# include <string.h>
# define BCOPY_EXISTS
# endif
-# if defined(MACOSX)
+# if defined(DARWIN)
+# include <string.h>
# define BCOPY_EXISTS
# endif
void GC_generic_push_regs GC_PROTO((ptr_t cold_gc_frame));
# else
void GC_push_regs GC_PROTO((void));
+# endif
+# if defined(SPARC) || defined(IA64)
+ /* Cause all stacked registers to be saved in memory. Return a */
+ /* pointer to the top of the corresponding memory stack. */
+ word GC_save_regs_in_stack GC_PROTO((void));
# endif
/* Push register contents onto mark stack. */
/* If NURSERY is defined, the default push */
/* Set all mark bits associated with */
/* a free list. */
void GC_add_roots_inner GC_PROTO((char * b, char * e, GC_bool tmp));
+void GC_remove_roots_inner GC_PROTO((char * b, char * e));
GC_bool GC_is_static_root GC_PROTO((ptr_t p));
/* Is the address p in one of the registered static */
/* root sections? */
/* Make the indicated */
/* free list nonempty, and return its */
/* head. */
+
+void GC_free_inner(GC_PTR p);
void GC_init_headers GC_PROTO((void));
struct hblkhdr * GC_install_header GC_PROTO((struct hblk *h));
/* First a unified test for Linux: */
# if defined(linux) || defined(__linux__)
+# ifndef LINUX
# define LINUX
+# endif
# endif
/* And one for NetBSD: */
# define ARM32
# define mach_type_known
# endif
-# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__))
+# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) || defined(powerpc64) || defined(__powerpc64__))
# define POWERPC
# define mach_type_known
# endif
# endif
# if defined(macosx) || \
defined(__APPLE__) && defined(__MACH__) && defined(__ppc__)
-# define MACOSX
+# define DARWIN
# define POWERPC
# define mach_type_known
# endif
# if defined(__APPLE__) && defined(__MACH__) && defined(__i386__)
-# define MACOSX
+# define DARWIN
# define I386
--> Not really supported, but at least we recognize it.
# endif
/* SH ==> Hitachi SuperH */
/* (LINUX & MSWINCE) */
/* X86_64 ==> AMD x86-64 */
+ /* POWERPC ==> IBM/Apple PowerPC */
+ /* (MACOS(<=9),DARWIN(incl.MACOSX),*/
+ /* LINUX, NETBSD, NOSYS variants) */
/*
# define DATAEND /* not needed */
# endif
# ifdef LINUX
-# define ALIGNMENT 4 /* Guess. Can someone verify? */
+# if (defined (powerpc64) || defined(__powerpc64__))
+# define ALIGNMENT 8
+# define CPP_WORDSZ 64
+# else
+# define ALIGNMENT 4 /* Guess. Can someone verify? */
/* This was 2, but that didn't sound right. */
+# endif
# define OS_TYPE "LINUX"
/* HEURISTIC1 has been reliably reported to fail for a 32-bit */
/* executable on a 64 bit kernel. */
extern int _end[];
# define DATAEND (_end)
# endif
-# ifdef MACOSX
- /* There are reasons to suspect this may not be reliable. */
+# ifdef DARWIN
# define ALIGNMENT 4
-# define OS_TYPE "MACOSX"
-# ifdef GC_MACOSX_THREADS
-# define SIG_SUSPEND SIGXCPU
-# define SIG_THR_RESTART SIGXFSZ
-# endif
+# define OS_TYPE "DARWIN"
# define DYNAMIC_LOADING
- /* XXX: see get_end(3), get_etext() and get_end() should not be used */
+ /* XXX: see get_end(3), get_etext() and get_end() should not be used.
+ These aren't used when dyld support is enabled (it is by default) */
# define DATASTART ((ptr_t) get_etext())
-# define STACKBOTTOM ((ptr_t) 0xc0000000)
# define DATAEND ((ptr_t) get_end())
+# define STACKBOTTOM ((ptr_t) 0xc0000000)
# define USE_MMAP
# define USE_MMAP_ANON
-/* # define MPROTECT_VDB -- There is some evidence that this breaks
- * on some minor versions of MACOSX, i.e. 10.2.3. In theory,
- * it should be OK */
+# define USE_ASM_PUSH_REGS
+ /* This is potentially buggy. It needs more testing. See the comments in
+ os_dep.c */
+# define MPROTECT_VDB
# include <unistd.h>
# define GETPAGESIZE() getpagesize()
# if defined(USE_PPC_PREFETCH) && defined(__GNUC__)
# define PREFETCH_FOR_WRITE(x) \
__asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x)))
# endif
+ /* There seems to be some issues with trylock hanging on darwin. This
+ should be looked into some more */
+# define NO_PTHREAD_TRYLOCK
# endif
# ifdef NETBSD
# define ALIGNMENT 4
/* DATAEND = _data_end__ */
/* To get it right for both, we take the */
/* minumum/maximum of the two. */
+# ifndef MAX
# define MAX(x,y) ((x) > (y) ? (x) : (y))
+# endif
+# ifndef MIN
# define MIN(x,y) ((x) < (y) ? (x) : (y))
+# endif
# define DATASTART ((ptr_t) MIN(_data_start__, _bss_start__))
# define DATAEND ((ptr_t) MAX(_data_end__, _bss_end__))
# undef STACK_GRAN
/* heap sections so they're not */
/* considered as roots. */
# define OS_TYPE "IRIX5"
-# define MPROTECT_VDB
+/*# define MPROTECT_VDB DOB: this should work, but there is evidence */
+/* of recent breakage. */
# ifdef _MIPS_SZPTR
# define CPP_WORDSZ _MIPS_SZPTR
# define ALIGNMENT (_MIPS_SZPTR/8)
# ifdef RS6000
# define MACH_TYPE "RS6000"
+# ifdef ALIGNMENT
+# undef ALIGNMENT
+# endif
+# ifdef IA64
+# undef IA64 /* DOB: some AIX installs stupidly define IA64 in /usr/include/sys/systemcfg.h */
+# endif
# ifdef __64BIT__
# define ALIGNMENT 8
# define CPP_WORDSZ 64
-# define STACKBOTTOM 0x1000000000000000
+# define STACKBOTTOM ((ptr_t)0x1000000000000000)
# else
# define ALIGNMENT 4
# define CPP_WORDSZ 32
# define STACKBOTTOM ((ptr_t)((ulong)&errno))
# endif
+ /* From AIX linker man page:
+ _text Specifies the first location of the program.
+ _etext Specifies the first location after the program.
+ _data Specifies the first location of the data.
+ _edata Specifies the first location after the initialized data
+ _end or end Specifies the first location after all data.
+ */
extern int _data[], _end[];
# define DATASTART ((ptr_t)((ulong)_data))
# define DATAEND ((ptr_t)((ulong)_end))
# endif
# if defined(SVR4) || defined(LINUX) || defined(IRIX) || defined(HPUX) \
- || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) || defined(DGUX) \
- || defined(BSD) || defined(AIX) || defined(MACOSX) || defined(OSF1)
+ || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \
+ || defined(DGUX) || defined(BSD) \
+ || defined(_AIX) || defined(DARWIN) || defined(OSF1)
# define UNIX_LIKE /* Basic Unix-like system calls work. */
# endif
# if defined(GC_HPUX_THREADS) && !defined(HPUX)
--> inconsistent configuration
# endif
+# if defined(GC_AIX_THREADS) && !defined(_AIX)
+ --> inconsistent configuration
+# endif
# if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32)
--> inconsistent configuration
# endif
# define THREADS
# endif
-# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(MACOSX) \
+# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(DARWIN) \
|| defined(LINT) || defined(MSWINCE) || defined(ARM32) \
|| (defined(I386) && defined(__LCC__))
/* Use setjmp based hack to mark from callee-save registers. */
--- /dev/null
+#ifndef GC_PTHREAD_STOP_WORLD_H
+#define GC_PTHREAD_STOP_WORLD_H
+
+struct thread_stop_info {
+ int signal;
+ word last_stop_count; /* GC_last_stop_count value when thread */
+ /* last successfully handled a suspend */
+ /* signal. */
+ ptr_t stack_ptr; /* Valid only when stopped. */
+};
+
+#endif
--- /dev/null
+#ifndef GC_PTHREAD_SUPPORT_H
+#define GC_PTHREAD_SUPPORT_H
+
+# include "private/gc_priv.h"
+
+# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
+ && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS)
+
+#if defined(GC_DARWIN_THREADS)
+# include "private/darwin_stop_world.h"
+#else
+# include "private/pthread_stop_world.h"
+#endif
+
+/* We use the allocation lock to protect thread-related data structures. */
+
+/* The set of all known threads. We intercept thread creation and */
+/* joins. */
+/* Protected by allocation/GC lock. */
+/* Some of this should be declared volatile, but that's inconsistent */
+/* with some library routine declarations. */
+typedef struct GC_Thread_Rep {
+ struct GC_Thread_Rep * next; /* More recently allocated threads */
+ /* with a given pthread id come */
+ /* first. (All but the first are */
+ /* guaranteed to be dead, but we may */
+ /* not yet have registered the join.) */
+ pthread_t id;
+ /* Extra bookkeeping information the stopping code uses */
+ struct thread_stop_info stop_info;
+
+ short flags;
+# define FINISHED 1 /* Thread has exited. */
+# define DETACHED 2 /* Thread is intended to be detached. */
+# define MAIN_THREAD 4 /* True for the original thread only. */
+ short thread_blocked; /* Protected by GC lock. */
+ /* Treated as a boolean value. If set, */
+ /* thread will acquire GC lock before */
+ /* doing any pointer manipulations, and */
+ /* has set its sp value. Thus it does */
+ /* not need to be sent a signal to stop */
+ /* it. */
+ ptr_t stack_end; /* Cold end of the stack. */
+# ifdef IA64
+ ptr_t backing_store_end;
+ ptr_t backing_store_ptr;
+# endif
+ void * status; /* The value returned from the thread. */
+ /* Used only to avoid premature */
+ /* reclamation of any data it might */
+ /* reference. */
+# ifdef THREAD_LOCAL_ALLOC
+# if CPP_WORDSZ == 64 && defined(ALIGN_DOUBLE)
+# define GRANULARITY 16
+# define NFREELISTS 49
+# else
+# define GRANULARITY 8
+# define NFREELISTS 65
+# endif
+ /* The ith free list corresponds to size i*GRANULARITY */
+# define INDEX_FROM_BYTES(n) ((ADD_SLOP(n) + GRANULARITY - 1)/GRANULARITY)
+# define BYTES_FROM_INDEX(i) ((i) * GRANULARITY - EXTRA_BYTES)
+# define SMALL_ENOUGH(bytes) (ADD_SLOP(bytes) <= \
+ (NFREELISTS-1)*GRANULARITY)
+ ptr_t ptrfree_freelists[NFREELISTS];
+ ptr_t normal_freelists[NFREELISTS];
+# ifdef GC_GCJ_SUPPORT
+ ptr_t gcj_freelists[NFREELISTS];
+# endif
+ /* Free lists contain either a pointer or a small count */
+ /* reflecting the number of granules allocated at that */
+ /* size. */
+ /* 0 ==> thread-local allocation in use, free list */
+ /* empty. */
+ /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */
+ /* too few objects of this size have been */
+ /* allocated by this thread. */
+ /* >= HBLKSIZE => pointer to nonempty free list. */
+ /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */
+ /* local alloc, equivalent to 0. */
+# define DIRECT_GRANULES (HBLKSIZE/GRANULARITY)
+ /* Don't use local free lists for up to this much */
+ /* allocation. */
+# endif
+} * GC_thread;
+
+# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
+extern volatile GC_thread GC_threads[THREAD_TABLE_SZ];
+
+extern GC_bool GC_thr_initialized;
+
+GC_thread GC_lookup_thread(pthread_t id);
+
+void GC_stop_init();
+
+#endif /* GC_PTHREADS && !GC_SOLARIS_THREADS.... etc */
+#endif /* GC_PTHREAD_SUPPORT_H */
/* on your architecture. Run the test_setjmp program to see whether */
/* there is any chance it will work. */
-#ifndef USE_GENERIC_PUSH_REGS
+#if !defined(USE_GENERIC_PUSH_REGS) && !defined(USE_ASM_PUSH_REGS)
+#undef HAVE_PUSH_REGS
void GC_push_regs()
{
# ifdef RT
asm("pushl r8"); asm("calls $1,_GC_push_one");
asm("pushl r7"); asm("calls $1,_GC_push_one");
asm("pushl r6"); asm("calls $1,_GC_push_one");
+# define HAVE_PUSH_REGS
# endif
# if defined(M68K) && (defined(SUNOS4) || defined(NEXT))
/* M68K SUNOS - could be replaced by generic code */
asm("movl d7,sp@"); asm("jbsr _GC_push_one");
asm("addqw #0x4,sp"); /* put stack back where it was */
+# define HAVE_PUSH_REGS
# endif
# if defined(M68K) && defined(HP)
asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one");
asm("addq.w &0x4,%sp"); /* put stack back where it was */
+# define HAVE_PUSH_REGS
# endif /* M68K HP */
# if defined(M68K) && defined(AMIGA)
asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one");
asm("addq.w &0x4,%sp"); /* put stack back where it was */
+# define HAVE_PUSH_REGS
# else /* !__GNUC__ */
GC_push_one(getreg(REG_A2));
GC_push_one(getreg(REG_A3));
GC_push_one(getreg(REG_D5));
GC_push_one(getreg(REG_D6));
GC_push_one(getreg(REG_D7));
+# define HAVE_PUSH_REGS
# endif /* !__GNUC__ */
# endif /* AMIGA */
PushMacReg(d7);
add.w #4,sp ; fix stack.
}
+# define HAVE_PUSH_REGS
# undef PushMacReg
# endif /* THINK_C */
# if defined(__MWERKS__)
PushMacRegisters();
+# define HAVE_PUSH_REGS
# endif /* __MWERKS__ */
# endif /* MACOS */
asm("pushl %esi"); asm("call _GC_push_one"); asm("addl $4,%esp");
asm("pushl %edi"); asm("call _GC_push_one"); asm("addl $4,%esp");
asm("pushl %ebx"); asm("call _GC_push_one"); asm("addl $4,%esp");
+# define HAVE_PUSH_REGS
# endif
# if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \
asm("pushl %esi; call GC_push_one; addl $4,%esp");
asm("pushl %edi; call GC_push_one; addl $4,%esp");
asm("pushl %ebx; call GC_push_one; addl $4,%esp");
+# define HAVE_PUSH_REGS
# endif
# if ( defined(I386) && defined(BEOS) && defined(__ELF__) )
asm("pushl %esi; call GC_push_one; addl $4,%esp");
asm("pushl %edi; call GC_push_one; addl $4,%esp");
asm("pushl %ebx; call GC_push_one; addl $4,%esp");
+# define HAVE_PUSH_REGS
# endif
# if defined(I386) && defined(MSWIN32) && !defined(__MINGW32__) \
__asm push edi
__asm call GC_push_one
__asm add esp,4
+# define HAVE_PUSH_REGS
# endif
# if defined(I386) && (defined(SVR4) || defined(SCO) || defined(SCO_ELF))
asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp");
asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp");
+# define HAVE_PUSH_REGS
# endif
# ifdef NS32K
asm ("movd r5, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4");
asm ("movd r6, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4");
asm ("movd r7, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4");
+# define HAVE_PUSH_REGS
# endif
# if defined(SPARC)
- {
- word GC_save_regs_in_stack();
-
- GC_save_regs_ret_val = GC_save_regs_in_stack();
- }
+ GC_save_regs_ret_val = GC_save_regs_in_stack();
+# define HAVE_PUSH_REGS
# endif
# ifdef RT
asm("cas r11, r13, r0"); GC_push_one(TMP_SP); /* through */
asm("cas r11, r14, r0"); GC_push_one(TMP_SP); /* r15 */
asm("cas r11, r15, r0"); GC_push_one(TMP_SP);
+# define HAVE_PUSH_REGS
# endif
# if defined(M68K) && defined(SYSV)
asm("movl %d7,%sp@"); asm("jbsr GC_push_one");
asm("addqw #0x4,%sp"); /* put stack back where it was */
+# define HAVE_PUSH_REGS
# else /* !__GNUC__*/
asm("subq.w &0x4,%sp"); /* allocate word on top of stack */
asm("mov.l %d7,(%sp)"); asm("jsr GC_push_one");
asm("addq.w &0x4,%sp"); /* put stack back where it was */
+# define HAVE_PUSH_REGS
# endif /* !__GNUC__ */
# endif /* M68K/SYSV */
extern int *__libc_stack_end;
GC_push_all_stack (sp, __libc_stack_end);
+# define HAVE_PUSH_REGS
+ /* Isn't this redundant with the code to push the stack? */
}
# endif
/* other machines... */
-# if !defined(M68K) && !defined(VAX) && !defined(RT)
-# if !defined(SPARC) && !defined(I386) && !defined(NS32K)
-# if !defined(POWERPC) && !defined(UTS4)
-# if !defined(PJ) && !(defined(MIPS) && defined(LINUX))
- --> bad news <--
-# endif
-# endif
-# endif
+# if !defined(HAVE_PUSH_REGS)
+ --> We just generated an empty GC_push_regs, which
+ --> is almost certainly broken. Try defining
+ --> USE_GENERIC_PUSH_REGS instead.
# endif
}
-#endif /* !USE_GENERIC_PUSH_REGS */
+#endif /* !USE_GENERIC_PUSH_REGS && !USE_ASM_PUSH_REGS */
#if defined(USE_GENERIC_PUSH_REGS)
void GC_generic_push_regs(cold_gc_frame)
/* needed on IA64, since some non-windowed registers are */
/* preserved. */
{
- word GC_save_regs_in_stack();
-
GC_save_regs_ret_val = GC_save_regs_in_stack();
/* On IA64 gcc, could use __builtin_ia64_flushrs() and */
/* __builtin_ia64_flushrs(). The latter will be done */
/* the stack. Return sp. */
# ifdef SPARC
asm(" .seg \"text\"");
-# ifdef SVR4
+# if defined(SVR4) || defined(NETBSD)
asm(" .globl GC_save_regs_in_stack");
asm("GC_save_regs_in_stack:");
asm(" .type GC_save_regs_in_stack,#function");
ENABLE_SIGNALS();
}
+/* Internal use only; lock held. */
+static void GC_remove_root_at_pos(i)
+int i;
+{
+ GC_root_size -= (GC_static_roots[i].r_end - GC_static_roots[i].r_start);
+ GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start;
+ GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end;
+ GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp;
+ n_root_sets--;
+}
+
+#if !defined(MSWIN32) && !defined(MSWINCE)
+static void GC_rebuild_root_index()
+{
+ register int i;
+
+ for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0;
+ for (i = 0; i < n_root_sets; i++)
+ add_roots_to_index(GC_static_roots + i);
+}
+#endif
+
/* Internal use only; lock held. */
void GC_remove_tmp_roots()
{
for (i = 0; i < n_root_sets; ) {
if (GC_static_roots[i].r_tmp) {
- GC_root_size -=
- (GC_static_roots[i].r_end - GC_static_roots[i].r_start);
- GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start;
- GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end;
- GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp;
- n_root_sets--;
+ GC_remove_root_at_pos(i);
} else {
i++;
- }
}
-# if !defined(MSWIN32) && !defined(MSWINCE)
- {
- register int i;
-
- for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0;
- for (i = 0; i < n_root_sets; i++)
- add_roots_to_index(GC_static_roots + i);
}
-# endif
+ #if !defined(MSWIN32) && !defined(MSWINCE)
+ GC_rebuild_root_index();
+ #endif
+}
+
+#if !defined(MSWIN32) && !defined(MSWINCE)
+void GC_remove_roots(b, e)
+char * b; char * e;
+{
+ DCL_LOCK_STATE;
+ DISABLE_SIGNALS();
+ LOCK();
+ GC_remove_roots_inner(b, e);
+ UNLOCK();
+ ENABLE_SIGNALS();
+}
+
+/* Should only be called when the lock is held */
+void GC_remove_roots_inner(b,e)
+char * b; char * e;
+{
+ int i;
+ for (i = 0; i < n_root_sets; ) {
+ if (GC_static_roots[i].r_start >= (ptr_t)b && GC_static_roots[i].r_end <= (ptr_t)e) {
+ GC_remove_root_at_pos(i);
+ } else {
+ i++;
+ }
+ }
+ GC_rebuild_root_index();
}
+#endif /* !defined(MSWIN32) && !defined(MSWINCE) */
#if defined(MSWIN32) || defined(_WIN32_WCE_EMULATION)
/* Workaround for the OS mapping and unmapping behind our back: */
GC_init_parallel();
}
# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
+
+# if defined(DYNAMIC_LOADING) && defined(DARWIN)
+ {
+ /* This must be called WITHOUT the allocation lock held
+ and before any threads are created */
+ extern void GC_init_dyld();
+ GC_init_dyld();
+ }
+# endif
}
#if defined(MSWIN32) || defined(MSWINCE)
# define jmp_buf sigjmp_buf
#endif
+#ifdef DARWIN
+/* for get_etext and friends */
+#include <mach-o/getsect.h>
+#endif
+
#ifdef DJGPP
/* Apparently necessary for djgpp 2.01. May cause problems with */
/* other versions. */
return NULL;
}
- memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4); // do the protections first
+ memcpy(prot_buf, buf_ptr+OFFSET_MAP_PROT, 4);
+ /* do the protections first. */
prot_buf[4] = '\0';
- if (prot_buf[1] == 'w') { // we can skip all of this if it's not writable
+ if (prot_buf[1] == 'w') {/* we can skip all of this if it's not writable. */
tok = buf_ptr;
buf_ptr[OFFSET_MAP_START+ADDR_WIDTH] = '\0';
&& 0 != __libc_ia64_register_backing_store_base) {
/* Glibc 2.2.4 has a bug such that for dynamically linked */
/* executables __libc_ia64_register_backing_store_base is */
- /* defined but ininitialized during constructor calls. */
+ /* defined but uninitialized during constructor calls. */
/* Hence we check for both nonzero address and value. */
return __libc_ia64_register_backing_store_base;
} else {
ptr_t GC_get_stack_base()
{
+# if defined(HEURISTIC1) || defined(HEURISTIC2) || \
+ defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM)
word dummy;
ptr_t result;
+# endif
# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
* make sure that other system calls are similarly protected
* or write only to the stack.
*/
-
GC_bool GC_dirty_maintained = FALSE;
# ifdef DEFAULT_VDB
/* Initialize virtual dirty bit implementation. */
void GC_dirty_init()
{
+# ifdef PRINTSTATS
+ GC_printf0("Initializing DEFAULT_VDB...\n");
+# endif
GC_dirty_maintained = TRUE;
}
* objects only if they are the same.
*/
-# if !defined(MSWIN32) && !defined(MSWINCE)
+# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN)
# include <sys/mman.h>
# include <signal.h>
# else
+# ifdef DARWIN
+ /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to
+ decrease the likelihood of some of the problems described below. */
+ #include <mach/vm_map.h>
+ extern mach_port_t GC_task_self;
+ #define PROTECT(addr,len) \
+ if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
+ FALSE,VM_PROT_READ) != KERN_SUCCESS) { \
+ ABORT("vm_portect failed"); \
+ }
+ #define UNPROTECT(addr,len) \
+ if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
+ FALSE,VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) { \
+ ABORT("vm_portect failed"); \
+ }
+# else
+
# ifndef MSWINCE
# include <signal.h>
# endif
&protect_junk)) { \
ABORT("un-VirtualProtect failed"); \
}
-
-# endif
+# endif /* !DARWIN */
+# endif /* MSWIN32 || MSWINCE || DARWIN */
#if defined(SUNOS4) || defined(FREEBSD)
typedef void (* SIG_PF)();
-#endif
+#endif /* SUNOS4 || FREEBSD */
+
#if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \
- || defined(MACOSX) || defined(HURD)
+ || defined(HURD)
# ifdef __STDC__
typedef void (* SIG_PF)(int);
# else
typedef void (* SIG_PF)();
# endif
-#endif
+#endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */
+
#if defined(MSWIN32)
typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF;
# undef SIG_DFL
#if defined(IRIX5) || defined(OSF1) || defined(HURD)
typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
-#endif
+#endif /* IRIX5 || OSF1 || HURD */
+
#if defined(SUNOS5SIGS)
# ifdef HPUX
# define SIGINFO __siginfo
# else
typedef void (* REAL_SIG_PF)();
# endif
-#endif
+#endif /* SUNOS5SIGS */
+
#if defined(LINUX)
# if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2
typedef struct sigcontext s_c;
return (char *)faultaddr;
}
# endif /* !ALPHA */
-# endif
-
-# if defined(MACOSX) /* Should also test for PowerPC? */
- typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *);
-
-/* Decodes the machine instruction which was responsible for the sending of the
- SIGBUS signal. Sadly this is the only way to find the faulting address because
- the signal handler doesn't get it directly from the kernel (although it is
- available on the Mach level, but droppped by the BSD personality before it
- calls our signal handler...)
- This code should be able to deal correctly with all PPCs starting from the
- 601 up to and including the G4s (including Velocity Engine). */
-#define EXTRACT_OP1(iw) (((iw) & 0xFC000000) >> 26)
-#define EXTRACT_OP2(iw) (((iw) & 0x000007FE) >> 1)
-#define EXTRACT_REGA(iw) (((iw) & 0x001F0000) >> 16)
-#define EXTRACT_REGB(iw) (((iw) & 0x03E00000) >> 21)
-#define EXTRACT_REGC(iw) (((iw) & 0x0000F800) >> 11)
-#define EXTRACT_DISP(iw) ((short *) &(iw))[1]
-
-static char *get_fault_addr(struct sigcontext *scp)
-{
- unsigned int instr = *((unsigned int *) scp->sc_ir);
- unsigned int * regs = &((unsigned int *) scp->sc_regs)[2];
- int disp = 0, tmp;
- unsigned int baseA = 0, baseB = 0;
- unsigned int addr, alignmask = 0xFFFFFFFF;
-
-#ifdef GC_DEBUG_DECODER
- GC_err_printf1("Instruction: 0x%lx\n", instr);
- GC_err_printf1("Opcode 1: d\n", (int)EXTRACT_OP1(instr));
-#endif
- switch(EXTRACT_OP1(instr)) {
- case 38: /* stb */
- case 39: /* stbu */
- case 54: /* stfd */
- case 55: /* stfdu */
- case 52: /* stfs */
- case 53: /* stfsu */
- case 44: /* sth */
- case 45: /* sthu */
- case 47: /* stmw */
- case 36: /* stw */
- case 37: /* stwu */
- tmp = EXTRACT_REGA(instr);
- if(tmp > 0)
- baseA = regs[tmp];
- disp = EXTRACT_DISP(instr);
- break;
- case 31:
-#ifdef GC_DEBUG_DECODER
- GC_err_printf1("Opcode 2: %d\n", (int)EXTRACT_OP2(instr));
-#endif
- switch(EXTRACT_OP2(instr)) {
- case 86: /* dcbf */
- case 54: /* dcbst */
- case 1014: /* dcbz */
- case 247: /* stbux */
- case 215: /* stbx */
- case 759: /* stfdux */
- case 727: /* stfdx */
- case 983: /* stfiwx */
- case 695: /* stfsux */
- case 663: /* stfsx */
- case 918: /* sthbrx */
- case 439: /* sthux */
- case 407: /* sthx */
- case 661: /* stswx */
- case 662: /* stwbrx */
- case 150: /* stwcx. */
- case 183: /* stwux */
- case 151: /* stwx */
- case 135: /* stvebx */
- case 167: /* stvehx */
- case 199: /* stvewx */
- case 231: /* stvx */
- case 487: /* stvxl */
- tmp = EXTRACT_REGA(instr);
- if(tmp > 0)
- baseA = regs[tmp];
- baseB = regs[EXTRACT_REGC(instr)];
- /* determine Altivec alignment mask */
- switch(EXTRACT_OP2(instr)) {
- case 167: /* stvehx */
- alignmask = 0xFFFFFFFE;
- break;
- case 199: /* stvewx */
- alignmask = 0xFFFFFFFC;
- break;
- case 231: /* stvx */
- alignmask = 0xFFFFFFF0;
- break;
- case 487: /* stvxl */
- alignmask = 0xFFFFFFF0;
- break;
- }
- break;
- case 725: /* stswi */
- tmp = EXTRACT_REGA(instr);
- if(tmp > 0)
- baseA = regs[tmp];
- break;
- default: /* ignore instruction */
-#ifdef GC_DEBUG_DECODER
- GC_err_printf("Ignored by inner handler\n");
-#endif
- return NULL;
- break;
- }
- break;
- default: /* ignore instruction */
-#ifdef GC_DEBUG_DECODER
- GC_err_printf("Ignored by main handler\n");
-#endif
- return NULL;
- break;
- }
-
- addr = (baseA + baseB) + disp;
- addr &= alignmask;
-#ifdef GC_DEBUG_DECODER
- GC_err_printf1("BaseA: %d\n", baseA);
- GC_err_printf1("BaseB: %d\n", baseB);
- GC_err_printf1("Disp: %d\n", disp);
- GC_err_printf1("Address: %d\n", addr);
-#endif
- return (char *)addr;
-}
-#endif /* MACOSX */
+# endif /* LINUX */
+#ifndef DARWIN
SIG_PF GC_old_bus_handler;
SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */
+#endif /* !DARWIN */
-#ifdef THREADS
+#if defined(THREADS)
/* We need to lock around the bitmap update in the write fault handler */
/* in order to avoid the risk of losing a bit. We do this with a */
/* test-and-set spin lock if we know how to do that. Otherwise we */
#endif /* !THREADS */
/*ARGSUSED*/
+#if !defined(DARWIN)
# if defined (SUNOS4) || defined(FREEBSD)
void GC_write_fault_handler(sig, code, scp, addr)
int sig, code;
# define SIG_OK (sig == SIGBUS)
# define CODE_OK (code == BUS_PAGE_FAULT)
# endif
-# endif
+# endif /* SUNOS4 || FREEBSD */
+
# if defined(IRIX5) || defined(OSF1) || defined(HURD)
# include <errno.h>
void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
# define SIG_OK (sig == SIGBUS || sig == SIGSEGV)
# define CODE_OK TRUE
# endif
-# endif
+# endif /* IRIX5 || OSF1 || HURD */
+
# if defined(LINUX)
# if defined(ALPHA) || defined(M68K)
void GC_write_fault_handler(int sig, int code, s_c * sc)
/* Empirically c.trapno == 14, on IA32, but is that useful? */
/* Should probably consider alignment issues on other */
/* architectures. */
-# endif
+# endif /* LINUX */
+
# if defined(SUNOS5SIGS)
# ifdef __STDC__
void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context)
# define SIG_OK (sig == SIGSEGV)
# define CODE_OK (scp -> si_code == SEGV_ACCERR)
# endif
-# endif
-
-# if defined(MACOSX)
- void GC_write_fault_handler(int sig, int code, struct sigcontext *scp)
-# define SIG_OK (sig == SIGBUS)
-# define CODE_OK (code == 0 /* experimentally determined */)
-# endif
+# endif /* SUNOS5SIGS */
# if defined(MSWIN32) || defined(MSWINCE)
LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info)
STATUS_ACCESS_VIOLATION)
# define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1)
/* Write fault */
-# endif
+# endif /* MSWIN32 || MSWINCE */
{
register unsigned i;
# if defined(HURD)
# endif
# endif
# endif
-# if defined(MACOSX)
- char * addr = get_fault_addr(scp);
-# endif
# if defined(MSWIN32) || defined(MSWINCE)
char * addr = (char *) (exc_info -> ExceptionRecord
-> ExceptionInformation[1]);
(*(REAL_SIG_PF)old_handler) (sig, code, scp);
return;
# endif
-# ifdef MACOSX
- (*(REAL_SIG_PF)old_handler) (sig, code, scp);
-# endif
# ifdef MSWIN32
return((*old_handler)(exc_info));
# endif
ABORT("Unexpected bus error or segmentation fault");
#endif
}
+#endif /* !DARWIN */
/*
* We hold the allocation lock. We expect block h to be written
UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc);
}
+#if !defined(DARWIN)
void GC_dirty_init()
{
# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \
(void)sigaddset(&act.sa_mask, SIG_SUSPEND);
# endif /* SIG_SUSPEND */
# endif
-# if defined(MACOSX)
- struct sigaction act, oldact;
-
- act.sa_flags = SA_RESTART;
- act.sa_handler = GC_write_fault_handler;
- sigemptyset(&act.sa_mask);
-# endif
# ifdef PRINTSTATS
GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n");
# endif
# endif
}
# endif
-# if defined(MACOSX) || defined(HPUX) || defined(LINUX) || defined(HURD)
+# if defined(HPUX) || defined(LINUX) || defined(HURD)
sigaction(SIGBUS, &act, &oldact);
GC_old_bus_handler = oldact.sa_handler;
if (GC_old_bus_handler == SIG_IGN) {
GC_err_printf0("Replaced other SIGBUS handler\n");
# endif
}
-# endif /* MACOS || HPUX || LINUX */
+# endif /* HPUX || LINUX || HURD */
# if defined(MSWIN32)
GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
if (GC_old_segv_handler != NULL) {
}
# endif
}
+#endif /* !DARWIN */
int GC_incremental_protection_needs()
{
# endif /* PCR_VDB */
+#if defined(MPROTECT_VDB) && defined(DARWIN)
+/* The following sources were used as a *reference* for this exception handling
+ code:
+ 1. Apple's mach/xnu documentation
+ 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
+ omnigroup's macosx-dev list.
+ www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html
+ 3. macosx-nat.c from Apple's GDB source code.
+*/
+
+/* There seem to be numerous problems with darwin's mach exception handling.
+ I'm pretty sure they are not problems in my code. Search for
+ BROKEN_EXCEPTION_HANDLING for more information. */
+#define BROKEN_EXCEPTION_HANDLING
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/thread_status.h>
+#include <mach/exception.h>
+#include <mach/task.h>
+#include <pthread.h>
+
+/* These are not defined in any header, although they are documented */
+extern boolean_t exc_server(mach_msg_header_t *,mach_msg_header_t *);
+extern kern_return_t exception_raise(
+ mach_port_t,mach_port_t,mach_port_t,
+ exception_type_t,exception_data_t,mach_msg_type_number_t);
+extern kern_return_t exception_raise_state(
+ mach_port_t,mach_port_t,mach_port_t,
+ exception_type_t,exception_data_t,mach_msg_type_number_t,
+ thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
+ thread_state_t,mach_msg_type_number_t*);
+extern kern_return_t exception_raise_state_identity(
+ mach_port_t,mach_port_t,mach_port_t,
+ exception_type_t,exception_data_t,mach_msg_type_number_t,
+ thread_state_flavor_t*,thread_state_t,mach_msg_type_number_t,
+ thread_state_t,mach_msg_type_number_t*);
+
+
+#define MAX_EXCEPTION_PORTS 16
+
+static mach_port_t GC_task_self;
+
+static struct {
+ mach_msg_type_number_t count;
+ exception_mask_t masks[MAX_EXCEPTION_PORTS];
+ exception_handler_t ports[MAX_EXCEPTION_PORTS];
+ exception_behavior_t behaviors[MAX_EXCEPTION_PORTS];
+ thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS];
+} GC_old_exc_ports;
+
+static struct {
+ mach_port_t exception;
+#if defined(THREADS)
+ mach_port_t reply;
+#endif
+} GC_ports;
+
+typedef struct {
+ mach_msg_header_t head;
+} GC_msg_t;
+
+typedef enum {
+ GC_MP_NORMAL, GC_MP_DISCARDING, GC_MP_STOPPED
+} GC_mprotect_state_t;
+
+/* FIXME: 1 and 2 seem to be safe to use in the msgh_id field,
+ but it isn't documented. Use the source and see if they
+ should be ok. */
+#define ID_STOP 1
+#define ID_RESUME 2
+
+/* These values are only used on the reply port */
+#define ID_ACK 3
+
+#if defined(THREADS)
+
+GC_mprotect_state_t GC_mprotect_state;
+
+/* The following should ONLY be called when the world is stopped */
+static void GC_mprotect_thread_notify(mach_msg_id_t id) {
+ struct {
+ GC_msg_t msg;
+ mach_msg_trailer_t trailer;
+ } buf;
+ mach_msg_return_t r;
+ /* remote, local */
+ buf.msg.head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
+ buf.msg.head.msgh_size = sizeof(buf.msg);
+ buf.msg.head.msgh_remote_port = GC_ports.exception;
+ buf.msg.head.msgh_local_port = MACH_PORT_NULL;
+ buf.msg.head.msgh_id = id;
+
+ r = mach_msg(
+ &buf.msg.head,
+ MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_LARGE,
+ sizeof(buf.msg),
+ sizeof(buf),
+ GC_ports.reply,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if(r != MACH_MSG_SUCCESS)
+ ABORT("mach_msg failed in GC_mprotect_thread_notify");
+ if(buf.msg.head.msgh_id != ID_ACK)
+ ABORT("invalid ack in GC_mprotect_thread_notify");
+}
+
+/* Should only be called by the mprotect thread */
+static void GC_mprotect_thread_reply() {
+ GC_msg_t msg;
+ mach_msg_return_t r;
+ /* remote, local */
+ msg.head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0);
+ msg.head.msgh_size = sizeof(msg);
+ msg.head.msgh_remote_port = GC_ports.reply;
+ msg.head.msgh_local_port = MACH_PORT_NULL;
+ msg.head.msgh_id = ID_ACK;
+
+ r = mach_msg(
+ &msg.head,
+ MACH_SEND_MSG,
+ sizeof(msg),
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if(r != MACH_MSG_SUCCESS)
+ ABORT("mach_msg failed in GC_mprotect_thread_reply");
+}
+
+void GC_mprotect_stop() {
+ GC_mprotect_thread_notify(ID_STOP);
+}
+void GC_mprotect_resume() {
+ GC_mprotect_thread_notify(ID_RESUME);
+}
+
+#else /* !THREADS */
+/* The compiler should optimize away any GC_mprotect_state computations */
+#define GC_mprotect_state GC_MP_NORMAL
+#endif
+
+static void *GC_mprotect_thread(void *arg) {
+ mach_msg_return_t r;
+ /* These two structures contain some private kernel data. We don't need to
+ access any of it so we don't bother defining a proper struct. The
+ correct definitions are in the xnu source code. */
+ struct {
+ mach_msg_header_t head;
+ char data[256];
+ } reply;
+ struct {
+ mach_msg_header_t head;
+ mach_msg_body_t msgh_body;
+ char data[1024];
+ } msg;
+
+ mach_msg_id_t id;
+
+ for(;;) {
+ r = mach_msg(
+ &msg.head,
+ MACH_RCV_MSG|MACH_RCV_LARGE|
+ (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0),
+ 0,
+ sizeof(msg),
+ GC_ports.exception,
+ GC_mprotect_state == GC_MP_DISCARDING ? 0 : MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1;
+
+#if defined(THREADS)
+ if(GC_mprotect_state == GC_MP_DISCARDING) {
+ if(r == MACH_RCV_TIMED_OUT) {
+ GC_mprotect_state = GC_MP_STOPPED;
+ GC_mprotect_thread_reply();
+ continue;
+ }
+ if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME))
+ ABORT("out of order mprotect thread request");
+ }
+#endif
+
+ if(r != MACH_MSG_SUCCESS) {
+ GC_err_printf2("mach_msg failed with %d %s\n",
+ (int)r,mach_error_string(r));
+ ABORT("mach_msg failed");
+ }
+
+ switch(id) {
+#if defined(THREADS)
+ case ID_STOP:
+ if(GC_mprotect_state != GC_MP_NORMAL)
+ ABORT("Called mprotect_stop when state wasn't normal");
+ GC_mprotect_state = GC_MP_DISCARDING;
+ break;
+ case ID_RESUME:
+ if(GC_mprotect_state != GC_MP_STOPPED)
+ ABORT("Called mprotect_resume when state wasn't stopped");
+ GC_mprotect_state = GC_MP_NORMAL;
+ GC_mprotect_thread_reply();
+ break;
+#endif /* THREADS */
+ default:
+ /* Handle the message (calls catch_exception_raise) */
+ if(!exc_server(&msg.head,&reply.head))
+ ABORT("exc_server failed");
+ /* Send the reply */
+ r = mach_msg(
+ &reply.head,
+ MACH_SEND_MSG,
+ reply.head.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if(r != MACH_MSG_SUCCESS) {
+ /* This will fail if the thread dies, but the thread shouldn't
+ die... */
+ #ifdef BROKEN_EXCEPTION_HANDLING
+ GC_err_printf2(
+ "mach_msg failed with %d %s while sending exc reply\n",
+ (int)r,mach_error_string(r));
+ #else
+ ABORT("mach_msg failed while sending exception reply");
+ #endif
+ }
+ } /* switch */
+ } /* for(;;) */
+ /* NOT REACHED */
+ return NULL;
+}
+
+/* All this SIGBUS code shouldn't be necessary. All protection faults should
+ be going throught the mach exception handler. However, it seems a SIGBUS is
+ occasionally sent for some unknown reason. Even more odd, it seems to be
+ meaningless and safe to ignore. */
+#ifdef BROKEN_EXCEPTION_HANDLING
+
+typedef void (* SIG_PF)();
+static SIG_PF GC_old_bus_handler;
+
+/* Updates to this aren't atomic, but the SIGBUSs seem pretty rare.
+ Even if this doesn't get updated property, it isn't really a problem */
+static int GC_sigbus_count;
+
+static void GC_darwin_sigbus(int num,siginfo_t *sip,void *context) {
+ if(num != SIGBUS) ABORT("Got a non-sigbus signal in the sigbus handler");
+
+ /* Ugh... some seem safe to ignore, but too many in a row probably means
+ trouble. GC_sigbus_count is reset for each mach exception that is
+ handled */
+ if(GC_sigbus_count >= 8) {
+ ABORT("Got more than 8 SIGBUSs in a row!");
+ } else {
+ GC_sigbus_count++;
+ GC_err_printf0("GC: WARNING: Ignoring SIGBUS.\n");
+ }
+}
+#endif /* BROKEN_EXCEPTION_HANDLING */
+
+void GC_dirty_init() {
+ kern_return_t r;
+ mach_port_t me;
+ pthread_t thread;
+ pthread_attr_t attr;
+ exception_mask_t mask;
+
+# ifdef PRINTSTATS
+ GC_printf0("Inititalizing mach/darwin mprotect virtual dirty bit "
+ "implementation\n");
+# endif
+# ifdef BROKEN_EXCEPTION_HANDLING
+ GC_err_printf0("GC: WARNING: Enabling workarounds for various darwin "
+ "exception handling bugs.\n");
+# endif
+ GC_dirty_maintained = TRUE;
+ if (GC_page_size % HBLKSIZE != 0) {
+ GC_err_printf0("Page size not multiple of HBLKSIZE\n");
+ ABORT("Page size not multiple of HBLKSIZE");
+ }
+
+ GC_task_self = me = mach_task_self();
+
+ r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.exception);
+ if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (exception port)");
+
+ r = mach_port_insert_right(me,GC_ports.exception,GC_ports.exception,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if(r != KERN_SUCCESS)
+ ABORT("mach_port_insert_right failed (exception port)");
+
+ #if defined(THREADS)
+ r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.reply);
+ if(r != KERN_SUCCESS) ABORT("mach_port_allocate failed (reply port)");
+ #endif
+
+ /* The exceptions we want to catch */
+ mask = EXC_MASK_BAD_ACCESS;
+
+ r = task_get_exception_ports(
+ me,
+ mask,
+ GC_old_exc_ports.masks,
+ &GC_old_exc_ports.count,
+ GC_old_exc_ports.ports,
+ GC_old_exc_ports.behaviors,
+ GC_old_exc_ports.flavors
+ );
+ if(r != KERN_SUCCESS) ABORT("task_get_exception_ports failed");
+
+ r = task_set_exception_ports(
+ me,
+ mask,
+ GC_ports.exception,
+ EXCEPTION_DEFAULT,
+ MACHINE_THREAD_STATE
+ );
+ if(r != KERN_SUCCESS) ABORT("task_set_exception_ports failed");
+
+ if(pthread_attr_init(&attr) != 0) ABORT("pthread_attr_init failed");
+ if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0)
+ ABORT("pthread_attr_setdetachedstate failed");
+
+# undef pthread_create
+ /* This will call the real pthread function, not our wrapper */
+ if(pthread_create(&thread,&attr,GC_mprotect_thread,NULL) != 0)
+ ABORT("pthread_create failed");
+ pthread_attr_destroy(&attr);
+
+ /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */
+ #ifdef BROKEN_EXCEPTION_HANDLING
+ {
+ struct sigaction sa, oldsa;
+ sa.sa_handler = (SIG_PF)GC_darwin_sigbus;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART|SA_SIGINFO;
+ if(sigaction(SIGBUS,&sa,&oldsa) < 0) ABORT("sigaction");
+ GC_old_bus_handler = (SIG_PF)oldsa.sa_handler;
+ if (GC_old_bus_handler != SIG_DFL) {
+# ifdef PRINTSTATS
+ GC_err_printf0("Replaced other SIGBUS handler\n");
+# endif
+ }
+ }
+ #endif /* BROKEN_EXCEPTION_HANDLING */
+}
+
+/* The source code for Apple's GDB was used as a reference for the exception
+ forwarding code. This code is similar to be GDB code only because there is
+ only one way to do it. */
+static kern_return_t GC_forward_exception(
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ exception_data_t data,
+ mach_msg_type_number_t data_count
+) {
+ int i;
+ kern_return_t r;
+ mach_port_t port;
+ exception_behavior_t behavior;
+ thread_state_flavor_t flavor;
+
+ thread_state_data_t thread_state;
+ mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
+
+ for(i=0;i<GC_old_exc_ports.count;i++)
+ if(GC_old_exc_ports.masks[i] & (1 << exception))
+ break;
+ if(i==GC_old_exc_ports.count) ABORT("No handler for exception!");
+
+ port = GC_old_exc_ports.ports[i];
+ behavior = GC_old_exc_ports.behaviors[i];
+ flavor = GC_old_exc_ports.flavors[i];
+
+ if(behavior != EXCEPTION_DEFAULT) {
+ r = thread_get_state(thread,flavor,thread_state,&thread_state_count);
+ if(r != KERN_SUCCESS)
+ ABORT("thread_get_state failed in forward_exception");
+ }
+
+ switch(behavior) {
+ case EXCEPTION_DEFAULT:
+ r = exception_raise(port,thread,task,exception,data,data_count);
+ break;
+ case EXCEPTION_STATE:
+ r = exception_raise_state(port,thread,task,exception,data,
+ data_count,&flavor,thread_state,thread_state_count,
+ thread_state,&thread_state_count);
+ break;
+ case EXCEPTION_STATE_IDENTITY:
+ r = exception_raise_state_identity(port,thread,task,exception,data,
+ data_count,&flavor,thread_state,thread_state_count,
+ thread_state,&thread_state_count);
+ break;
+ default:
+ r = KERN_FAILURE; /* make gcc happy */
+ ABORT("forward_exception: unknown behavior");
+ break;
+ }
+
+ if(behavior != EXCEPTION_DEFAULT) {
+ r = thread_set_state(thread,flavor,thread_state,thread_state_count);
+ if(r != KERN_SUCCESS)
+ ABORT("thread_set_state failed in forward_exception");
+ }
+
+ return r;
+}
+
+#define FWD() GC_forward_exception(thread,task,exception,code,code_count)
+
+/* This violates the namespace rules but there isn't anything that can be done
+ about it. The exception handling stuff is hard coded to call this */
+kern_return_t
+catch_exception_raise(
+ mach_port_t exception_port,mach_port_t thread,mach_port_t task,
+ exception_type_t exception,exception_data_t code,
+ mach_msg_type_number_t code_count
+) {
+ kern_return_t r;
+ char *addr;
+ struct hblk *h;
+ int i;
+#ifdef POWERPC
+ thread_state_flavor_t flavor = PPC_EXCEPTION_STATE;
+ mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT;
+ ppc_exception_state_t exc_state;
+#else
+# error FIXME for non-ppc darwin
+#endif
+
+
+ if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) {
+ #ifdef DEBUG_EXCEPTION_HANDLING
+ /* We aren't interested, pass it on to the old handler */
+ GC_printf3("Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
+ exception,
+ code_count > 0 ? code[0] : -1,
+ code_count > 1 ? code[1] : -1);
+ #endif
+ return FWD();
+ }
+
+ r = thread_get_state(thread,flavor,
+ (natural_t*)&exc_state,&exc_state_count);
+ if(r != KERN_SUCCESS) {
+ /* The thread is supposed to be suspended while the exception handler
+ is called. This shouldn't fail. */
+ #ifdef BROKEN_EXCEPTION_HANDLING
+ GC_err_printf0("thread_get_state failed in "
+ "catch_exception_raise\n");
+ return KERN_SUCCESS;
+ #else
+ ABORT("thread_get_state failed in catch_exception_raise");
+ #endif
+ }
+
+ /* This is the address that caused the fault */
+ addr = (char*) exc_state.dar;
+
+ if((HDR(addr)) == 0) {
+ /* Ugh... just like the SIGBUS problem above, it seems we get a bogus
+ KERN_PROTECTION_FAILURE every once and a while. We wait till we get
+ a bunch in a row before doing anything about it. If a "real" fault
+ ever occurres it'll just keep faulting over and over and we'll hit
+ the limit pretty quickly. */
+ #ifdef BROKEN_EXCEPTION_HANDLING
+ static char *last_fault;
+ static int last_fault_count;
+
+ if(addr != last_fault) {
+ last_fault = addr;
+ last_fault_count = 0;
+ }
+ if(++last_fault_count < 32) {
+ if(last_fault_count == 1)
+ GC_err_printf1(
+ "GC: WARNING: Ignoring KERN_PROTECTION_FAILURE at %p\n",
+ addr);
+ return KERN_SUCCESS;
+ }
+
+ GC_err_printf1("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr);
+ /* Can't pass it along to the signal handler because that is
+ ignoring SIGBUS signals. We also shouldn't call ABORT here as
+ signals don't always work too well from the exception handler. */
+ GC_err_printf0("Aborting\n");
+ exit(EXIT_FAILURE);
+ #else /* BROKEN_EXCEPTION_HANDLING */
+ /* Pass it along to the next exception handler
+ (which should call SIGBUS/SIGSEGV) */
+ return FWD();
+ #endif /* !BROKEN_EXCEPTION_HANDLING */
+ }
+
+ #ifdef BROKEN_EXCEPTION_HANDLING
+ /* Reset the number of consecutive SIGBUSs */
+ GC_sigbus_count = 0;
+ #endif
+
+ if(GC_mprotect_state == GC_MP_NORMAL) { /* common case */
+ h = (struct hblk*)((word)addr & ~(GC_page_size-1));
+ UNPROTECT(h, GC_page_size);
+ for (i = 0; i < divHBLKSZ(GC_page_size); i++) {
+ register int index = PHT_HASH(h+i);
+ async_set_pht_entry_from_index(GC_dirty_pages, index);
+ }
+ } else if(GC_mprotect_state == GC_MP_DISCARDING) {
+ /* Lie to the thread for now. No sense UNPROTECT()ing the memory
+ when we're just going to PROTECT() it again later. The thread
+ will just fault again once it resumes */
+ } else {
+ /* Shouldn't happen, i don't think */
+ GC_printf0("KERN_PROTECTION_FAILURE while world is stopped\n");
+ return FWD();
+ }
+ return KERN_SUCCESS;
+}
+#undef FWD
+
+/* These should never be called, but just in case... */
+kern_return_t catch_exception_raise_state(mach_port_name_t exception_port,
+ int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
+ int flavor, thread_state_t old_state, int old_stateCnt,
+ thread_state_t new_state, int new_stateCnt)
+{
+ ABORT("catch_exception_raise_state");
+ return(KERN_INVALID_ARGUMENT);
+}
+kern_return_t catch_exception_raise_state_identity(
+ mach_port_name_t exception_port, mach_port_t thread, mach_port_t task,
+ int exception, exception_data_t code, mach_msg_type_number_t codeCnt,
+ int flavor, thread_state_t old_state, int old_stateCnt,
+ thread_state_t new_state, int new_stateCnt)
+{
+ ABORT("catch_exception_raise_state_identity");
+ return(KERN_INVALID_ARGUMENT);
+}
+
+
+#endif /* DARWIN && MPROTECT_VDB */
+
# ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS
int GC_incremental_protection_needs()
{
asm("movl %%ebp,%0" : "=r"(frame));
fp = frame;
# else
- word GC_save_regs_in_stack();
-
frame = (struct frame *) GC_save_regs_in_stack ();
fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
#endif
--- /dev/null
+
+; GC_push_regs function. Under some optimization levels GCC will clobber
+; some of the non-volatile registers before we get a chance to save them
+; therefore, this can't be inline asm.
+
+.text
+ .align 2
+ .globl _GC_push_regs
+_GC_push_regs:
+
+ ; Prolog
+ mflr r0
+ stw r0,8(r1)
+ stwu r1,-80(r1)
+
+ ; Push r13-r31
+ mr r3,r13
+ bl L_GC_push_one$stub
+ mr r3,r14
+ bl L_GC_push_one$stub
+ mr r3,r15
+ bl L_GC_push_one$stub
+ mr r3,r16
+ bl L_GC_push_one$stub
+ mr r3,r17
+ bl L_GC_push_one$stub
+ mr r3,r18
+ bl L_GC_push_one$stub
+ mr r3,r19
+ bl L_GC_push_one$stub
+ mr r3,r20
+ bl L_GC_push_one$stub
+ mr r3,r21
+ bl L_GC_push_one$stub
+ mr r3,r22
+ bl L_GC_push_one$stub
+ mr r3,r23
+ bl L_GC_push_one$stub
+ mr r3,r24
+ bl L_GC_push_one$stub
+ mr r3,r25
+ bl L_GC_push_one$stub
+ mr r3,r26
+ bl L_GC_push_one$stub
+ mr r3,r27
+ bl L_GC_push_one$stub
+ mr r3,r28
+ bl L_GC_push_one$stub
+ mr r3,r29
+ bl L_GC_push_one$stub
+ mr r3,r30
+ bl L_GC_push_one$stub
+ mr r3,r31
+ bl L_GC_push_one$stub
+
+ ;
+ lwz r0,88(r1)
+ addi r1,r1,80
+ mtlr r0
+
+ ; Return
+ blr
+
+; PIC stuff, generated by GCC
+
+.data
+.picsymbol_stub
+L_GC_push_one$stub:
+ .indirect_symbol _GC_push_one
+ mflr r0
+ bcl 20,31,L0$_GC_push_one
+L0$_GC_push_one:
+ mflr r11
+ addis r11,r11,ha16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
+ mtlr r0
+ lwz r12,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)(r11)
+ mtctr r12
+ addi r11,r11,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
+ bctr
+.data
+.lazy_symbol_pointer
+L_GC_push_one$lazy_ptr:
+ .indirect_symbol _GC_push_one
+ .long dyld_stub_binding_helper
+++ /dev/null
-
-.text
-
- .set linkageArea,24
- .set params,4
- .set alignment,4
-
- .set spaceToSave,linkageArea+params+alignment
- .set spaceToSave8,spaceToSave+8
-
-; Mark from machine registers that are saved by C compiler
- .globl _GC_push_regs
-_GC_push_regs:
- ; PROLOG
- mflr r0 ; get return address
- stw r0,8(r1) ; save return address
- stwu r1,-spaceToSave(r1) ; skip over caller save area
- ;
- mr r3,r2 ; mark from r2. Well Im not really sure
- ; that this is necessary or even the right
- ; thing to do - at least it doesnt harm...
- ; According to Apples docs it points to
- ; the direct data area, whatever that is...
- bl L_GC_push_one$stub
- mr r3,r13 ; mark from r13-r31
- bl L_GC_push_one$stub
- mr r3,r14
- bl L_GC_push_one$stub
- mr r3,r15
- bl L_GC_push_one$stub
- mr r3,r16
- bl L_GC_push_one$stub
- mr r3,r17
- bl L_GC_push_one$stub
- mr r3,r18
- bl L_GC_push_one$stub
- mr r3,r19
- bl L_GC_push_one$stub
- mr r3,r20
- bl L_GC_push_one$stub
- mr r3,r21
- bl L_GC_push_one$stub
- mr r3,r22
- bl L_GC_push_one$stub
- mr r3,r23
- bl L_GC_push_one$stub
- mr r3,r24
- bl L_GC_push_one$stub
- mr r3,r25
- bl L_GC_push_one$stub
- mr r3,r26
- bl L_GC_push_one$stub
- mr r3,r27
- bl L_GC_push_one$stub
- mr r3,r28
- bl L_GC_push_one$stub
- mr r3,r29
- bl L_GC_push_one$stub
- mr r3,r30
- bl L_GC_push_one$stub
- mr r3,r31
- bl L_GC_push_one$stub
- ; EPILOG
- lwz r0,spaceToSave8(r1) ; get return address back
- mtlr r0 ; reset link register
- addic r1,r1,spaceToSave ; restore stack pointer
- blr
-
-.data
-.picsymbol_stub
-L_GC_push_one$stub:
- .indirect_symbol _GC_push_one
- mflr r0
- bcl 20,31,L0$_GC_push_one
-L0$_GC_push_one:
- mflr r11
- addis r11,r11,ha16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
- mtlr r0
- lwz r12,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)(r11)
- mtctr r12
- addi r11,r11,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)
- bctr
-.data
-.lazy_symbol_pointer
-L_GC_push_one$lazy_ptr:
- .indirect_symbol _GC_push_one
- .long dyld_stub_binding_helper
-.non_lazy_symbol_pointer
-L_GC_push_one$non_lazy_ptr:
- .indirect_symbol _GC_push_one
- .long 0
-
-
-
-
--- /dev/null
+#include "private/pthread_support.h"
+
+#if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
+ && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
+ && !defined(GC_DARWIN_THREADS)
+
+#include <signal.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <unistd.h>
+
+#if DEBUG_THREADS
+
+#ifndef NSIG
+# if defined(MAXSIG)
+# define NSIG (MAXSIG+1)
+# elif defined(_NSIG)
+# define NSIG _NSIG
+# elif defined(__SIGRTMAX)
+# define NSIG (__SIGRTMAX+1)
+# else
+ --> please fix it
+# endif
+#endif
+
+void GC_print_sig_mask()
+{
+ sigset_t blocked;
+ int i;
+
+ if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
+ ABORT("pthread_sigmask");
+ GC_printf0("Blocked: ");
+ for (i = 1; i < NSIG; i++) {
+ if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
+ }
+ GC_printf0("\n");
+}
+
+#endif
+
+word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
+
+#ifdef GC_OSF1_THREADS
+ GC_bool GC_retry_signals = TRUE;
+#else
+ GC_bool GC_retry_signals = FALSE;
+#endif
+
+/*
+ * We use signals to stop threads during GC.
+ *
+ * Suspended threads wait in signal handler for SIG_THR_RESTART.
+ * That's more portable than semaphores or condition variables.
+ * (We do use sem_post from a signal handler, but that should be portable.)
+ *
+ * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h.
+ * Note that we can't just stop a thread; we need it to save its stack
+ * pointer(s) and acknowledge.
+ */
+
+#ifndef SIG_THR_RESTART
+# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)
+# ifdef _SIGRTMIN
+# define SIG_THR_RESTART _SIGRTMIN + 5
+# else
+# define SIG_THR_RESTART SIGRTMIN + 5
+# endif
+# else
+# define SIG_THR_RESTART SIGXCPU
+# endif
+#endif
+
+sem_t GC_suspend_ack_sem;
+
+void GC_suspend_handler(int sig)
+{
+ int dummy;
+ pthread_t my_thread = pthread_self();
+ GC_thread me;
+ sigset_t mask;
+# ifdef PARALLEL_MARK
+ word my_mark_no = GC_mark_no;
+ /* Marker can't proceed until we acknowledge. Thus this is */
+ /* guaranteed to be the mark_no correspending to our */
+ /* suspension, i.e. the marker can't have incremented it yet. */
+# endif
+ word my_stop_count = GC_stop_count;
+
+ if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
+
+#if DEBUG_THREADS
+ GC_printf1("Suspending 0x%lx\n", my_thread);
+#endif
+
+ me = GC_lookup_thread(my_thread);
+ /* The lookup here is safe, since I'm doing this on behalf */
+ /* of a thread which holds the allocation lock in order */
+ /* to stop the world. Thus concurrent modification of the */
+ /* data structure is impossible. */
+ if (me -> stop_info.last_stop_count == my_stop_count) {
+ /* Duplicate signal. OK if we are retrying. */
+ if (!GC_retry_signals) {
+ WARN("Duplicate suspend signal in thread %lx\n",
+ pthread_self());
+ }
+ return;
+ }
+# ifdef SPARC
+ me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
+# else
+ me -> stop_info.stack_ptr = (ptr_t)(&dummy);
+# endif
+# ifdef IA64
+ me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack();
+# endif
+
+ /* Tell the thread that wants to stop the world that this */
+ /* thread has been stopped. Note that sem_post() is */
+ /* the only async-signal-safe primitive in LinuxThreads. */
+ sem_post(&GC_suspend_ack_sem);
+ me -> stop_info.last_stop_count = my_stop_count;
+
+ /* Wait until that thread tells us to restart by sending */
+ /* this thread a SIG_THR_RESTART signal. */
+ /* SIG_THR_RESTART should be masked at this point. Thus there */
+ /* is no race. */
+ if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
+ if (sigdelset(&mask, SIG_THR_RESTART) != 0) ABORT("sigdelset() failed");
+# ifdef NO_SIGNALS
+ if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed");
+ if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed");
+ if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed");
+ if (sigdelset(&mask, SIGABRT) != 0) ABORT("sigdelset() failed");
+# endif
+ do {
+ me->stop_info.signal = 0;
+ sigsuspend(&mask); /* Wait for signal */
+ } while (me->stop_info.signal != SIG_THR_RESTART);
+ /* If the RESTART signal gets lost, we can still lose. That should be */
+ /* less likely than losing the SUSPEND signal, since we don't do much */
+ /* between the sem_post and sigsuspend. */
+ /* We'd need more handshaking to work around that, since we don't want */
+ /* to accidentally leave a RESTART signal pending, thus causing us to */
+ /* continue prematurely in a future round. */
+
+#if DEBUG_THREADS
+ GC_printf1("Continuing 0x%lx\n", my_thread);
+#endif
+}
+
+void GC_restart_handler(int sig)
+{
+ pthread_t my_thread = pthread_self();
+ GC_thread me;
+
+ if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
+
+ /* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */
+ /* The lookup here is safe, since I'm doing this on behalf */
+ /* of a thread which holds the allocation lock in order */
+ /* to stop the world. Thus concurrent modification of the */
+ /* data structure is impossible. */
+ me = GC_lookup_thread(my_thread);
+ me->stop_info.signal = SIG_THR_RESTART;
+
+ /*
+ ** Note: even if we didn't do anything useful here,
+ ** it would still be necessary to have a signal handler,
+ ** rather than ignoring the signals, otherwise
+ ** the signals will not be delivered at all, and
+ ** will thus not interrupt the sigsuspend() above.
+ */
+
+#if DEBUG_THREADS
+ GC_printf1("In GC_restart_handler for 0x%lx\n", pthread_self());
+#endif
+}
+
+# ifdef IA64
+# define IF_IA64(x) x
+# else
+# define IF_IA64(x)
+# endif
+/* We hold allocation lock. Should do exactly the right thing if the */
+/* world is stopped. Should not fail if it isn't. */
+void GC_push_all_stacks()
+{
+ int i;
+ GC_thread p;
+ ptr_t lo, hi;
+ /* On IA64, we also need to scan the register backing store. */
+ IF_IA64(ptr_t bs_lo; ptr_t bs_hi;)
+ pthread_t me = pthread_self();
+
+ if (!GC_thr_initialized) GC_thr_init();
+ #if DEBUG_THREADS
+ GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
+ #endif
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> flags & FINISHED) continue;
+ if (pthread_equal(p -> id, me)) {
+# ifdef SPARC
+ lo = (ptr_t)GC_save_regs_in_stack();
+# else
+ lo = GC_approx_sp();
+# endif
+ IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
+ } else {
+ lo = p -> stop_info.stack_ptr;
+ IF_IA64(bs_hi = p -> backing_store_ptr;)
+ }
+ if ((p -> flags & MAIN_THREAD) == 0) {
+ hi = p -> stack_end;
+ IF_IA64(bs_lo = p -> backing_store_end);
+ } else {
+ /* The original stack. */
+ hi = GC_stackbottom;
+ IF_IA64(bs_lo = BACKING_STORE_BASE;)
+ }
+ #if DEBUG_THREADS
+ GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
+ (unsigned long) p -> id,
+ (unsigned long) lo, (unsigned long) hi);
+ #endif
+ if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
+# ifdef STACK_GROWS_UP
+ /* We got them backwards! */
+ GC_push_all_stack(hi, lo);
+# else
+ GC_push_all_stack(lo, hi);
+# endif
+# ifdef IA64
+ if (pthread_equal(p -> id, me)) {
+ GC_push_all_eager(bs_lo, bs_hi);
+ } else {
+ GC_push_all_stack(bs_lo, bs_hi);
+ }
+# endif
+ }
+ }
+}
+
+/* There seems to be a very rare thread stopping problem. To help us */
+/* debug that, we save the ids of the stopping thread. */
+pthread_t GC_stopping_thread;
+int GC_stopping_pid;
+
+/* We hold the allocation lock. Suspend all threads that might */
+/* still be running. Return the number of suspend signals that */
+/* were sent. */
+int GC_suspend_all()
+{
+ int n_live_threads = 0;
+ int i;
+ GC_thread p;
+ int result;
+ pthread_t my_thread = pthread_self();
+
+ GC_stopping_thread = my_thread; /* debugging only. */
+ GC_stopping_pid = getpid(); /* debugging only. */
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> id != my_thread) {
+ if (p -> flags & FINISHED) continue;
+ if (p -> stop_info.last_stop_count == GC_stop_count) continue;
+ if (p -> thread_blocked) /* Will wait */ continue;
+ n_live_threads++;
+ #if DEBUG_THREADS
+ GC_printf1("Sending suspend signal to 0x%lx\n", p -> id);
+ #endif
+
+ result = pthread_kill(p -> id, SIG_SUSPEND);
+ switch(result) {
+ case ESRCH:
+ /* Not really there anymore. Possible? */
+ n_live_threads--;
+ break;
+ case 0:
+ break;
+ default:
+ ABORT("pthread_kill failed");
+ }
+ }
+ }
+ }
+ return n_live_threads;
+}
+
+/* Caller holds allocation lock. */
+void GC_stop_world()
+{
+ int i;
+ int n_live_threads;
+ int code;
+
+ #if DEBUG_THREADS
+ GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
+ #endif
+
+ /* Make sure all free list construction has stopped before we start. */
+ /* No new construction can start, since free list construction is */
+ /* required to acquire and release the GC lock before it starts, */
+ /* and we have the lock. */
+# ifdef PARALLEL_MARK
+ GC_acquire_mark_lock();
+ GC_ASSERT(GC_fl_builder_count == 0);
+ /* We should have previously waited for it to become zero. */
+# endif /* PARALLEL_MARK */
+ ++GC_stop_count;
+ n_live_threads = GC_suspend_all();
+
+ if (GC_retry_signals) {
+ unsigned long wait_usecs = 0; /* Total wait since retry. */
+# define WAIT_UNIT 3000
+# define RETRY_INTERVAL 100000
+ for (;;) {
+ int ack_count;
+
+ sem_getvalue(&GC_suspend_ack_sem, &ack_count);
+ if (ack_count == n_live_threads) break;
+ if (wait_usecs > RETRY_INTERVAL) {
+ int newly_sent = GC_suspend_all();
+
+# ifdef CONDPRINT
+ if (GC_print_stats) {
+ GC_printf1("Resent %ld signals after timeout\n",
+ newly_sent);
+ }
+# endif
+ sem_getvalue(&GC_suspend_ack_sem, &ack_count);
+ if (newly_sent < n_live_threads - ack_count) {
+ WARN("Lost some threads during GC_stop_world?!\n",0);
+ n_live_threads = ack_count + newly_sent;
+ }
+ wait_usecs = 0;
+ }
+ usleep(WAIT_UNIT);
+ wait_usecs += WAIT_UNIT;
+ }
+ }
+ for (i = 0; i < n_live_threads; i++) {
+ if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+ GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
+ ABORT("sem_wait for handler failed");
+ }
+ }
+# ifdef PARALLEL_MARK
+ GC_release_mark_lock();
+# endif
+ #if DEBUG_THREADS
+ GC_printf1("World stopped from 0x%lx\n", pthread_self());
+ #endif
+ GC_stopping_thread = 0; /* debugging only */
+}
+
+/* Caller holds allocation lock, and has held it continuously since */
+/* the world stopped. */
+void GC_start_world()
+{
+ pthread_t my_thread = pthread_self();
+ register int i;
+ register GC_thread p;
+ register int n_live_threads = 0;
+ register int result;
+
+# if DEBUG_THREADS
+ GC_printf0("World starting\n");
+# endif
+
+ for (i = 0; i < THREAD_TABLE_SZ; i++) {
+ for (p = GC_threads[i]; p != 0; p = p -> next) {
+ if (p -> id != my_thread) {
+ if (p -> flags & FINISHED) continue;
+ if (p -> thread_blocked) continue;
+ n_live_threads++;
+ #if DEBUG_THREADS
+ GC_printf1("Sending restart signal to 0x%lx\n", p -> id);
+ #endif
+
+ result = pthread_kill(p -> id, SIG_THR_RESTART);
+ switch(result) {
+ case ESRCH:
+ /* Not really there anymore. Possible? */
+ n_live_threads--;
+ break;
+ case 0:
+ break;
+ default:
+ ABORT("pthread_kill failed");
+ }
+ }
+ }
+ }
+ #if DEBUG_THREADS
+ GC_printf0("World started\n");
+ #endif
+}
+
+void GC_stop_init() {
+ struct sigaction act;
+
+ if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
+ ABORT("sem_init failed");
+
+ act.sa_flags = SA_RESTART;
+ if (sigfillset(&act.sa_mask) != 0) {
+ ABORT("sigfillset() failed");
+ }
+# ifdef NO_SIGNALS
+ if (sigdelset(&act.sa_mask, SIGINT) != 0
+ || sigdelset(&act.sa_mask, SIGQUIT != 0)
+ || sigdelset(&act.sa_mask, SIGABRT != 0)
+ || sigdelset(&act.sa_mask, SIGTERM != 0)) {
+ ABORT("sigdelset() failed");
+ }
+# endif
+
+ /* SIG_THR_RESTART is unmasked by the handler when necessary. */
+ act.sa_handler = GC_suspend_handler;
+ if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
+ ABORT("Cannot set SIG_SUSPEND handler");
+ }
+
+ act.sa_handler = GC_restart_handler;
+ if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
+ ABORT("Cannot set SIG_THR_RESTART handler");
+ }
+
+ /* Check for GC_RETRY_SIGNALS. */
+ if (0 != GETENV("GC_RETRY_SIGNALS")) {
+ GC_retry_signals = TRUE;
+ }
+ if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
+ GC_retry_signals = FALSE;
+ }
+# ifdef CONDPRINT
+ if (GC_print_stats && GC_retry_signals) {
+ GC_printf0("Will retry suspend signal if necessary.\n");
+ }
+# endif
+}
+
+#endif
* + # define GC_LOCK_TAKEN GC_allocate_lock
*/
-/* #define DEBUG_THREADS 1 */
+/*#define DEBUG_THREADS 1*/
+/*#define GC_ASSERTIONS*/
-/* ANSI C requires that a compilation unit contains something */
-
-# include "gc.h"
+# include "private/pthread_support.h"
# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
- && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS)
-
-# include "private/gc_priv.h"
+ && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
+ && !defined(GC_AIX_THREADS)
# if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
&& !defined(USE_HPUX_TLS)
# define USE_HPUX_TLS
# endif
-# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS)) \
- && !defined(USE_PTHREAD_SPECIFIC)
+# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
+ defined(GC_DARWIN_THREADS)) && !defined(USE_PTHREAD_SPECIFIC)
# define USE_PTHREAD_SPECIFIC
# endif
# include <unistd.h>
# include <sys/mman.h>
# include <sys/time.h>
-# include <semaphore.h>
-# include <signal.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
-#if defined(GC_MACOSX_THREADS)
+#if defined(GC_DARWIN_THREADS)
+# include "private/darwin_semaphore.h"
+#else
+# include <semaphore.h>
+#endif /* !GC_DARWIN_THREADS */
+
+#if defined(GC_DARWIN_THREADS)
# include <sys/sysctl.h>
-#endif /* GC_MACOSX_THREADS */
+#endif /* GC_DARWIN_THREADS */
+
+
#if defined(GC_DGUX386_THREADS)
# include <sys/dg_sys_info.h>
# define REAL_FUNC(f) __d10_##f
# endif /* GC_DGUX386_THREADS */
# undef pthread_create
+# if !defined(GC_DARWIN_THREADS)
# undef pthread_sigmask
+# endif
# undef pthread_join
# undef pthread_detach
#endif
-
void GC_thr_init();
-#if DEBUG_THREADS
-
-#ifndef NSIG
-# if defined(MAXSIG)
-# define NSIG (MAXSIG+1)
-# elif defined(_NSIG)
-# define NSIG _NSIG
-# elif defined(__SIGRTMAX)
-# define NSIG (__SIGRTMAX+1)
-# else
- --> please fix it
-# endif
-#endif
-
-void GC_print_sig_mask()
-{
- sigset_t blocked;
- int i;
-
- if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
- ABORT("pthread_sigmask");
- GC_printf0("Blocked: ");
- for (i = 1; i < NSIG; i++) {
- if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
- }
- GC_printf0("\n");
-}
-#endif
-
-word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
-
-#ifdef GC_OSF1_THREADS
- GC_bool GC_retry_signals = TRUE;
-#else
- GC_bool GC_retry_signals = FALSE;
-#endif
-
-/* We use the allocation lock to protect thread-related data structures. */
-
-/* The set of all known threads. We intercept thread creation and */
-/* joins. */
-/* Protected by allocation/GC lock. */
-/* Some of this should be declared volatile, but that's inconsistent */
-/* with some library routine declarations. */
-typedef struct GC_Thread_Rep {
- struct GC_Thread_Rep * next; /* More recently allocated threads */
- /* with a given pthread id come */
- /* first. (All but the first are */
- /* guaranteed to be dead, but we may */
- /* not yet have registered the join.) */
- pthread_t id;
- short flags;
-# define FINISHED 1 /* Thread has exited. */
-# define DETACHED 2 /* Thread is intended to be detached. */
-# define MAIN_THREAD 4 /* True for the original thread only. */
- short thread_blocked; /* Protected by GC lock. */
- /* Treated as a boolean value. If set, */
- /* thread will acquire GC lock before */
- /* doing any pointer manipulations, and */
- /* has set its sp value. Thus it does */
- /* not need to be sent a signal to stop */
- /* it. */
- ptr_t stack_end; /* Cold end of the stack. */
- ptr_t stack_ptr; /* Valid only when stopped. */
-# ifdef IA64
- ptr_t backing_store_end;
- ptr_t backing_store_ptr;
-# endif
- int signal;
- void * status; /* The value returned from the thread. */
- /* Used only to avoid premature */
- /* reclamation of any data it might */
- /* reference. */
- word last_stop_count; /* GC_last_stop_count value when thread */
- /* last successfully handled a suspend */
- /* signal. */
-# ifdef THREAD_LOCAL_ALLOC
-# if CPP_WORDSZ == 64 && defined(ALIGN_DOUBLE)
-# define GRANULARITY 16
-# define NFREELISTS 49
-# else
-# define GRANULARITY 8
-# define NFREELISTS 65
-# endif
- /* The ith free list corresponds to size i*GRANULARITY */
-# define INDEX_FROM_BYTES(n) ((ADD_SLOP(n) + GRANULARITY - 1)/GRANULARITY)
-# define BYTES_FROM_INDEX(i) ((i) * GRANULARITY - EXTRA_BYTES)
-# define SMALL_ENOUGH(bytes) (ADD_SLOP(bytes) <= \
- (NFREELISTS-1)*GRANULARITY)
- ptr_t ptrfree_freelists[NFREELISTS];
- ptr_t normal_freelists[NFREELISTS];
-# ifdef GC_GCJ_SUPPORT
- ptr_t gcj_freelists[NFREELISTS];
-# endif
- /* Free lists contain either a pointer or a small count */
- /* reflecting the number of granules allocated at that */
- /* size. */
- /* 0 ==> thread-local allocation in use, free list */
- /* empty. */
- /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */
- /* too few objects of this size have been */
- /* allocated by this thread. */
- /* >= HBLKSIZE => pointer to nonempty free list. */
- /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */
- /* local alloc, equivalent to 0. */
-# define DIRECT_GRANULES (HBLKSIZE/GRANULARITY)
- /* Don't use local free lists for up to this much */
- /* allocation. */
-# endif
-} * GC_thread;
-
-GC_thread GC_lookup_thread(pthread_t id);
-
static GC_bool parallel_initialized = FALSE;
void GC_init_parallel();
int index = INDEX_FROM_BYTES(bytes);
ptr_t * my_fl;
ptr_t my_entry;
+# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
GC_key_t k = GC_thread_key;
+# endif
void * tsd;
# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
-> ptrfree_freelists + index;
ptr_t my_entry = *my_fl;
+
if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
GC_PTR result = (GC_PTR)my_entry;
*my_fl = obj_link(my_entry);
return result;
} else if ((word)my_entry - 1 < DIRECT_GRANULES) {
*my_fl = my_entry + index + 1;
- return GC_malloc_atomic(bytes);
+ return GC_malloc_atomic(bytes);
} else {
GC_generic_malloc_many(BYTES_FROM_INDEX(index), PTRFREE, my_fl);
/* *my_fl is updated while the collector is excluded; */
# endif /* !THREAD_LOCAL_ALLOC */
-/*
- * We use signals to stop threads during GC.
- *
- * Suspended threads wait in signal handler for SIG_THR_RESTART.
- * That's more portable than semaphores or condition variables.
- * (We do use sem_post from a signal handler, but that should be portable.)
- *
- * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h.
- * Note that we can't just stop a thread; we need it to save its stack
- * pointer(s) and acknowledge.
- */
-
-#ifndef SIG_THR_RESTART
-# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)
-# ifdef _SIGRTMIN
-# define SIG_THR_RESTART _SIGRTMIN + 5
-# else
-# define SIG_THR_RESTART SIGRTMIN + 5
-# endif
-# else
-# define SIG_THR_RESTART SIGXCPU
-# endif
-#endif
-
-#ifdef GC_MACOSX_THREADS
-# include <mach/task.h>
-# include <mach/mach_init.h>
-# include <mach/semaphore.h>
-
- semaphore_t GC_suspend_ack_sem;
-#else
- sem_t GC_suspend_ack_sem;
-#endif
-
#if 0
/*
To make sure that we're using LinuxThreads and not some other thread
void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np;
#endif /* 0 */
-#if defined(SPARC) || defined(IA64)
- extern word GC_save_regs_in_stack();
-#endif
-
long GC_nprocs = 1; /* Number of processors. We may not have */
/* access to all of them, but this is as good */
/* a guess as any ... */
#endif /* !PARALLEL_MARK */
-void GC_suspend_handler(int sig)
-{
- int dummy;
- pthread_t my_thread = pthread_self();
- GC_thread me;
- sigset_t all_sigs;
- sigset_t old_sigs;
- int i;
- sigset_t mask;
-# ifdef PARALLEL_MARK
- word my_mark_no = GC_mark_no;
- /* Marker can't proceed until we acknowledge. Thus this is */
- /* guaranteed to be the mark_no correspending to our */
- /* suspension, i.e. the marker can't have incremented it yet. */
-# endif
- word my_stop_count = GC_stop_count;
-
- if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
-
-#if DEBUG_THREADS
- GC_printf1("Suspending 0x%lx\n", my_thread);
-#endif
-
- me = GC_lookup_thread(my_thread);
- /* The lookup here is safe, since I'm doing this on behalf */
- /* of a thread which holds the allocation lock in order */
- /* to stop the world. Thus concurrent modification of the */
- /* data structure is impossible. */
- if (me -> last_stop_count == my_stop_count) {
- /* Duplicate signal. OK if we are retrying. */
- if (!GC_retry_signals) {
- WARN("Duplicate suspend signal in thread %lx\n",
- pthread_self());
- }
- return;
- }
-# ifdef SPARC
- me -> stack_ptr = (ptr_t)GC_save_regs_in_stack();
-# else
- me -> stack_ptr = (ptr_t)(&dummy);
-# endif
-# ifdef IA64
- me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack();
-# endif
-
- /* Tell the thread that wants to stop the world that this */
- /* thread has been stopped. Note that sem_post() is */
- /* the only async-signal-safe primitive in LinuxThreads. */
-# ifdef GC_MACOSX_THREADS
- semaphore_signal(GC_suspend_ack_sem);
-# else
- sem_post(&GC_suspend_ack_sem);
-# endif
- me -> last_stop_count = my_stop_count;
-
- /* Wait until that thread tells us to restart by sending */
- /* this thread a SIG_THR_RESTART signal. */
- /* SIG_THR_RESTART should be masked at this point. Thus there */
- /* is no race. */
- if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
- if (sigdelset(&mask, SIG_THR_RESTART) != 0) ABORT("sigdelset() failed");
-# ifdef NO_SIGNALS
- if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed");
- if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed");
- if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed");
- if (sigdelset(&mask, SIGABRT) != 0) ABORT("sigdelset() failed");
-# endif
- do {
- me->signal = 0;
- sigsuspend(&mask); /* Wait for signal */
- } while (me->signal != SIG_THR_RESTART);
- /* If the RESTART signal gets lost, we can still lose. That should be */
- /* less likely than losing the SUSPEND signal, since we don't do much */
- /* between the sem_post and sigsuspend. */
- /* We'd need more handshaking to work around that, since we don't want */
- /* to accidentally leave a RESTART signal pending, thus causing us to */
- /* continue prematurely in a future round. */
-
-#if DEBUG_THREADS
- GC_printf1("Continuing 0x%lx\n", my_thread);
-#endif
-}
-
-void GC_restart_handler(int sig)
-{
- pthread_t my_thread = pthread_self();
- GC_thread me;
-
- if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
-
- /* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */
- /* The lookup here is safe, since I'm doing this on behalf */
- /* of a thread which holds the allocation lock in order */
- /* to stop the world. Thus concurrent modification of the */
- /* data structure is impossible. */
- me = GC_lookup_thread(my_thread);
- me->signal = SIG_THR_RESTART;
-
- /*
- ** Note: even if we didn't do anything useful here,
- ** it would still be necessary to have a signal handler,
- ** rather than ignoring the signals, otherwise
- ** the signals will not be delivered at all, and
- ** will thus not interrupt the sigsuspend() above.
- */
-
-#if DEBUG_THREADS
- GC_printf1("In GC_restart_handler for 0x%lx\n", pthread_self());
-#endif
-}
-
/* Defining INSTALL_LOOPING_SEGV_HANDLER causes SIGSEGV and SIGBUS to */
/* result in an infinite loop in a signal handler. This can be very */
/* useful for debugging, since (as of RH7) gdb still seems to have */
GC_bool GC_thr_initialized = FALSE;
-# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
void GC_push_thread_structures GC_PROTO((void))
}
#endif /* HANDLE_FORK */
-/* There seems to be a very rare thread stopping problem. To help us */
-/* debug that, we save the ids of the stopping thread. */
-pthread_t GC_stopping_thread;
-int GC_stopping_pid;
-
-/* We hold the allocation lock. Suspend all threads that might */
-/* still be running. Return the number of suspend signals that */
-/* were sent. */
-int GC_suspend_all()
-{
- int n_live_threads = 0;
- int i;
- GC_thread p;
- int result;
- pthread_t my_thread = pthread_self();
-
- GC_stopping_thread = my_thread; /* debugging only. */
- GC_stopping_pid = getpid(); /* debugging only. */
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> id != my_thread) {
- if (p -> flags & FINISHED) continue;
- if (p -> last_stop_count == GC_stop_count) continue;
- if (p -> thread_blocked) /* Will wait */ continue;
- n_live_threads++;
- #if DEBUG_THREADS
- GC_printf1("Sending suspend signal to 0x%lx\n", p -> id);
- #endif
- result = pthread_kill(p -> id, SIG_SUSPEND);
- switch(result) {
- case ESRCH:
- /* Not really there anymore. Possible? */
- n_live_threads--;
- break;
- case 0:
- break;
- default:
- ABORT("pthread_kill failed");
- }
- }
- }
- }
- return n_live_threads;
-}
-
-/* Caller holds allocation lock. */
-void GC_stop_world()
-{
- int i;
- int n_live_threads;
- int code;
-
- /* Make sure all free list construction has stopped before we start. */
- /* No new construction can start, since free list construction is */
- /* required to acquire and release the GC lock before it starts, */
- /* and we have the lock. */
-# ifdef PARALLEL_MARK
- GC_acquire_mark_lock();
- GC_ASSERT(GC_fl_builder_count == 0);
- /* We should have previously waited for it to become zero. */
-# endif /* PARALLEL_MARK */
- ++GC_stop_count;
- n_live_threads = GC_suspend_all();
- /* sem_getvalue() is not suppored on OS X, and there does not appear */
- /* to be a mach equivalent, so we disable this code. */
-# ifndef GC_MACOSX_THREADS
- if (GC_retry_signals) {
- unsigned long wait_usecs = 0; /* Total wait since retry. */
-# define WAIT_UNIT 3000
-# define RETRY_INTERVAL 100000
- for (;;) {
- int ack_count;
-
- sem_getvalue(&GC_suspend_ack_sem, &ack_count);
- if (ack_count == n_live_threads) break;
- if (wait_usecs > RETRY_INTERVAL) {
- int newly_sent = GC_suspend_all();
-
-# ifdef CONDPRINT
- if (GC_print_stats) {
- GC_printf1("Resent %ld signals after timeout\n",
- newly_sent);
- }
-# endif
- sem_getvalue(&GC_suspend_ack_sem, &ack_count);
- if (newly_sent < n_live_threads - ack_count) {
- WARN("Lost some threads during GC_stop_world?!\n",0);
- n_live_threads = ack_count + newly_sent;
- }
- wait_usecs = 0;
- }
- usleep(WAIT_UNIT);
- wait_usecs += WAIT_UNIT;
- }
- }
-# endif /* GC_MACOSX_THREADS */
- for (i = 0; i < n_live_threads; i++) {
-# ifdef GC_MACOSX_THREADS
- if (KERN_SUCCESS != semaphore_wait(GC_suspend_ack_sem))
- ABORT("semaphore_wait for handler failed");
-# else
- if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
- GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
- ABORT("sem_wait for handler failed");
- }
-# endif
- }
-# ifdef PARALLEL_MARK
- GC_release_mark_lock();
-# endif
- #if DEBUG_THREADS
- GC_printf1("World stopped from 0x%lx\n", pthread_self());
- #endif
- GC_stopping_thread = 0; /* debugging only */
-}
-
-/* Caller holds allocation lock, and has held it continuously since */
-/* the world stopped. */
-void GC_start_world()
-{
- pthread_t my_thread = pthread_self();
- register int i;
- register GC_thread p;
- register int n_live_threads = 0;
- register int result;
-
-# if DEBUG_THREADS
- GC_printf0("World starting\n");
-# endif
-
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> id != my_thread) {
- if (p -> flags & FINISHED) continue;
- if (p -> thread_blocked) continue;
- n_live_threads++;
- #if DEBUG_THREADS
- GC_printf1("Sending restart signal to 0x%lx\n", p -> id);
- #endif
- result = pthread_kill(p -> id, SIG_THR_RESTART);
- switch(result) {
- case ESRCH:
- /* Not really there anymore. Possible? */
- n_live_threads--;
- break;
- case 0:
- break;
- default:
- ABORT("pthread_kill failed");
- }
- }
- }
- }
- #if DEBUG_THREADS
- GC_printf0("World started\n");
- #endif
-}
-
-# ifdef IA64
-# define IF_IA64(x) x
-# else
-# define IF_IA64(x)
-# endif
-/* We hold allocation lock. Should do exactly the right thing if the */
-/* world is stopped. Should not fail if it isn't. */
-void GC_push_all_stacks()
-{
- int i;
- GC_thread p;
- ptr_t sp = GC_approx_sp();
- ptr_t lo, hi;
- /* On IA64, we also need to scan the register backing store. */
- IF_IA64(ptr_t bs_lo; ptr_t bs_hi;)
- pthread_t me = pthread_self();
-
- if (!GC_thr_initialized) GC_thr_init();
- #if DEBUG_THREADS
- GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
- #endif
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> flags & FINISHED) continue;
- if (pthread_equal(p -> id, me)) {
-# ifdef SPARC
- lo = (ptr_t)GC_save_regs_in_stack();
-# else
- lo = GC_approx_sp();
-# endif
- IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
- } else {
- lo = p -> stack_ptr;
- IF_IA64(bs_hi = p -> backing_store_ptr;)
- }
- if ((p -> flags & MAIN_THREAD) == 0) {
- hi = p -> stack_end;
- IF_IA64(bs_lo = p -> backing_store_end);
- } else {
- /* The original stack. */
- hi = GC_stackbottom;
- IF_IA64(bs_lo = BACKING_STORE_BASE;)
- }
- #if DEBUG_THREADS
- GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
- (unsigned long) p -> id,
- (unsigned long) lo, (unsigned long) hi);
- #endif
- if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
-# ifdef STACK_GROWS_UP
- /* We got them backwards! */
- GC_push_all_stack(hi, lo);
-# else
- GC_push_all_stack(lo, hi);
-# endif
-# ifdef IA64
- if (pthread_equal(p -> id, me)) {
- GC_push_all_eager(bs_lo, bs_hi);
- } else {
- GC_push_all_stack(bs_lo, bs_hi);
- }
-# endif
- }
- }
-}
-
#ifdef USE_PROC_FOR_LIBRARIES
int GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
{
/* the real one. */
char stat_buf[STAT_BUF_SIZE];
int f;
- char c;
word result = 1;
/* Some old kernels only have a single "cpu nnnn ..." */
/* entry in /proc/stat. We identify those as */
/* If wait_for_all is true, then we exit with the GC lock held and no */
/* collection in progress; otherwise we just wait for the current GC */
/* to finish. */
+extern GC_bool GC_collection_in_progress();
void GC_wait_for_gc_completion(GC_bool wait_for_all)
{
if (GC_incremental && GC_collection_in_progress()) {
/* We hold the allocation lock. */
void GC_thr_init()
{
- int dummy;
+# ifndef GC_DARWIN_THREADS
+ int dummy;
+# endif
GC_thread t;
- struct sigaction act;
if (GC_thr_initialized) return;
GC_thr_initialized = TRUE;
-
-# ifdef GC_MACOSX_THREADS
- if (semaphore_create(mach_task_self(), &GC_suspend_ack_sem,
- SYNC_POLICY_FIFO, 0) != KERN_SUCCESS)
- ABORT("semaphore_create failed");
-# else
- if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
- ABORT("sem_init failed");
-# endif
-
- act.sa_flags = SA_RESTART;
- if (sigfillset(&act.sa_mask) != 0) {
- ABORT("sigfillset() failed");
- }
-# ifdef NO_SIGNALS
- if (sigdelset(&act.sa_mask, SIGINT) != 0
- || sigdelset(&act.sa_mask, SIGQUIT != 0)
- || sigdelset(&act.sa_mask, SIGABRT != 0)
- || sigdelset(&act.sa_mask, SIGTERM != 0)) {
- ABORT("sigdelset() failed");
- }
-# endif
-
- /* SIG_THR_RESTART is unmasked by the handler when necessary. */
- act.sa_handler = GC_suspend_handler;
- if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
- ABORT("Cannot set SIG_SUSPEND handler");
- }
-
- act.sa_handler = GC_restart_handler;
- if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
- ABORT("Cannot set SIG_THR_RESTART handler");
- }
+
# ifdef HANDLE_FORK
/* Prepare for a possible fork. */
pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
# endif /* HANDLE_FORK */
/* Add the initial thread, so we can stop it. */
t = GC_new_thread(pthread_self());
- t -> stack_ptr = (ptr_t)(&dummy);
+# ifdef GC_DARWIN_THREADS
+ t -> stop_info.mach_thread = mach_thread_self();
+# else
+ t -> stop_info.stack_ptr = (ptr_t)(&dummy);
+# endif
t -> flags = DETACHED | MAIN_THREAD;
- /* Check for GC_RETRY_SIGNALS. */
- if (0 != GETENV("GC_RETRY_SIGNALS")) {
- GC_retry_signals = TRUE;
- }
- if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
- GC_retry_signals = FALSE;
- }
-# ifdef CONDPRINT
- if (GC_print_stats && GC_retry_signals) {
- GC_printf0("Will retry suspend signal if necessary.\n");
- }
-# endif
+ GC_stop_init();
/* Set GC_nprocs. */
{
# if defined(GC_FREEBSD_THREADS)
GC_nprocs = 1;
# endif
-# if defined(GC_MACOSX_THREADS)
+# if defined(GC_DARWIN_THREADS)
int ncpus = 1;
size_t len = sizeof(ncpus);
sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
{
if (parallel_initialized) return;
parallel_initialized = TRUE;
- /* GC_init() calls us back, so set flag first. */
+
+ /* GC_init() calls us back, so set flag first. */
if (!GC_is_initialized) GC_init();
/* If we are using a parallel marker, start the helper threads. */
# ifdef PARALLEL_MARK
}
+#if !defined(GC_DARWIN_THREADS)
int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
{
sigset_t fudged_set;
}
return(REAL_FUNC(pthread_sigmask)(how, set, oset));
}
+#endif /* !GC_DARWIN_THREADS */
/* Wrappers for functions that are likely to block for an appreciable */
/* length of time. Must be called in pairs, if at all. */
me = GC_lookup_thread(pthread_self());
GC_ASSERT(!(me -> thread_blocked));
# ifdef SPARC
- me -> stack_ptr = (ptr_t)GC_save_regs_in_stack();
+ me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
# else
- me -> stack_ptr = (ptr_t)GC_approx_sp();
+# ifndef GC_DARWIN_THREADS
+ me -> stop_info.stack_ptr = (ptr_t)GC_approx_sp();
+# endif
# endif
# ifdef IA64
me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack() + SP_SLOP;
# endif
/* Add some slop to the stack pointer, since the wrapped call may */
/* end up pushing more callee-save registers. */
+# ifndef GC_DARWIN_THREADS
# ifdef STACK_GROWS_UP
- me -> stack_ptr += SP_SLOP;
+ me -> stop_info.stack_ptr += SP_SLOP;
# else
- me -> stack_ptr -= SP_SLOP;
+ me -> stop_info.stack_ptr -= SP_SLOP;
+# endif
# endif
me -> thread_blocked = TRUE;
UNLOCK();
}
-GC_end_blocking(void) {
+void GC_end_blocking(void) {
GC_thread me;
LOCK(); /* This will block if the world is stopped. */
me = GC_lookup_thread(pthread_self());
void *(*start_routine)(void *);
void *arg;
word flags;
-#ifdef GC_MACOSX_THREADS
- semaphore_t registered;
-#else
sem_t registered; /* 1 ==> in our thread table, but */
/* parent hasn't yet noticed. */
-#endif
};
/* Called at thread exit. */
# endif
LOCK();
me = GC_new_thread(my_pthread);
+#ifdef GC_DARWIN_THREADS
+ me -> stop_info.mach_thread = mach_thread_self();
+#else
+ me -> stop_info.stack_ptr = 0;
+#endif
me -> flags = si -> flags;
- me -> stack_ptr = 0;
/* me -> stack_end = GC_linux_stack_base(); -- currently (11/99) */
/* doesn't work because the stack base in /proc/self/stat is the */
/* one for the main thread. There is a strong argument that that's */
# ifdef STACK_GROWS_DOWN
me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
& ~(GC_page_size - 1));
- me -> stack_ptr = me -> stack_end - 0x10;
+# ifndef GC_DARWIN_THREADS
+ me -> stop_info.stack_ptr = me -> stack_end - 0x10;
+# endif
/* Needs to be plausible, since an asynchronous stack mark */
/* should not crash. */
# else
me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1));
- me -> stack_ptr = me -> stack_end + 0x10;
+ me -> stop_info.stack_ptr = me -> stack_end + 0x10;
# endif
/* This is dubious, since we may be more than a page into the stack, */
/* and hence skip some of it, though it's not clear that matters. */
GC_printf1("start_routine = 0x%lx\n", start);
# endif
start_arg = si -> arg;
-# ifdef GC_MACOSX_THREADS
- semaphore_signal(si->registered);
-# else
- sem_post(&(si -> registered)); /* Last action on si. */
-# endif
+ sem_post(&(si -> registered)); /* Last action on si. */
/* OK to deallocate. */
pthread_cleanup_push(GC_thread_exit_proc, 0);
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
void *(*start_routine)(void *), void *arg)
{
int result;
- GC_thread t;
- pthread_t my_new_thread;
int detachstate;
word my_flags = 0;
struct start_info * si;
UNLOCK();
if (!parallel_initialized) GC_init_parallel();
if (0 == si) return(ENOMEM);
-# ifdef GC_MACOSX_THREADS
- semaphore_create(mach_task_self(), &si->registered, SYNC_POLICY_FIFO, 0);
-# else
- sem_init(&(si -> registered), 0, 0);
-# endif
+ sem_init(&(si -> registered), 0, 0);
si -> start_routine = start_routine;
si -> arg = arg;
LOCK();
# endif
result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, si);
+
# ifdef DEBUG_THREADS
GC_printf1("Started thread 0x%X\n", *new_thread);
# endif
/* This also ensures that we hold onto si until the child is done */
/* with it. Thus it doesn't matter whether it is otherwise */
/* visible to the collector. */
-# ifdef GC_MACOSX_THREADS
- semaphore_wait(si->registered);
- semaphore_destroy(mach_task_self(), si->registered);
-# else
- while (0 != sem_wait(&(si -> registered))) {
- if (EINTR != errno) ABORT("sem_wait failed");
- }
- sem_destroy(&(si -> registered));
-# endif
+ while (0 != sem_wait(&(si -> registered))) {
+ if (EINTR != errno) ABORT("sem_wait failed");
+ }
+ sem_destroy(&(si -> registered));
LOCK();
GC_INTERNAL_FREE(si);
UNLOCK();
void GC_pause()
{
int i;
- volatile word dummy = 0;
+# ifndef __GNUC__
+ volatile word dummy = 0;
+# endif
for (i = 0; i < 10; ++i) {
# ifdef __GNUC__
void GC_generic_lock(pthread_mutex_t * lock)
{
+#ifndef NO_PTHREAD_TRYLOCK
unsigned pause_length = 1;
unsigned i;
ABORT("Unexpected error from pthread_mutex_trylock");
}
}
+#endif /* !NO_PTHREAD_TRYLOCK */
pthread_mutex_lock(lock);
}
}
#else /* !USE_SPINLOCK */
-
void GC_lock()
{
+#ifndef NO_PTHREAD_TRYLOCK
if (1 == GC_nprocs || GC_collecting) {
pthread_mutex_lock(&GC_allocate_ml);
} else {
GC_generic_lock(&GC_allocate_ml);
}
+#else /* !NO_PTHREAD_TRYLOCK */
+ pthread_mutex_lock(&GC_allocate_ml);
+#endif /* !NO_PTHREAD_TRYLOCK */
}
#endif /* !USE_SPINLOCK */
# include <pthread.h>
# endif
-# ifdef GC_WIN32_THREADS
+# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
static CRITICAL_SECTION incr_cs;
# endif
+#ifdef __STDC__
+# include <stdarg.h>
+#endif
+
/* Allocation Statistics */
int stubborn_count = 0;
}
}
-/* Try to force a to be strangely aligned */
-struct {
- char dummy;
- sexpr aa;
-} A;
-#define a A.aa
-
/*
* A tiny list reversal test to check thread creation.
*/
#endif
+/* Try to force a to be strangely aligned */
+struct {
+ char dummy;
+ sexpr aa;
+} A;
+#define a A.aa
+
/*
* Repeatedly reverse lists built out of very different sized cons cells.
* Check that we didn't lose anything.
b = c = 0;
}
+#undef a
+
/*
* The rest of this builds balanced binary trees, checks that they don't
* disappear, and tests finalization.
fail_count++;
}
+static void uniq(void *p, ...) {
+ va_list a;
+ void *q[100];
+ int n = 0, i, j;
+ q[n++] = p;
+ va_start(a,p);
+ for (;(q[n] = va_arg(a,void *));n++) ;
+ va_end(a);
+ for (i=0; i<n; i++)
+ for (j=0; j<i; j++)
+ if (q[i] == q[j]) {
+ GC_printf0(
+ "Apparently failed to mark form some function arguments.\n"
+ "Perhaps GC_push_regs was configured incorrectly?\n"
+ );
+ FAIL;
+ }
+}
+
#endif /* __STDC__ */
#ifdef THREADS
# ifdef GC_GCJ_SUPPORT
GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *));
GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc);
+# endif
+ /* Make sure that fn arguments are visible to the collector. */
+# ifdef __STDC__
+ uniq(
+ GC_malloc(12), GC_malloc(12), GC_malloc(12),
+ (GC_gcollect(),GC_malloc(12)),
+ GC_malloc(12), GC_malloc(12), GC_malloc(12),
+ (GC_gcollect(),GC_malloc(12)),
+ GC_malloc(12), GC_malloc(12), GC_malloc(12),
+ (GC_gcollect(),GC_malloc(12)),
+ GC_malloc(12), GC_malloc(12), GC_malloc(12),
+ (GC_gcollect(),GC_malloc(12)),
+ GC_malloc(12), GC_malloc(12), GC_malloc(12),
+ (GC_gcollect(),GC_malloc(12)),
+ (void *)0);
# endif
/* Repeated list reversal test. */
reverse_test();
# endif
n_tests = 0;
+#if defined(__APPLE__) && defined(__MACH__)
+ GC_INIT();
+#endif
+
# if defined(DJGPP)
/* No good way to determine stack base from library; do it */
/* manually on this platform. */
# endif
GC_INIT(); /* Only needed if gc is dynamic library. */
(void) GC_set_warn_proc(warn_proc);
-# if (defined(MPROTECT_VDB) || defined(PROC_VDB)) && !defined(MAKE_BACK_GRAPH)
+# if (defined(MPROTECT_VDB) || defined(PROC_VDB)) \
+ && !defined(MAKE_BACK_GRAPH)
GC_enable_incremental();
(void) GC_printf0("Switched to incremental mode\n");
# if defined(MPROTECT_VDB)
(void)GC_printf0("Emulating dirty bits with mprotect/signals\n");
# else
+# ifdef PROC_VDB
(void)GC_printf0("Reading dirty bits from /proc\n");
+# else
+ (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n");
+# endif
# endif
# endif
run_one_test();
(void)GC_printf0("pthread_default_stacksize_np failed.\n");
}
# endif /* GC_HPUX_THREADS */
+# if defined(__APPLE__) && defined(__MACH__)
+ GC_INIT();
+# endif
+
pthread_attr_init(&attr);
# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \
- || defined(GC_MACOSX_THREADS)
+ || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)
pthread_attr_setstacksize(&attr, 1000000);
# endif
n_tests = 0;
-# if defined(MPROTECT_VDB) && !defined(PARALLEL_MARK) &&!defined(REDIRECT_MALLOC) && !defined(MAKE_BACK_GRAPH)
+# if (defined(MPROTECT_VDB)) \
+ && !defined(PARALLEL_MARK) &&!defined(REDIRECT_MALLOC) \
+ && !defined(MAKE_BACK_GRAPH)
GC_enable_incremental();
(void) GC_printf0("Switched to incremental mode\n");
- (void) GC_printf0("Emulating dirty bits with mprotect/signals\n");
+# if defined(MPROTECT_VDB)
+ (void)GC_printf0("Emulating dirty bits with mprotect/signals\n");
+# else
+# ifdef PROC_VDB
+ (void)GC_printf0("Reading dirty bits from /proc\n");
+# else
+ (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n");
+# endif
+# endif
# endif
(void) GC_set_warn_proc(warn_proc);
if ((code = pthread_key_create(&fl_key, 0)) != 0) {
# endif
#endif
+ GC_init();
+
# if defined(MACOS) // MacOS
char* argv_[] = {"test_cpp", "10"}; // doesn't
argv = argv_; // have a
# endif
# if defined(GC_LINUX_THREADS) || defined(GC_IRIX_THREADS) \
|| defined(GC_FREEBSD_THREADS) || defined(GC_SOLARIS_PTHREADS) \
- || defined(GC_MACOSX_THREADS)
+ || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)
printf("-lpthread\n");
# endif
# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)
/* it to keep the old-style build process working. */
#define GC_TMP_VERSION_MAJOR 6
#define GC_TMP_VERSION_MINOR 2
-#define GC_TMP_ALPHA_VERSION 5
+#define GC_TMP_ALPHA_VERSION 6
#if defined(GC_VERSION_MAJOR)
# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR || \
#if defined(GC_WIN32_THREADS)
#include "private/gc_priv.h"
+#include <windows.h>
#ifdef CYGWIN32
# include <errno.h>
if (*lo < start) *lo = start;
}
+#if !defined(CYGWIN32)
+
#if !defined(MSWINCE) && defined(GC_DLL)
/* We register threads from DllMain */
}
#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
+#endif /* !CYGWIN32 */
+
#ifdef MSWINCE
typedef struct {