From 2f098983b69715ee180182cc4f27407428b7ef59 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Thu, 5 Jun 2003 00:00:00 +0000 Subject: [PATCH] gc6.2alpha6 tarball import --- Makefile | 22 +- Makefile.am | 40 +- Makefile.direct | 22 +- Makefile.dj | 6 +- Makefile.in | 84 ++-- irix_threads.c => aix_irix_threads.c | 266 ++++++++---- configure | 99 ++++- configure.in | 25 +- darwin_stop_world.c | 205 +++++++++ doc/README | 2 +- doc/README.MacOSX | 41 +- doc/README.changes | 31 +- dyn_load.c | 123 ++++-- gc_dlopen.c | 2 +- include/Makefile.am | 2 + include/Makefile.in | 2 + include/gc.h | 13 +- include/gc_config_macros.h | 9 +- include/gc_pthread_redirects.h | 6 + include/private/darwin_semaphore.h | 68 +++ include/private/darwin_stop_world.h | 15 + include/private/gc_locks.h | 79 +++- include/private/gc_priv.h | 15 +- include/private/gcconfig.h | 74 +++- include/private/pthread_stop_world.h | 12 + include/private/pthread_support.h | 97 +++++ mach_dep.c | 46 ++- mark_rts.c | 69 +++- misc.c | 9 + os_dep.c | 778 +++++++++++++++++++++++++++-------- powerpc_darwin_mach_dep.s | 84 ++++ powerpc_macosx_mach_dep.s | 95 ----- pthread_stop_world.c | 445 ++++++++++++++++++++ linux_threads.c => pthread_support.c | 661 ++++------------------------- tests/test.c | 87 +++- tests/test_cpp.cc | 2 + threadlibs.c | 2 +- version.h | 2 +- win32_threads.c | 5 + 39 files changed, 2477 insertions(+), 1168 deletions(-) rename irix_threads.c => aix_irix_threads.c (73%) create mode 100644 darwin_stop_world.c create mode 100644 include/private/darwin_semaphore.h create mode 100644 include/private/darwin_stop_world.h create mode 100644 include/private/pthread_stop_world.h create mode 100644 include/private/pthread_support.h create mode 100644 powerpc_darwin_mach_dep.s delete mode 100755 powerpc_macosx_mach_dep.s create mode 100644 pthread_stop_world.c rename linux_threads.c => pthread_support.c (70%) diff --git a/Makefile b/Makefile index 9c2cf4c..d2742b0 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,8 @@ HOSTCFLAGS=$(CFLAGS) # -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 @@ -253,7 +255,7 @@ HOSTCFLAGS=$(CFLAGS) # 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 @@ -277,9 +279,9 @@ AR= ar RANLIB= ranlib -OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o 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 @@ -298,10 +300,12 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \ include/private/solaris_threads.h include/gc_backptr.h \ hpux_test_and_clear.s include/gc_gcj.h \ include/gc_local_alloc.h include/private/dbg_mlc.h \ - include/private/specific.h powerpc_macosx_mach_dep.s \ + include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h 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 \ @@ -334,7 +338,7 @@ OTHER_FILES= Makefile setjmp_t.c callprocs pc_excludes \ MacProjects.sit.hqx MacOS.c \ Mac_files/datastart.c Mac_files/dataend.c \ Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ - add_gc_prefix.c gc_cpp.cpp win32_threads.c \ + add_gc_prefix.c gc_cpp.cpp \ version.h AmigaOS.c \ $(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES) @@ -388,7 +392,7 @@ $(OBJS) tests/test.o dyn_load.o dyn_load_sunos53.o: \ 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 @@ -485,7 +489,7 @@ liblinuxgc.so: $(OBJS) dyn_load.o 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) @@ -494,7 +498,7 @@ mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S \ | ./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 diff --git a/Makefile.am b/Makefile.am index 7b851e2..649ede4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,9 +25,12 @@ SUBDIRS = doc include 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 \ @@ -35,12 +38,20 @@ include/leak_detector.h include/gc_typed.h @addincludes@ 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: @@ -49,7 +60,7 @@ libgc_la_DEPENDENCIES = @addobjs@ 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 @@ -62,8 +73,13 @@ EXTRA_DIST += alpha_mach_dep.S mips_sgi_mach_dep.S sparc_mach_dep.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 @@ -79,11 +95,7 @@ gctest_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS) 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) diff --git a/Makefile.direct b/Makefile.direct index 9c2cf4c..d2742b0 100644 --- a/Makefile.direct +++ b/Makefile.direct @@ -76,6 +76,8 @@ HOSTCFLAGS=$(CFLAGS) # -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 @@ -253,7 +255,7 @@ HOSTCFLAGS=$(CFLAGS) # 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 @@ -277,9 +279,9 @@ AR= ar RANLIB= ranlib -OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o 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 @@ -298,10 +300,12 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \ include/private/solaris_threads.h include/gc_backptr.h \ hpux_test_and_clear.s include/gc_gcj.h \ include/gc_local_alloc.h include/private/dbg_mlc.h \ - include/private/specific.h powerpc_macosx_mach_dep.s \ + include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h 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 \ @@ -334,7 +338,7 @@ OTHER_FILES= Makefile setjmp_t.c callprocs pc_excludes \ MacProjects.sit.hqx MacOS.c \ Mac_files/datastart.c Mac_files/dataend.c \ Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ - add_gc_prefix.c gc_cpp.cpp win32_threads.c \ + add_gc_prefix.c gc_cpp.cpp \ version.h AmigaOS.c \ $(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES) @@ -388,7 +392,7 @@ $(OBJS) tests/test.o dyn_load.o dyn_load_sunos53.o: \ 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 @@ -485,7 +489,7 @@ liblinuxgc.so: $(OBJS) dyn_load.o 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) @@ -494,7 +498,7 @@ mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.S \ | ./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 diff --git a/Makefile.dj b/Makefile.dj index 146c83b..6097293 100644 --- a/Makefile.dj +++ b/Makefile.dj @@ -177,7 +177,7 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \ include/private/solaris_threads.h include/gc_backptr.h \ hpux_test_and_clear.s include/gc_gcj.h \ include/gc_local_alloc.h include/private/dbg_mlc.h \ - include/private/specific.h powerpc_macosx_mach_dep.s \ + include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h $(CORD_SRCS) OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ @@ -285,13 +285,13 @@ liblinuxgc.so: $(OBJS) dyn_load.o 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 diff --git a/Makefile.in b/Makefile.in index 4d3ee55..6d38aaa 100644 --- a/Makefile.in +++ b/Makefile.in @@ -162,9 +162,9 @@ cord/cordbscs.c cord/cordtest.c cord/de.c cord/de_win.c \ 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 \ @@ -173,12 +173,17 @@ include/leak_detector.h include/gc_typed.h @addincludes@ 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 @@ -188,7 +193,7 @@ libgc_la_DEPENDENCIES = @addobjs@ 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 @@ -200,8 +205,10 @@ libgccpp_la_LDFLAGS = -version-info 1:2:0 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 @@ -209,7 +216,7 @@ gctest_LDADD = ./libgc.la $(THREADLIBS) $(EXTRA_TEST_LIBS) 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) @@ -237,19 +244,22 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs 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 @@ -268,19 +278,22 @@ LDFLAGS = @LDFLAGS@ 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 \ @@ -370,7 +383,7 @@ clean-libLTLIBRARIES: 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 \ @@ -383,6 +396,9 @@ gctest$(EXEEXT): $(gctest_OBJECTS) $(gctest_DEPENDENCIES) @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 @@ -390,11 +406,13 @@ mostlyclean-compile: 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@ @@ -402,8 +420,6 @@ distclean-compile: @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@ @@ -413,6 +429,8 @@ distclean-compile: @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@ @@ -887,10 +905,6 @@ test.o: $(srcdir)/tests/test.c # 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 \ diff --git a/irix_threads.c b/aix_irix_threads.c similarity index 73% rename from irix_threads.c rename to aix_irix_threads.c index 48173e4..4785160 100644 --- a/irix_threads.c +++ b/aix_irix_threads.c @@ -1,7 +1,7 @@ /* * 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. @@ -13,22 +13,24 @@ * 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 +# include # include # include # include @@ -39,10 +41,9 @@ #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(); @@ -104,8 +105,14 @@ GC_thread GC_lookup_thread(pthread_t id); * The only way to suspend threads given the pthread interface is to send * signals. Unfortunately, this means we have to reserve * a signal, and intercept client calls to change the signal mask. - * We use SIG_SUSPEND, defined in gc_priv.h. */ +#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 */ @@ -145,6 +152,8 @@ GC_bool GC_thr_initialized = FALSE; size_t GC_min_stack_sz; +size_t GC_page_sz; + # define N_FREE_LISTS 25 ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 }; /* GC_stack_free_lists[i] is free list for stacks of */ @@ -173,14 +182,14 @@ ptr_t GC_stack_alloc(size_t * stack_size) 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; @@ -227,7 +236,7 @@ GC_thread GC_new_thread(pthread_t id) /* 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; @@ -294,6 +303,42 @@ GC_thread GC_lookup_thread(pthread_t 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() @@ -371,6 +416,8 @@ void GC_start_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) @@ -409,7 +456,41 @@ void GC_push_all_stacks() 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 @@ -424,6 +505,7 @@ void GC_push_all_stacks() # 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 } @@ -440,6 +522,8 @@ void GC_thr_init() 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"); @@ -449,6 +533,7 @@ void GC_thr_init() (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; @@ -460,6 +545,10 @@ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { 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); @@ -474,8 +563,7 @@ struct start_info { 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) @@ -506,33 +594,10 @@ int GC_pthread_join(pthread_t thread, void **retval) /* Some versions of the Irix pthreads library can erroneously */ /* return EINTR when the call succeeds. */ if (EINTR == result) result = 0; - if (result == 0) { - LOCK(); - /* Here the pthread thread id may have been recycled. */ - GC_delete_gc_thread(thread, thread_gc_id); - UNLOCK(); - } - return result; -} - -int GC_pthread_detach(pthread_t thread) -{ - int result; - GC_thread thread_gc_id; - LOCK(); - thread_gc_id = GC_lookup_thread(thread); + /* 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; } @@ -562,7 +627,12 @@ void * GC_start_routine(void * arg) 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; @@ -578,7 +648,43 @@ void * GC_start_routine(void * 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, @@ -593,8 +699,9 @@ 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)) { @@ -603,7 +710,8 @@ GC_pthread_create(pthread_t *new_thread, 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); @@ -613,25 +721,43 @@ GC_pthread_create(pthread_t *new_thread, } 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 */ @@ -644,12 +770,14 @@ GC_pthread_create(pthread_t *new_thread, } } 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. */ @@ -658,9 +786,9 @@ VOLATILE GC_bool GC_collecting = 0; #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() { @@ -720,11 +848,11 @@ yield: } } -# else +# else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */ #ifndef LINT int GC_no_Irix_threads; #endif -# endif /* GC_IRIX_THREADS */ +# endif /* IRIX_THREADS */ diff --git a/configure b/configure index db84f60..845fff8 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #! /bin/sh # From configure.in Revision: 1.2 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.53 for gc 6.2alpha5. +# Generated by GNU Autoconf 2.53 for gc 6.2alpha6. # # Report bugs to . # @@ -416,8 +416,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # 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" @@ -930,7 +930,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gc 6.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]... @@ -997,7 +997,7 @@ fi 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 @@ -1106,7 +1106,7 @@ fi 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 @@ -1121,7 +1121,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gc $as_me 6.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 $@ @@ -1782,7 +1782,7 @@ fi # Define the identity of the package. PACKAGE=gc - VERSION=6.2alpha5 + VERSION=6.2alpha6 cat >>confdefs.h <<_ACEOF @@ -3555,6 +3555,16 @@ _ACEOF _ACEOF ;; + *-*-aix*) + cat >>confdefs.h <<\_ACEOF +#define GC_AIX_THREADS 1 +_ACEOF + + cat >>confdefs.h <<\_ACEOF +#define _REENTRANT 1 +_ACEOF + + ;; *-*-hpux*) { echo "$as_me:$LINENO: WARNING: \"Only HP/UX 11 threads are supported.\"" >&5 echo "$as_me: WARNING: \"Only HP/UX 11 threads are supported.\"" >&2;} @@ -3612,9 +3622,19 @@ _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 @@ -3688,7 +3708,24 @@ echo "$as_me: error: $THREADS is an unknown thread package" >&2;} 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*) ;; *) @@ -3788,13 +3825,23 @@ _ACEOF 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 + + + + @@ -5344,7 +5391,7 @@ test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes 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=$? @@ -5880,7 +5927,7 @@ chmod -w . 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 @@ -7673,7 +7720,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&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 @@ -9200,7 +9261,7 @@ _ASBOX } >&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 @@ -9257,7 +9318,7 @@ _ACEOF 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'`\\" @@ -9502,8 +9563,12 @@ s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t 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 diff --git a/configure.in b/configure.in index 2e18090..cb6477d 100644 --- a/configure.in +++ b/configure.in @@ -17,7 +17,7 @@ dnl Process this file with autoconf to produce configure. # Initialization # ============== -AC_INIT(gc,6.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 @@ -89,6 +89,10 @@ case "$THREADS" in AC_DEFINE(GC_LINUX_THREADS) AC_DEFINE(_REENTRANT) ;; + *-*-aix*) + AC_DEFINE(GC_AIX_THREADS) + AC_DEFINE(_REENTRANT) + ;; *-*-hpux*) AC_MSG_WARN("Only HP/UX 11 threads are supported.") AC_DEFINE(GC_HPUX_THREADS) @@ -116,7 +120,11 @@ case "$THREADS" in 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) @@ -159,7 +167,15 @@ AC_MSG_RESULT($THREADLIBS) 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*) ;; *) @@ -198,11 +214,12 @@ case "$TARGET_ECOS" in 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) diff --git a/darwin_stop_world.c b/darwin_stop_world.c new file mode 100644 index 0000000..28ade58 --- /dev/null +++ b/darwin_stop_world.c @@ -0,0 +1,205 @@ +#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;inext) { + 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 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 diff --git a/doc/README b/doc/README index 581d69d..593a4b5 100644 --- a/doc/README +++ b/doc/README @@ -28,7 +28,7 @@ are GPL'ed, but with an exception that should cover all uses in the collector. (If you are concerned about such things, I recommend you look at the notice in config.guess or ltmain.sh.) -This is version 6.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 diff --git a/doc/README.MacOSX b/doc/README.MacOSX index a517dd6..f5333d5 100644 --- a/doc/README.MacOSX +++ b/doc/README.MacOSX @@ -1,40 +1 @@ -While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested -it on MacOS X Server. -I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is -no longer necessary. Incremental collection is supported via mprotect/signal. -The current solution isn't really optimal because the signal handler must decode -the faulting PPC machine instruction in order to find the correct heap address. -Further, it must poke around in the register state which the kernel saved away -in some obscure register state structure before it calls the signal handler - -needless to say the layout of this structure is no where documented. -Threads and dynamic libraries are not yet supported (adding dynamic library -support via the low-level dyld API shouldn't be that hard). - -The original MacOS X port was brought to you by Andrew Stone. - - -June, 1 2000 - -Dietmar Planitzer -dave.pl@ping.at - -Note from Andrew Begel: - -One more fix to enable gc.a to link successfully into a shared library for -MacOS X. You have to add -fno-common to the CFLAGS in the Makefile. MacOSX -disallows common symbols in anything that eventually finds its way into a -shared library. (I don't completely understand why, but -fno-common seems to -work and doesn't mess up the garbage collector's functionality). - -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. diff --git a/doc/README.changes b/doc/README.changes index 125f83f..781fd42 100644 --- a/doc/README.changes +++ b/doc/README.changes @@ -1834,10 +1834,39 @@ Since 6.2alpha4: 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 diff --git a/dyn_load.c b/dyn_load.c index aacdaf6..9a530a2 100644 --- a/dyn_load.c +++ b/dyn_load.c @@ -58,7 +58,7 @@ !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. @@ -961,7 +961,7 @@ void GC_register_dynamic_libraries() len = ldi->ldinfo_next; GC_add_roots_inner( ldi->ldinfo_dataorg, - (unsigned long)ldi->ldinfo_dataorg + (ptr_t)(unsigned long)ldi->ldinfo_dataorg + ldi->ldinfo_datasize, TRUE); ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; @@ -969,63 +969,122 @@ void GC_register_dynamic_libraries() } #endif /* RS6000 */ -#ifdef MACOSX +#ifdef DARWIN #include #include -/*#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;ifiletype; +#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;isize == 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;isize == 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 */ diff --git a/gc_dlopen.c b/gc_dlopen.c index 1f38907..4c690ed 100644 --- a/gc_dlopen.c +++ b/gc_dlopen.c @@ -25,7 +25,7 @@ #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) diff --git a/include/Makefile.am b/include/Makefile.am index d754edb..306026b 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -30,4 +30,6 @@ dist_noinst_HEADERS = private/gc_hdrs.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 diff --git a/include/Makefile.in b/include/Makefile.in index cb650a8..9ade270 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -134,6 +134,8 @@ dist_noinst_HEADERS = private/gc_hdrs.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 diff --git a/include/gc.h b/include/gc.h index dfecfca..f3e31d9 100644 --- a/include/gc.h +++ b/include/gc.h @@ -341,6 +341,10 @@ GC_API void GC_clear_roots GC_PROTO((void)); GC_API void GC_add_roots GC_PROTO((char * low_address, char * high_address_plus_1)); +/* Remove a root segment. Wizards only. */ +GC_API void GC_remove_roots GC_PROTO((char * low_address, + char * high_address_plus_1)); + /* Add a displacement to the set of those considered valid by the */ /* collector. GC_register_displacement(n) means that if p was returned */ /* by GC_malloc, then (char *)p + n will be considered to be a valid */ @@ -872,9 +876,8 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ #endif /* THREADS && !SRC_M3 */ -#if defined(GC_WIN32_THREADS) +#if defined(GC_WIN32_THREADS) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) # include -# include /* * All threads must be created using GC_CreateThread, so that they will be @@ -905,7 +908,7 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ # endif # endif /* defined(_WIN32_WCE) */ -#endif /* defined(GC_WIN32_THREADS) */ +#endif /* defined(GC_WIN32_THREADS) && !cygwin */ /* * If you are planning on putting @@ -924,7 +927,11 @@ extern void GC_thr_init(); /* Needed for Solaris/X86 */ */ # define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } # else +# if defined(__APPLE__) && defined(__MACH__) +# define GC_INIT() { GC_init(); } +# else # define GC_INIT() +# endif # endif #endif diff --git a/include/gc_config_macros.h b/include/gc_config_macros.h index 01de9c5..0c836d8 100644 --- a/include/gc_config_macros.h +++ b/include/gc_config_macros.h @@ -19,6 +19,9 @@ # define GC_DGUX386_THREADS # endif #endif +#if defined(AIX_THREADS) +# define GC_AIX_THREADS +#endif #if defined(HPUX_THREADS) # define GC_HPUX_THREADS #endif @@ -38,6 +41,7 @@ #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 */ @@ -51,7 +55,8 @@ # 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 @@ -79,7 +84,7 @@ # 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__) diff --git a/include/gc_pthread_redirects.h b/include/gc_pthread_redirects.h index 47284fb..99a3e8d 100644 --- a/include/gc_pthread_redirects.h +++ b/include/gc_pthread_redirects.h @@ -52,15 +52,21 @@ 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 */ diff --git a/include/private/darwin_semaphore.h b/include/private/darwin_semaphore.h new file mode 100644 index 0000000..0f43982 --- /dev/null +++ b/include/private/darwin_semaphore.h @@ -0,0 +1,68 @@ +#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 diff --git a/include/private/darwin_stop_world.h b/include/private/darwin_stop_world.h new file mode 100644 index 0000000..9924297 --- /dev/null +++ b/include/private/darwin_stop_world.h @@ -0,0 +1,15 @@ +#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 +#include + +struct thread_stop_info { + mach_port_t mach_thread; +}; + +#endif diff --git a/include/private/gc_locks.h b/include/private/gc_locks.h index 61397b1..2507605 100644 --- a/include/private/gc_locks.h +++ b/include/private/gc_locks.h @@ -153,10 +153,11 @@ "\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 @@ -250,17 +251,50 @@ # 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 +# 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 */ @@ -338,6 +372,37 @@ __asm__ __volatile__("" : : : "memory"); } # endif /* I386 */ + +# if defined(POWERPC) +# if !defined(GENERIC_COMPARE_AND_SWAP) + /* Returns TRUE if the comparison succeeded. */ + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + int result, dummy; + __asm__ __volatile__( + "1:\tlwarx %0,0,%5\n" + "\tcmpw %0,%4\n" + "\tbne 2f\n" + "\tstwcx. %3,0,%2\n" + "\tbne- 1b\n" + "\tsync\n" + "\tli %1, 1\n" + "\tb 3f\n" + "2:\tli %1, 0\n" + "3:\t\n" + : "=&r" (dummy), "=r" (result), "=p" (addr) + : "r" (new_val), "r" (old), "2"(addr) + : "cr0","memory"); + return (GC_bool) result; + } +# endif /* !GENERIC_COMPARE_AND_SWAP */ + inline static void GC_memory_barrier() + { + __asm__ __volatile__("sync" : : : "memory"); + } +# endif /* POWERPC */ + # if defined(IA64) # if !defined(GENERIC_COMPARE_AND_SWAP) inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, @@ -489,8 +554,12 @@ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ pthread_mutex_unlock(&GC_allocate_ml); } # else /* !GC_ASSERTIONS */ +# if defined(NO_PTHREAD_TRYLOCK) +# define LOCK() GC_lock(); +# else /* !defined(NO_PTHREAD_TRYLOCK) */ # define LOCK() \ { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); } +# endif # define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) # endif /* !GC_ASSERTIONS */ # endif /* USE_PTHREAD_LOCKS */ @@ -512,7 +581,7 @@ /* on Irix anymore. */ # include - 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. */ diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 3822555..49db446 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -42,8 +42,8 @@ # include #endif /* BSD_TIME */ -# ifndef GC_H -# include "gc.h" +# ifndef _GC_H +# include "../gc.h" # endif # ifndef GC_MARK_H @@ -352,7 +352,8 @@ void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES])); # include # define BCOPY_EXISTS # endif -# if defined(MACOSX) +# if defined(DARWIN) +# include # define BCOPY_EXISTS # endif @@ -1358,6 +1359,11 @@ extern void (*GC_start_call_back) GC_PROTO((void)); # else void GC_push_regs GC_PROTO((void)); # endif +# if defined(SPARC) || defined(IA64) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + word GC_save_regs_in_stack GC_PROTO((void)); +# endif /* Push register contents onto mark stack. */ /* If NURSERY is defined, the default push */ /* action can be overridden with GC_push_proc */ @@ -1407,6 +1413,7 @@ void GC_set_fl_marks GC_PROTO((ptr_t p)); /* Set all mark bits associated with */ /* a free list. */ void GC_add_roots_inner GC_PROTO((char * b, char * e, GC_bool tmp)); +void GC_remove_roots_inner GC_PROTO((char * b, char * e)); GC_bool GC_is_static_root GC_PROTO((ptr_t p)); /* Is the address p in one of the registered static */ /* root sections? */ @@ -1622,6 +1629,8 @@ ptr_t GC_allocobj GC_PROTO((word sz, int kind)); /* Make the indicated */ /* free list nonempty, and return its */ /* head. */ + +void GC_free_inner(GC_PTR p); void GC_init_headers GC_PROTO((void)); struct hblkhdr * GC_install_header GC_PROTO((struct hblk *h)); diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index 35d47fd..290055b 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -39,7 +39,9 @@ /* First a unified test for Linux: */ # if defined(linux) || defined(__linux__) +# ifndef LINUX # define LINUX +# endif # endif /* And one for NetBSD: */ @@ -226,7 +228,7 @@ # 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 @@ -272,12 +274,12 @@ # 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 @@ -456,6 +458,9 @@ /* SH ==> Hitachi SuperH */ /* (LINUX & MSWINCE) */ /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, NOSYS variants) */ /* @@ -702,8 +707,13 @@ # 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. */ @@ -713,24 +723,21 @@ 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 # define GETPAGESIZE() getpagesize() # if defined(USE_PPC_PREFETCH) && defined(__GNUC__) @@ -740,6 +747,9 @@ # 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 @@ -1086,8 +1096,12 @@ /* DATAEND = _data_end__ */ /* To get it right for both, we take the */ /* minumum/maximum of the two. */ +# ifndef MAX # define MAX(x,y) ((x) > (y) ? (x) : (y)) +# endif +# ifndef MIN # define MIN(x,y) ((x) < (y) ? (x) : (y)) +# endif # define DATASTART ((ptr_t) MIN(_data_start__, _bss_start__)) # define DATAEND ((ptr_t) MAX(_data_end__, _bss_end__)) # undef STACK_GRAN @@ -1280,7 +1294,8 @@ /* heap sections so they're not */ /* considered as roots. */ # define OS_TYPE "IRIX5" -# define MPROTECT_VDB +/*# define MPROTECT_VDB DOB: this should work, but there is evidence */ +/* of recent breakage. */ # ifdef _MIPS_SZPTR # define CPP_WORDSZ _MIPS_SZPTR # define ALIGNMENT (_MIPS_SZPTR/8) @@ -1319,15 +1334,28 @@ # 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)) @@ -1821,8 +1849,9 @@ # 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 @@ -1915,6 +1944,9 @@ # 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 @@ -1925,7 +1957,7 @@ # 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. */ diff --git a/include/private/pthread_stop_world.h b/include/private/pthread_stop_world.h new file mode 100644 index 0000000..054c7a0 --- /dev/null +++ b/include/private/pthread_stop_world.h @@ -0,0 +1,12 @@ +#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 diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h new file mode 100644 index 0000000..0ef917e --- /dev/null +++ b/include/private/pthread_support.h @@ -0,0 +1,97 @@ +#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 */ diff --git a/mach_dep.c b/mach_dep.c index 4a66d5d..3dc5f0b 100644 --- a/mach_dep.c +++ b/mach_dep.c @@ -74,7 +74,8 @@ asm static void PushMacRegisters() /* on your architecture. Run the test_setjmp program to see whether */ /* there is any chance it will work. */ -#ifndef USE_GENERIC_PUSH_REGS +#if !defined(USE_GENERIC_PUSH_REGS) && !defined(USE_ASM_PUSH_REGS) +#undef HAVE_PUSH_REGS void GC_push_regs() { # ifdef RT @@ -91,6 +92,7 @@ void GC_push_regs() asm("pushl r8"); asm("calls $1,_GC_push_one"); asm("pushl r7"); asm("calls $1,_GC_push_one"); asm("pushl r6"); asm("calls $1,_GC_push_one"); +# define HAVE_PUSH_REGS # endif # if defined(M68K) && (defined(SUNOS4) || defined(NEXT)) /* M68K SUNOS - could be replaced by generic code */ @@ -113,6 +115,7 @@ void GC_push_regs() asm("movl d7,sp@"); asm("jbsr _GC_push_one"); asm("addqw #0x4,sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif # if defined(M68K) && defined(HP) @@ -135,6 +138,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif /* M68K HP */ # if defined(M68K) && defined(AMIGA) @@ -158,6 +162,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # else /* !__GNUC__ */ GC_push_one(getreg(REG_A2)); GC_push_one(getreg(REG_A3)); @@ -174,6 +179,7 @@ void GC_push_regs() GC_push_one(getreg(REG_D5)); GC_push_one(getreg(REG_D6)); GC_push_one(getreg(REG_D7)); +# define HAVE_PUSH_REGS # endif /* !__GNUC__ */ # endif /* AMIGA */ @@ -196,10 +202,12 @@ void GC_push_regs() PushMacReg(d7); add.w #4,sp ; fix stack. } +# define HAVE_PUSH_REGS # undef PushMacReg # endif /* THINK_C */ # if defined(__MWERKS__) PushMacRegisters(); +# define HAVE_PUSH_REGS # endif /* __MWERKS__ */ # endif /* MACOS */ @@ -222,6 +230,7 @@ void GC_push_regs() asm("pushl %esi"); asm("call _GC_push_one"); asm("addl $4,%esp"); asm("pushl %edi"); asm("call _GC_push_one"); asm("addl $4,%esp"); asm("pushl %ebx"); asm("call _GC_push_one"); asm("addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \ @@ -244,6 +253,7 @@ void GC_push_regs() asm("pushl %esi; call GC_push_one; addl $4,%esp"); asm("pushl %edi; call GC_push_one; addl $4,%esp"); asm("pushl %ebx; call GC_push_one; addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if ( defined(I386) && defined(BEOS) && defined(__ELF__) ) @@ -255,6 +265,7 @@ void GC_push_regs() asm("pushl %esi; call GC_push_one; addl $4,%esp"); asm("pushl %edi; call GC_push_one; addl $4,%esp"); asm("pushl %ebx; call GC_push_one; addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # if defined(I386) && defined(MSWIN32) && !defined(__MINGW32__) \ @@ -281,6 +292,7 @@ void GC_push_regs() __asm push edi __asm call GC_push_one __asm add esp,4 +# define HAVE_PUSH_REGS # endif # if defined(I386) && (defined(SVR4) || defined(SCO) || defined(SCO_ELF)) @@ -292,6 +304,7 @@ void GC_push_regs() asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp"); asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp"); +# define HAVE_PUSH_REGS # endif # ifdef NS32K @@ -300,14 +313,12 @@ void GC_push_regs() asm ("movd r5, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); asm ("movd r6, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); asm ("movd r7, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); +# define HAVE_PUSH_REGS # endif # if defined(SPARC) - { - word GC_save_regs_in_stack(); - - GC_save_regs_ret_val = GC_save_regs_in_stack(); - } + GC_save_regs_ret_val = GC_save_regs_in_stack(); +# define HAVE_PUSH_REGS # endif # ifdef RT @@ -323,6 +334,7 @@ void GC_push_regs() asm("cas r11, r13, r0"); GC_push_one(TMP_SP); /* through */ asm("cas r11, r14, r0"); GC_push_one(TMP_SP); /* r15 */ asm("cas r11, r15, r0"); GC_push_one(TMP_SP); +# define HAVE_PUSH_REGS # endif # if defined(M68K) && defined(SYSV) @@ -346,6 +358,7 @@ void GC_push_regs() asm("movl %d7,%sp@"); asm("jbsr GC_push_one"); asm("addqw #0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # else /* !__GNUC__*/ asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ @@ -363,6 +376,7 @@ void GC_push_regs() asm("mov.l %d7,(%sp)"); asm("jsr GC_push_one"); asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# define HAVE_PUSH_REGS # endif /* !__GNUC__ */ # endif /* M68K/SYSV */ @@ -372,21 +386,19 @@ void GC_push_regs() extern int *__libc_stack_end; GC_push_all_stack (sp, __libc_stack_end); +# define HAVE_PUSH_REGS + /* Isn't this redundant with the code to push the stack? */ } # endif /* other machines... */ -# if !defined(M68K) && !defined(VAX) && !defined(RT) -# if !defined(SPARC) && !defined(I386) && !defined(NS32K) -# if !defined(POWERPC) && !defined(UTS4) -# if !defined(PJ) && !(defined(MIPS) && defined(LINUX)) - --> bad news <-- -# endif -# endif -# endif +# if !defined(HAVE_PUSH_REGS) + --> We just generated an empty GC_push_regs, which + --> is almost certainly broken. Try defining + --> USE_GENERIC_PUSH_REGS instead. # endif } -#endif /* !USE_GENERIC_PUSH_REGS */ +#endif /* !USE_GENERIC_PUSH_REGS && !USE_ASM_PUSH_REGS */ #if defined(USE_GENERIC_PUSH_REGS) void GC_generic_push_regs(cold_gc_frame) @@ -428,8 +440,6 @@ ptr_t cold_gc_frame; /* needed on IA64, since some non-windowed registers are */ /* preserved. */ { - word GC_save_regs_in_stack(); - GC_save_regs_ret_val = GC_save_regs_in_stack(); /* On IA64 gcc, could use __builtin_ia64_flushrs() and */ /* __builtin_ia64_flushrs(). The latter will be done */ @@ -446,7 +456,7 @@ ptr_t cold_gc_frame; /* the stack. Return sp. */ # ifdef SPARC asm(" .seg \"text\""); -# ifdef SVR4 +# if defined(SVR4) || defined(NETBSD) asm(" .globl GC_save_regs_in_stack"); asm("GC_save_regs_in_stack:"); asm(" .type GC_save_regs_in_stack,#function"); diff --git a/mark_rts.c b/mark_rts.c index 2aae516..55eb5d5 100644 --- a/mark_rts.c +++ b/mark_rts.c @@ -275,33 +275,72 @@ void GC_clear_roots GC_PROTO((void)) } /* Internal use only; lock held. */ +static void GC_remove_root_at_pos(i) +int i; +{ + GC_root_size -= (GC_static_roots[i].r_end - GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; +} + +#if !defined(MSWIN32) && !defined(MSWINCE) +static void GC_rebuild_root_index() +{ + register int i; + + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); +} +#endif + +/* Internal use only; lock held. */ void GC_remove_tmp_roots() { register int i; for (i = 0; i < n_root_sets; ) { if (GC_static_roots[i].r_tmp) { - GC_root_size -= - (GC_static_roots[i].r_end - GC_static_roots[i].r_start); - GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; - GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; - GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; - n_root_sets--; + GC_remove_root_at_pos(i); } else { i++; - } } -# if !defined(MSWIN32) && !defined(MSWINCE) - { - register int i; - - for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; - for (i = 0; i < n_root_sets; i++) - add_roots_to_index(GC_static_roots + i); } -# endif + #if !defined(MSWIN32) && !defined(MSWINCE) + GC_rebuild_root_index(); + #endif +} + +#if !defined(MSWIN32) && !defined(MSWINCE) +void GC_remove_roots(b, e) +char * b; char * e; +{ + DCL_LOCK_STATE; + DISABLE_SIGNALS(); + LOCK(); + GC_remove_roots_inner(b, e); + UNLOCK(); + ENABLE_SIGNALS(); +} + +/* Should only be called when the lock is held */ +void GC_remove_roots_inner(b,e) +char * b; char * e; +{ + int i; + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_start >= (ptr_t)b && GC_static_roots[i].r_end <= (ptr_t)e) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } + GC_rebuild_root_index(); } +#endif /* !defined(MSWIN32) && !defined(MSWINCE) */ #if defined(MSWIN32) || defined(_WIN32_WCE_EMULATION) /* Workaround for the OS mapping and unmapping behind our back: */ diff --git a/misc.c b/misc.c index 60857f5..814fa41 100644 --- a/misc.c +++ b/misc.c @@ -487,6 +487,15 @@ void GC_init() GC_init_parallel(); } # endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ + +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + { + /* This must be called WITHOUT the allocation lock held + and before any threads are created */ + extern void GC_init_dyld(); + GC_init_dyld(); + } +# endif } #if defined(MSWIN32) || defined(MSWINCE) diff --git a/os_dep.c b/os_dep.c index 668cd26..e3e7e25 100644 --- a/os_dep.c +++ b/os_dep.c @@ -132,6 +132,11 @@ # define jmp_buf sigjmp_buf #endif +#ifdef DARWIN +/* for get_etext and friends */ +#include +#endif + #ifdef DJGPP /* Apparently necessary for djgpp 2.01. May cause problems with */ /* other versions. */ @@ -272,10 +277,11 @@ char *GC_parse_map_entry(char *buf_ptr, word *start, word *end, 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'; @@ -864,7 +870,7 @@ ptr_t GC_get_stack_base() && 0 != __libc_ia64_register_backing_store_base) { /* Glibc 2.2.4 has a bug such that for dynamically linked */ /* executables __libc_ia64_register_backing_store_base is */ - /* defined but ininitialized during constructor calls. */ + /* defined but uninitialized during constructor calls. */ /* Hence we check for both nonzero address and value. */ return __libc_ia64_register_backing_store_base; } else { @@ -961,8 +967,11 @@ ptr_t GC_get_stack_base() ptr_t GC_get_stack_base() { +# if defined(HEURISTIC1) || defined(HEURISTIC2) || \ + defined(LINUX_STACKBOTTOM) || defined(FREEBSD_STACKBOTTOM) word dummy; ptr_t result; +# endif # define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) @@ -2007,7 +2016,6 @@ void (*GC_push_other_roots) GC_PROTO((void)) = GC_default_push_other_roots; * make sure that other system calls are similarly protected * or write only to the stack. */ - GC_bool GC_dirty_maintained = FALSE; # ifdef DEFAULT_VDB @@ -2021,6 +2029,9 @@ GC_bool GC_dirty_maintained = FALSE; /* Initialize virtual dirty bit implementation. */ void GC_dirty_init() { +# ifdef PRINTSTATS + GC_printf0("Initializing DEFAULT_VDB...\n"); +# endif GC_dirty_maintained = TRUE; } @@ -2103,7 +2114,7 @@ GC_bool is_ptrfree; * objects only if they are the same. */ -# if !defined(MSWIN32) && !defined(MSWINCE) +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN) # include # include @@ -2122,6 +2133,23 @@ GC_bool is_ptrfree; # else +# ifdef DARWIN + /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to + decrease the likelihood of some of the problems described below. */ + #include + 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 # endif @@ -2139,20 +2167,22 @@ GC_bool is_ptrfree; &protect_junk)) { \ ABORT("un-VirtualProtect failed"); \ } - -# endif +# endif /* !DARWIN */ +# endif /* MSWIN32 || MSWINCE || DARWIN */ #if defined(SUNOS4) || defined(FREEBSD) typedef void (* SIG_PF)(); -#endif +#endif /* SUNOS4 || FREEBSD */ + #if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) \ - || defined(MACOSX) || defined(HURD) + || defined(HURD) # ifdef __STDC__ typedef void (* SIG_PF)(int); # else typedef void (* SIG_PF)(); # endif -#endif +#endif /* SUNOS5SIGS || OSF1 || LINUX || HURD */ + #if defined(MSWIN32) typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF; # undef SIG_DFL @@ -2166,7 +2196,8 @@ GC_bool is_ptrfree; #if defined(IRIX5) || defined(OSF1) || defined(HURD) typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *); -#endif +#endif /* IRIX5 || OSF1 || HURD */ + #if defined(SUNOS5SIGS) # ifdef HPUX # define SIGINFO __siginfo @@ -2178,7 +2209,8 @@ GC_bool is_ptrfree; # else typedef void (* REAL_SIG_PF)(); # endif -#endif +#endif /* SUNOS5SIGS */ + #if defined(LINUX) # if __GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 typedef struct sigcontext s_c; @@ -2212,139 +2244,14 @@ GC_bool is_ptrfree; return (char *)faultaddr; } # endif /* !ALPHA */ -# endif - -# if defined(MACOSX) /* Should also test for PowerPC? */ - typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *); - -/* Decodes the machine instruction which was responsible for the sending of the - SIGBUS signal. Sadly this is the only way to find the faulting address because - the signal handler doesn't get it directly from the kernel (although it is - available on the Mach level, but droppped by the BSD personality before it - calls our signal handler...) - This code should be able to deal correctly with all PPCs starting from the - 601 up to and including the G4s (including Velocity Engine). */ -#define EXTRACT_OP1(iw) (((iw) & 0xFC000000) >> 26) -#define EXTRACT_OP2(iw) (((iw) & 0x000007FE) >> 1) -#define EXTRACT_REGA(iw) (((iw) & 0x001F0000) >> 16) -#define EXTRACT_REGB(iw) (((iw) & 0x03E00000) >> 21) -#define EXTRACT_REGC(iw) (((iw) & 0x0000F800) >> 11) -#define EXTRACT_DISP(iw) ((short *) &(iw))[1] - -static char *get_fault_addr(struct sigcontext *scp) -{ - unsigned int instr = *((unsigned int *) scp->sc_ir); - unsigned int * regs = &((unsigned int *) scp->sc_regs)[2]; - int disp = 0, tmp; - unsigned int baseA = 0, baseB = 0; - unsigned int addr, alignmask = 0xFFFFFFFF; - -#ifdef GC_DEBUG_DECODER - GC_err_printf1("Instruction: 0x%lx\n", instr); - GC_err_printf1("Opcode 1: d\n", (int)EXTRACT_OP1(instr)); -#endif - switch(EXTRACT_OP1(instr)) { - case 38: /* stb */ - case 39: /* stbu */ - case 54: /* stfd */ - case 55: /* stfdu */ - case 52: /* stfs */ - case 53: /* stfsu */ - case 44: /* sth */ - case 45: /* sthu */ - case 47: /* stmw */ - case 36: /* stw */ - case 37: /* stwu */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - disp = EXTRACT_DISP(instr); - break; - case 31: -#ifdef GC_DEBUG_DECODER - GC_err_printf1("Opcode 2: %d\n", (int)EXTRACT_OP2(instr)); -#endif - switch(EXTRACT_OP2(instr)) { - case 86: /* dcbf */ - case 54: /* dcbst */ - case 1014: /* dcbz */ - case 247: /* stbux */ - case 215: /* stbx */ - case 759: /* stfdux */ - case 727: /* stfdx */ - case 983: /* stfiwx */ - case 695: /* stfsux */ - case 663: /* stfsx */ - case 918: /* sthbrx */ - case 439: /* sthux */ - case 407: /* sthx */ - case 661: /* stswx */ - case 662: /* stwbrx */ - case 150: /* stwcx. */ - case 183: /* stwux */ - case 151: /* stwx */ - case 135: /* stvebx */ - case 167: /* stvehx */ - case 199: /* stvewx */ - case 231: /* stvx */ - case 487: /* stvxl */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - baseB = regs[EXTRACT_REGC(instr)]; - /* determine Altivec alignment mask */ - switch(EXTRACT_OP2(instr)) { - case 167: /* stvehx */ - alignmask = 0xFFFFFFFE; - break; - case 199: /* stvewx */ - alignmask = 0xFFFFFFFC; - break; - case 231: /* stvx */ - alignmask = 0xFFFFFFF0; - break; - case 487: /* stvxl */ - alignmask = 0xFFFFFFF0; - break; - } - break; - case 725: /* stswi */ - tmp = EXTRACT_REGA(instr); - if(tmp > 0) - baseA = regs[tmp]; - break; - default: /* ignore instruction */ -#ifdef GC_DEBUG_DECODER - GC_err_printf("Ignored by inner handler\n"); -#endif - return NULL; - break; - } - break; - default: /* ignore instruction */ -#ifdef GC_DEBUG_DECODER - GC_err_printf("Ignored by main handler\n"); -#endif - return NULL; - break; - } - - addr = (baseA + baseB) + disp; - addr &= alignmask; -#ifdef GC_DEBUG_DECODER - GC_err_printf1("BaseA: %d\n", baseA); - GC_err_printf1("BaseB: %d\n", baseB); - GC_err_printf1("Disp: %d\n", disp); - GC_err_printf1("Address: %d\n", addr); -#endif - return (char *)addr; -} -#endif /* MACOSX */ +# endif /* LINUX */ +#ifndef DARWIN SIG_PF GC_old_bus_handler; SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ +#endif /* !DARWIN */ -#ifdef THREADS +#if defined(THREADS) /* We need to lock around the bitmap update in the write fault handler */ /* in order to avoid the risk of losing a bit. We do this with a */ /* test-and-set spin lock if we know how to do that. Otherwise we */ @@ -2393,6 +2300,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ #endif /* !THREADS */ /*ARGSUSED*/ +#if !defined(DARWIN) # if defined (SUNOS4) || defined(FREEBSD) void GC_write_fault_handler(sig, code, scp, addr) int sig, code; @@ -2408,7 +2316,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGBUS) # define CODE_OK (code == BUS_PAGE_FAULT) # endif -# endif +# endif /* SUNOS4 || FREEBSD */ + # if defined(IRIX5) || defined(OSF1) || defined(HURD) # include void GC_write_fault_handler(int sig, int code, struct sigcontext *scp) @@ -2424,7 +2333,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGBUS || sig == SIGSEGV) # define CODE_OK TRUE # endif -# endif +# endif /* IRIX5 || OSF1 || HURD */ + # if defined(LINUX) # if defined(ALPHA) || defined(M68K) void GC_write_fault_handler(int sig, int code, s_c * sc) @@ -2444,7 +2354,8 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ /* Empirically c.trapno == 14, on IA32, but is that useful? */ /* Should probably consider alignment issues on other */ /* architectures. */ -# endif +# endif /* LINUX */ + # if defined(SUNOS5SIGS) # ifdef __STDC__ void GC_write_fault_handler(int sig, struct SIGINFO *scp, void * context) @@ -2465,13 +2376,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # define SIG_OK (sig == SIGSEGV) # define CODE_OK (scp -> si_code == SEGV_ACCERR) # endif -# endif - -# if defined(MACOSX) - void GC_write_fault_handler(int sig, int code, struct sigcontext *scp) -# define SIG_OK (sig == SIGBUS) -# define CODE_OK (code == 0 /* experimentally determined */) -# endif +# endif /* SUNOS5SIGS */ # if defined(MSWIN32) || defined(MSWINCE) LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info) @@ -2479,7 +2384,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ STATUS_ACCESS_VIOLATION) # define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1) /* Write fault */ -# endif +# endif /* MSWIN32 || MSWINCE */ { register unsigned i; # if defined(HURD) @@ -2550,9 +2455,6 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ # 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]); @@ -2616,9 +2518,6 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ (*(REAL_SIG_PF)old_handler) (sig, code, scp); return; # endif -# ifdef MACOSX - (*(REAL_SIG_PF)old_handler) (sig, code, scp); -# endif # ifdef MSWIN32 return((*old_handler)(exc_info)); # endif @@ -2660,6 +2559,7 @@ SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ ABORT("Unexpected bus error or segmentation fault"); #endif } +#endif /* !DARWIN */ /* * We hold the allocation lock. We expect block h to be written @@ -2692,6 +2592,7 @@ GC_bool is_ptrfree; UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); } +#if !defined(DARWIN) void GC_dirty_init() { # if defined(SUNOS5SIGS) || defined(IRIX5) || defined(LINUX) || \ @@ -2714,13 +2615,6 @@ void GC_dirty_init() (void)sigaddset(&act.sa_mask, SIG_SUSPEND); # endif /* SIG_SUSPEND */ # endif -# if defined(MACOSX) - struct sigaction act, oldact; - - act.sa_flags = SA_RESTART; - act.sa_handler = GC_write_fault_handler; - sigemptyset(&act.sa_mask); -# endif # ifdef PRINTSTATS GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n"); # endif @@ -2786,7 +2680,7 @@ void GC_dirty_init() # endif } # endif -# if defined(MACOSX) || defined(HPUX) || defined(LINUX) || defined(HURD) +# if defined(HPUX) || defined(LINUX) || defined(HURD) sigaction(SIGBUS, &act, &oldact); GC_old_bus_handler = oldact.sa_handler; if (GC_old_bus_handler == SIG_IGN) { @@ -2798,7 +2692,7 @@ void GC_dirty_init() GC_err_printf0("Replaced other SIGBUS handler\n"); # endif } -# endif /* MACOS || HPUX || LINUX */ +# endif /* HPUX || LINUX || HURD */ # if defined(MSWIN32) GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler); if (GC_old_segv_handler != NULL) { @@ -2810,6 +2704,7 @@ void GC_dirty_init() } # endif } +#endif /* !DARWIN */ int GC_incremental_protection_needs() { @@ -3377,6 +3272,553 @@ GC_bool is_ptrfree; # endif /* PCR_VDB */ +#if defined(MPROTECT_VDB) && defined(DARWIN) +/* The following sources were used as a *reference* for this exception handling + code: + 1. Apple's mach/xnu documentation + 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the + omnigroup's macosx-dev list. + www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html + 3. macosx-nat.c from Apple's GDB source code. +*/ + +/* 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 +#include +#include +#include +#include +#include + +/* 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 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() { @@ -3496,8 +3938,6 @@ struct callinfo info[NFRAMES]; asm("movl %%ebp,%0" : "=r"(frame)); fp = frame; # else - word GC_save_regs_in_stack(); - frame = (struct frame *) GC_save_regs_in_stack (); fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS); #endif diff --git a/powerpc_darwin_mach_dep.s b/powerpc_darwin_mach_dep.s new file mode 100644 index 0000000..92f2c93 --- /dev/null +++ b/powerpc_darwin_mach_dep.s @@ -0,0 +1,84 @@ + +; 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 diff --git a/powerpc_macosx_mach_dep.s b/powerpc_macosx_mach_dep.s deleted file mode 100755 index 92f0628..0000000 --- a/powerpc_macosx_mach_dep.s +++ /dev/null @@ -1,95 +0,0 @@ - -.text - - .set linkageArea,24 - .set params,4 - .set alignment,4 - - .set spaceToSave,linkageArea+params+alignment - .set spaceToSave8,spaceToSave+8 - -; Mark from machine registers that are saved by C compiler - .globl _GC_push_regs -_GC_push_regs: - ; PROLOG - mflr r0 ; get return address - stw r0,8(r1) ; save return address - stwu r1,-spaceToSave(r1) ; skip over caller save area - ; - mr r3,r2 ; mark from r2. Well Im not really sure - ; that this is necessary or even the right - ; thing to do - at least it doesnt harm... - ; According to Apples docs it points to - ; the direct data area, whatever that is... - bl L_GC_push_one$stub - mr r3,r13 ; mark from r13-r31 - bl L_GC_push_one$stub - mr r3,r14 - bl L_GC_push_one$stub - mr r3,r15 - bl L_GC_push_one$stub - mr r3,r16 - bl L_GC_push_one$stub - mr r3,r17 - bl L_GC_push_one$stub - mr r3,r18 - bl L_GC_push_one$stub - mr r3,r19 - bl L_GC_push_one$stub - mr r3,r20 - bl L_GC_push_one$stub - mr r3,r21 - bl L_GC_push_one$stub - mr r3,r22 - bl L_GC_push_one$stub - mr r3,r23 - bl L_GC_push_one$stub - mr r3,r24 - bl L_GC_push_one$stub - mr r3,r25 - bl L_GC_push_one$stub - mr r3,r26 - bl L_GC_push_one$stub - mr r3,r27 - bl L_GC_push_one$stub - mr r3,r28 - bl L_GC_push_one$stub - mr r3,r29 - bl L_GC_push_one$stub - mr r3,r30 - bl L_GC_push_one$stub - mr r3,r31 - bl L_GC_push_one$stub - ; EPILOG - lwz r0,spaceToSave8(r1) ; get return address back - mtlr r0 ; reset link register - addic r1,r1,spaceToSave ; restore stack pointer - blr - -.data -.picsymbol_stub -L_GC_push_one$stub: - .indirect_symbol _GC_push_one - mflr r0 - bcl 20,31,L0$_GC_push_one -L0$_GC_push_one: - mflr r11 - addis r11,r11,ha16(L_GC_push_one$lazy_ptr-L0$_GC_push_one) - mtlr r0 - lwz r12,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one)(r11) - mtctr r12 - addi r11,r11,lo16(L_GC_push_one$lazy_ptr-L0$_GC_push_one) - bctr -.data -.lazy_symbol_pointer -L_GC_push_one$lazy_ptr: - .indirect_symbol _GC_push_one - .long dyld_stub_binding_helper -.non_lazy_symbol_pointer -L_GC_push_one$non_lazy_ptr: - .indirect_symbol _GC_push_one - .long 0 - - - - diff --git a/pthread_stop_world.c b/pthread_stop_world.c new file mode 100644 index 0000000..0a75c6c --- /dev/null +++ b/pthread_stop_world.c @@ -0,0 +1,445 @@ +#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 +#include +#include +#include + +#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 diff --git a/linux_threads.c b/pthread_support.c similarity index 70% rename from linux_threads.c rename to pthread_support.c index ab5820a..a5a87ad 100644 --- a/linux_threads.c +++ b/pthread_support.c @@ -47,24 +47,22 @@ * + # 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 @@ -101,15 +99,21 @@ # include # include # include -# include -# include # include # include # include -#if defined(GC_MACOSX_THREADS) +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_semaphore.h" +#else +# include +#endif /* !GC_DARWIN_THREADS */ + +#if defined(GC_DARWIN_THREADS) # include -#endif /* GC_MACOSX_THREADS */ +#endif /* GC_DARWIN_THREADS */ + + #if defined(GC_DGUX386_THREADS) # include @@ -133,127 +137,15 @@ # 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(); @@ -366,7 +258,9 @@ GC_PTR GC_local_malloc(size_t bytes) 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) @@ -413,13 +307,14 @@ GC_PTR GC_local_malloc_atomic(size_t bytes) ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key)) -> ptrfree_freelists + index; ptr_t my_entry = *my_fl; + if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { GC_PTR result = (GC_PTR)my_entry; *my_fl = obj_link(my_entry); return result; } else if ((word)my_entry - 1 < DIRECT_GRANULES) { *my_fl = my_entry + index + 1; - return GC_malloc_atomic(bytes); + 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; */ @@ -491,40 +386,6 @@ GC_PTR GC_local_gcj_malloc(size_t bytes, # 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 -# include -# include - - 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 @@ -539,10 +400,6 @@ actually work for something else. void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np; #endif /* 0 */ -#if defined(SPARC) || defined(IA64) - extern word GC_save_regs_in_stack(); -#endif - long GC_nprocs = 1; /* Number of processors. We may not have */ /* access to all of them, but this is as good */ /* a guess as any ... */ @@ -639,117 +496,6 @@ static __inline__ void start_mark_threads() #endif /* !PARALLEL_MARK */ -void GC_suspend_handler(int sig) -{ - int dummy; - pthread_t my_thread = pthread_self(); - GC_thread me; - sigset_t all_sigs; - sigset_t old_sigs; - int i; - sigset_t mask; -# ifdef PARALLEL_MARK - word my_mark_no = GC_mark_no; - /* Marker can't proceed until we acknowledge. Thus this is */ - /* guaranteed to be the mark_no correspending to our */ - /* suspension, i.e. the marker can't have incremented it yet. */ -# endif - 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 */ @@ -765,7 +511,6 @@ void GC_looping_handler(int sig) 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)) @@ -920,230 +665,6 @@ void GC_remove_all_threads_but_me(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) { @@ -1183,7 +704,6 @@ int GC_get_nprocs() /* 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 */ @@ -1212,6 +732,7 @@ int GC_get_nprocs() /* 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()) { @@ -1315,45 +836,14 @@ int GC_get_nprocs() /* 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, @@ -1361,21 +851,14 @@ void GC_thr_init() # 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. */ { @@ -1394,7 +877,7 @@ void GC_thr_init() # 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); @@ -1454,7 +937,8 @@ void GC_init_parallel() { 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 @@ -1469,6 +953,7 @@ void GC_init_parallel() } +#if !defined(GC_DARWIN_THREADS) int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset) { sigset_t fudged_set; @@ -1480,6 +965,7 @@ int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset) } 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. */ @@ -1493,25 +979,29 @@ void GC_start_blocking(void) { 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()); @@ -1539,12 +1029,8 @@ struct start_info { 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. */ @@ -1643,8 +1129,12 @@ void * GC_start_routine(void * arg) # 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 */ @@ -1652,12 +1142,14 @@ void * GC_start_routine(void * arg) # 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. */ @@ -1673,11 +1165,7 @@ void * GC_start_routine(void * arg) 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) @@ -1704,8 +1192,6 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread, 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; @@ -1722,11 +1208,7 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread, 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(); @@ -1761,6 +1243,7 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread, # 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 @@ -1768,15 +1251,10 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread, /* 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(); @@ -1818,7 +1296,9 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread, void GC_pause() { int i; - volatile word dummy = 0; +# ifndef __GNUC__ + volatile word dummy = 0; +# endif for (i = 0; i < 10; ++i) { # ifdef __GNUC__ @@ -1857,6 +1337,7 @@ VOLATILE GC_bool GC_collecting = 0; void GC_generic_lock(pthread_mutex_t * lock) { +#ifndef NO_PTHREAD_TRYLOCK unsigned pause_length = 1; unsigned i; @@ -1874,6 +1355,7 @@ void GC_generic_lock(pthread_mutex_t * lock) ABORT("Unexpected error from pthread_mutex_trylock"); } } +#endif /* !NO_PTHREAD_TRYLOCK */ pthread_mutex_lock(lock); } @@ -1951,14 +1433,17 @@ yield: } #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 */ diff --git a/tests/test.c b/tests/test.c index 43ebe8c..cfe23c0 100644 --- a/tests/test.c +++ b/tests/test.c @@ -68,10 +68,14 @@ # include # endif -# ifdef GC_WIN32_THREADS +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) static CRITICAL_SECTION incr_cs; # endif +#ifdef __STDC__ +# include +#endif + /* Allocation Statistics */ int stubborn_count = 0; @@ -516,13 +520,6 @@ sexpr x; } } -/* Try to force a to be strangely aligned */ -struct { - char dummy; - sexpr aa; -} A; -#define a A.aa - /* * A tiny list reversal test to check thread creation. */ @@ -593,6 +590,13 @@ struct { #endif +/* Try to force a to be strangely aligned */ +struct { + char dummy; + sexpr aa; +} A; +#define a A.aa + /* * Repeatedly reverse lists built out of very different sized cons cells. * Check that we didn't lose anything. @@ -713,6 +717,8 @@ void reverse_test() b = c = 0; } +#undef a + /* * The rest of this builds balanced binary trees, checks that they don't * disappear, and tests finalization. @@ -1165,6 +1171,25 @@ void fail_proc1(GC_PTR x) fail_count++; } +static void uniq(void *p, ...) { + va_list a; + void *q[100]; + int n = 0, i, j; + q[n++] = p; + va_start(a,p); + for (;(q[n] = va_arg(a,void *));n++) ; + va_end(a); + for (i=0; i #ifdef CYGWIN32 # include @@ -369,6 +370,8 @@ void GC_get_next_stack(char *start, char **lo, char **hi) if (*lo < start) *lo = start; } +#if !defined(CYGWIN32) + #if !defined(MSWINCE) && defined(GC_DLL) /* We register threads from DllMain */ @@ -511,6 +514,8 @@ static DWORD WINAPI thread_start(LPVOID arg) } #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ +#endif /* !CYGWIN32 */ + #ifdef MSWINCE typedef struct { -- 2.7.4