+2008-05-03 Hans Boehm <Hans.Boehm@hp.com>
+
+ * doc/gcinterface.html: Improve C++ interface documentation.
+
+2008-03-10 Hans Boehm <Hans.Boehm@hp.com>
+
+ * allchblk.c (GC_allochblk): Check for overflow during size
+ rounding.
+ * tests/huge_test.c: New.
+ * Makefile.direct, tests/tests.am: Add huge_test.c
+ * Makefile.in: Regenerate.
+
+2008-02-29 Hans Boehm <Hans.Boehm@hp.com>
+
+ * pthread_support.c: Fix typo in comment.
+ * os_dep.c (GC_win32_get_mem): Add heap section only if
+ allocation succeeded.
+
+2008-02-28 Hans Boehm <Hans.Boehm@hp.com>
+
+ * malloc.c: (free replacement) Fix caller address space check.
+
+2008-02-25 Hans Boehm <Hans.Boehm@hp.com>
+
+ * finalize.c (GC_grow_table): Dereference table in null-check.
+
+2008-02-24 Hans Boehm <Hans.Boehm@hp.com>
+
+ * win32_threads.c (GC_delete_gc_thread, GC_delete_thread):
+ Consistently call CloseHandle. (GC_suspend): Call
+ GC_delete_gc_thread.
+ * tests/test.c: Don't reference GC_print_stats if not exported.
+
+2008-02-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * tests/test.c (run_one_test): Don't mention pthread_self().
+ * misc.c: Declare GC_thr_init().
+
+2008-02-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * allchblk.c (add_to_fl): disable assertions with USE_MUNMAP,
+ and refine assertions to handle huge unmergable blocks.
+ (GC_allochblk_nth): Add comment.
+
+2008-02-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/private/gcconfig.h: Add misssing FREEBSD macro
+ consistency test.
+
+2008-02-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * allchblk.c (GC_enough_large_bytes_left): No longer take
+ parameters; return free list index bound.
+ (GC_merge_unmapped): Don't access nexthdr until after null test.
+ (Fixes bug in 1/29/08 check-in.) (GC_allochblk): Calculate
+ when splitting is allowable only once here, not when considering each
+ block. (GC_allchblk_nth): Accept new may_split parameter.
+ Avoid some redundant tests for exact size matches.
+ * alloc.c (GC_should_collect): Cache min_bytes_allocd.
+ (GC_maybe_gc): Make locking assertion testable.
+ * mark_rts.c: Fix indentation.
+ * pthread_stop_world.c: Replace old GC_err_printf1 reference.
+ * tests/test.c: Remove (void) casts. Optionally print some
+ timing information.
+
+2008-02-15 Hans Boehm <Hans.Boehm@hp.com>
+
+ * windows-untested/gc.def: Remove CreateThread line.
+ * windows-untested/README: New file.
+ * win32_threads.c (GC_use_DllMain): Force collector initialization.
+ (GC_init_parallel): Reformat comment.
+ * include/gc.h (GC_use_DllMain): Clarify usage rules in comment.
+ * mark.c (GC_mark_from): Slightly simplify GC_DS_PER_OBJECT code.
+ * include/gc_cpp.h: Add matching placement delete overloads
+ everywhere.
+ * include/private/gc_locks.h (NO_THREAD): Add cast.
+ * include/private/gcconfig.h: Add test for __HP_aCC.
+ * configure.ac, tests/tests.am: Avoid libgccpp on HP/UX.
+ * Makefile.in, configure: Regenerate.
+
+2008-02-11 Hans Boehm <Hans.Boehm@hp.com> (partly David Leonard)
+
+ * doc/README.win32: Fix typo.
+ * configure.ac: Fix printing of enable-shared result.
+ * configure: Regenerate.
+
+2008-02-08 Hans Boehm <Hans.Boehm@hp.com>
+
+ * misc.c (GC_init_inner): Assert !GC_need_to_lock only when
+ defined. (GC_call_with_stack_base): Add GC_API.
+ * os_dep.c (GC_get_stack_base): Add GC_API.
+ * win32_threads.c: (GC_register_my_thread, GC_unregister_my_thread):
+ Add GC_API.
+ * include/gc.h: Add GC_API annotations.
+ * include/private/gc_locks.h: Define UNCOND_LOCK etc. also for
+ PCR.
+ * include/private/gc_pmark.h: Fix comments.
+
+2008-02-06 Hans Boehm <Hans.Boehm@hp.com> (mostly from Henning Makholm)
+
+ * include/private/gc_priv.h, mark_rts.c, typd_mlc.c:
+ Add GC_push_typed_structures() to push GC_ext_descriptors.
+
+2008-01-31 Hans Boehm <Hans.Boehm@hp.com> (mostly from Andreas Tobler)
+
+ * tests/test.c: Call GC_INIT for DARWIN; test system type using
+ gcconfig.h-defined macros.
+
+2008-01-29 Hans Boehm <Hans.Boehm@hp.com>
+
+ * allchblk.c (GC_merge_unmapped, GC_freehblk): Refuse to create
+ blocks large enough that their size, when interpreted as a signed
+ value, would be negative.
+ * include/private/gc_priv.h: Comment hb_sz range limit.
+
+2008-01-29 Hans Boehm <Hans.Boehm@hp.com> (with help from Manuel Serrano)
+
+ * mark.c (GC_push_next_marked): correct comment.
+ * Makefile.direct: document NO_PROC_STAT.
+ * include/private/gcconfig.h: Accomodate NO_PROC_STAT.
+
+2008-01-10 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/gc_version.h, configure.ac, doc/README:
+ Change to version 7.1alpha3.
+ * configure: Regenerate.
+
+[7.1alpha2]
+
+2008-01-10 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/gc_version.h, configure.ac, doc/README:
+ Change to version 7.1alpha2.
+ * configure: Regenerate.
+
+2008-01-10 Hans Boehm <Hans.Boehm@hp.com>
+
+ * Makefile.am: Mention atomic_ops.c and atomic_ops_sysdeps.S
+ again. Refer to build directory as ".".
+ * Makefile.in: Regenerate.
+
+2008-01-10 Hans Boehm <Hans.Boehm@hp.com>
+
+ * configure.ac: Ignore --enable-parallel-mark on Darwin for now.
+ * configure: Regenerate.
+ * darwin_stop_world.c: Add FIXME comment for parallel marker.
+
+2008-01-09 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/private/gc_priv.h: Update MAX_ROOT_SETS
+ and LOG_PHT_ENTRIES to handle larger heaps.
+
+2008-01-03 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/gc.h (GC_INIT,GC_init): Update comments.
+
+2008-01-03 Hans Boehm <Hans.Boehm@hp.com> (based on a patch from
+ John Bowman, and an ancient patch from Fergus Henderson)
+
+ * allchblk.c, alloc.c, include/private/gc_priv.h:
+ Track GC_bytes_dropped and use in GC triggering decisions.
+ * alloc.c (min_bytes_allocd): Weight atomic blocks less.
+
+2008-01-02 Hans Boehm <Hans.Boehm@hp.com>
+
+ * alloc.c (GC_add_to_heap): Call GC_install_header(p) AFTER
+ adjusting p.
+
+2007-12-23 Hans Boehm <Hans.Boehm@hp.com>
+
+ * Makefile.am: Add NT_X64_THREADS_MAKEFILE.
+
+2007-12-23 Hans Boehm <Hans.Boehm@hp.com> (Really mostly Friedrich Dominicus)
+
+ * NT_X64_STATIC_THREADS_MAKEFILE: Clean up obsolete comment.
+ * alloc.c: Add declaration for GC_add_current_malloc_heap.
+ * win32_threads.c (GC_beginthreadex): Clean up error
+ return code.
+ * doc/README.win64, NT_X64_THREADS_MAKEFILE, Makefile.direct:
+ Add NT_X64_THREADS_MAKEFILE.
+
+2007-12-21 Hans Boehm <Hans.Boehm@hp.com>
+
+ * alloc.c: Define GC_version instead of in version.h.
+ * version.h: Remove.
+ * include/gc_version.h: Move most of version.h here.
+ * include/gc.h: Include gc_version.h.
+ * gcname.c, add_gc_prefix.c: include gc.h instead of version.h.
+ * Makefile.direct, Makefile.dj, Makefile.am, include/include.am:
+ Adjust for version.h rename.
+ * Makefile.in: Regenerate.
+
+2007-12-21 Hans Boehm <Hans.Boehm@hp.com> (Really mostly russ sludge dot net)
+
+ * configure.ac: Put libatomic_ops links in build directory.
+ * configure: Regenerate.
+ * Makefile.am: Dont mention atomic_ops.c and atomic_ops_sysdeps.S
+ as nodist sources.
+
+2007-12-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * include/gc.h, doc/README.macros: Add GC_NO_THREAD_REDIRECTS,
+ GC_NO_THREAD_DECLS, don't test explicitly for GC_SOLARIS_THREADS.
+
+2007-12-20 Hans Boehm <Hans.Boehm@hp.com>
+
+ * alloc.c: Deal correctly with address wrapping for
+ GC_greatest_plausible_heap_addr and GC_least_plausible_heap_addr.
+ * finalize.c, include/gc.h (GC_register_disappearing_link,
+ GC_register_finalizer_inner): Improve out-of-memory handling.
+ * include/private/gc_pmark.h: Fix comment spelling.
+
+2007-12-18 Hans Boehm <Hans.Boehm@hp.com> (really mainly Peter Wang)
+
+ * include/gc_inline.h, include/gc_tiny_fl.h: cleanups to make usable
+ in other contexts.
+
+2007-12-18 Hans Boehm <Hans.Boehm@hp.com> (really Radek Polak)
+
+ * include/gc.h: Don't define GC_HAVE_BUILTIN_BACKTRACE for uclibc.
+
+2007-12-18 Hans Boehm <Hans.Boehm@hp.com>
+
+ * gc_cpp.cc: Don't include gc_cpp.h from local directory.
+
+2007-12-18 Hans Boehm <Hans.Boehm@hp.com> (really Adam Megacz)
+
+ * allchblk.c, configure.ac (add --enable-munmap)
+ * configure: Regenerate.
+
+2007-12-10 Andreas Tobler <a.tobler@schweiz.org>
+
+ * dyn_load.c (GC_dyld_image_add): Remove ifdef clause and use the macro
+ GC_GETSECTBYNAME instead.
+ * include/private/gc_priv.h: Define GC_GETSECTBYNAME according to the
+ architecture (Darwin).
+
+2007-10-24 Hans Boehm <Hans.Boehm@hp.com>
+
+ * reclaim.c (GC_bytes_found): Expand comment.
+ * thread_local_alloc.c (GC_malloc_atomic, GC_gcj_malloc): Pass
+ granules, not bytes, to GC_FAST_MALLOC_GRANS.
+ * include/gc.h: Never include gc_local_alloc.h.
+ * tests/test.c: Add size zero allocation tests.
+
+2007-10-23 Hans Boehm <Hans.Boehm@hp.com>
+
+ * malloc.c: Update GC_large_allocd_bytes on explicit deallocation.
+ * allchblk.c: Sanity check GC_max_large_allocd_bytes.
+
+2007-10-23 Hans Boehm <Hans.Boehm@hp.com> (Really Manuel Serrano)
+
+ * Makefile.direct: Invoke $(MAKE) instead of make.
+
+2007-10-23 Hans Boehm <Hans.Boehm@hp.com>
+
+ * doc/scale.html: Reflect gc7 thread local allocation behavior.
+
+2007-10-23 Hans Boehm <Hans.Boehm@hp.com> (really Petter Urkedal)
+
+ * include/extra/gc.h, include/extra/gc_cpp.h: New.
+ * include/include.am: Install gc.h and gc_cpp.h in $(prefix)/include
+ again.
+ * Makefile.in: Regenerate.
+
+2007-08-15 Hans Boehm <Hans.Boehm@hp.com> (really Samuel Thibault)
+
+ * pthread_support.c (GC_thr_init): Use sysconf(_SC_NPROCESSORS_ONLN)
+ for HURD.
+
+2007-08-14 Hans Boehm <Hans.Boehm@hp.com> (really David Daney)
+
+ * include/private/gcconfig.h: Add Linux/mips-64 support.
+
+2007-08-14 Hans Boehm <Hans.Boehm@hp.com> (really mostly Samuel Thibault)
+
+ * dbg_mlc.c: Use random() on all glibc systems.
+ * mach_dep.c (GC_with_callee_saves_pushed): Don't use getcontext() on
+ HURD. Add comment.
+ * pthread_stop_world.c (GC_suspend_handler, GC_stop_init): Accomodate
+ systems without SA_SIGINFO.
+
+2007-08-14 Hans Boehm <Hans.Boehm@hp.com> (partly really Henrik Theiling)
+
+ * include/gc.h (GC_PTR_STORE): Fix non-DEBUG parentheses.
+ * tests/test.c (run_one_test): Add GC_PTR_STORE test.
+ No longer test for RS6000.
+
+2007-08-03 Hans Boehm <Hans.Boehm@hp.com>
+
+ * alloc.c, backgraph.c, headers.c, include/private/gc_priv.h:
+ Maintain GC_our_memory and GC_n_memory.
+ * dbg_mlc.c (GC_print_smashed_obj): Improve message.
+ (GC_print_all_smashed_proc): Pass client object address instead of
+ base.
+ * dyn_load.c (sort_heap_sects): New. (GC_register_map_entries):
+ Register sections that are contiguous and merged with our heap.
+ * malloc.c, os_dep.c (GC_text_mapping): Check for just base name
+ of libraries.
+ * malloc.c (calloc): Check for special callers even with
+ USE_PROC_FOR_LIBRARIES. Move assertion. Add rudimentary
+ malloc/free tracing.
+ * misc.c: No longer call GC_init_lib_bounds explicitly.
+ * thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Always
+ initialize on demand.
+ * tests/test.c: Call GC_INIT only when required.
+
+2007-08-03 Hans Boehm <Hans.Boehm@hp.com>
+
+ * Makefile.direct: Remove comment fragment.
+ * tests/tests.am: Add smashtest.
+ * Makefile.in: Regenerate.
+ * configure.ac: Define GC_USE_DLOPEN_WRAP with redirect-malloc.
+ * configure: Regenerate.
+ * pthread_support.c: Fix comment spelling.
+ * include/private/gcconfig.h: Define USE_PROC_FOR_LIBRARIES with
+ GC_LINUX_THREADS and REDIRECT_MALLOC.
+ * tests/smash_test.c: Initial check-in.
+ * obj_map.c: Print log entry to correct file.
+ * include/private/thread_local_alloc.h: Add TlsAlloc error check.
+
+2007-07-23 Hans Boehm <Hans.Boehm@hp.com>
+
+ * alloc.c (GC_stopped_mark): Call GC_add_current_malloc_heap()
+ while world is still running.
+ * os_dep.c (GC_is_heap_base): Don't call GC_add_current_malloc_heap()
+ with world stopped.
+ * include/gc.h (GC_INIT for cygwin): Always call GC_add_roots.
+ * misc.c (GC_init/GC_init_inner): Perform all work in
+ GC_init_inner.
+ * Makefile.direct: Expand -DUSE_MUNMAP comment.
+
+2007-07-23 Hans Boehm <Hans.Boehm@hp.com> (really Jim Marshall)
+
+ * include/gc.h: Define uintptr_t explicitly for VC++6.
+ * msvc_dbg.c (GetModuleBase): Revert to strcat if strcat_s doesn't
+ exist.
+
+2007-07-02 Hans Boehm <Hans.Boehm@hp.com>
+
+ * version.h, configure.ac, doc/README: Change to version 7.1alpha1.
+ * configure: Regenerate.
+
+2007-07-02 Hans Boehm <Hans.Boehm@hp.com>
+
+ * version.h, configure.ac, doc/README: Change to version 7.0.
+ * configure: Regenerate.
+
2007-07-02 Hans Boehm <Hans.Boehm@hp.com>
- * gc_config_macros.h: Also check for IA64 when setting
+ * include/gc_config_macros.h: Also check for IA64 when setting
GC_HPUX_THREADS.
* mallocx.c: Change my_bytes_allocd to signed_word.
- * include/pthread_redirects.h: Remove obsolete Solaris threads
+ * include/gc_pthread_redirects.h: Remove obsolete Solaris threads
(as opposed to pthreads) support.
2007-07-02 Hans Boehm <Hans.Boehm@hp.com>
libgc_la_SOURCES += win32_threads.c
endif
-if USE_INTERNAL_LIBATOMIC_OPS
-nodist_libgc_la_SOURCES = atomic_ops.c
-endif
-
-if NEED_ATOMIC_OPS_ASM
-nodist_libgc_la_SOURCES = atomic_ops_sysdeps.S
+if USE_INTERNAL_LIBATOMIC_OPS
+nodist_libgc_la_SOURCES = ./atomic_ops.c
+endif
+
+if NEED_ATOMIC_OPS_ASM
+nodist_libgc_la_SOURCES = ./atomic_ops_sysdeps.S
endif
# Include THREADDLLIBS here to ensure that the correct versions of
noinst_LTLIBRARIES += libgccpp.la
pkginclude_HEADERS += include/gc_cpp.h include/gc_allocator.h
libgccpp_la_SOURCES = gc_cpp.cc
-libgccpp_la_LIBADD = $(top_builddir)/libgc.la
+libgccpp_la_LIBADD = ./libgc.la
libgccpp_la_LDFLAGS = -version-info 1:3:0 -no-undefined
endif
# headers which are not installed
# (see include/include.am for more)
#
-dist_noinst_HEADERS += version.h
# documentation which is not installed
#
OS2_MAKEFILE PCR-Makefile digimars.mak EMX_MAKEFILE \
Makefile.direct Makefile.dj Makefile.DLLs SMakefile.amiga \
WCC_MAKEFILE configure_atomic_ops.sh \
- NT_STATIC_THREADS_MAKEFILE NT_X64_STATIC_THREADS_MAKEFILE
+ NT_STATIC_THREADS_MAKEFILE NT_X64_STATIC_THREADS_MAKEFILE \
+ NT_X64_THREADS_MAKEFILE
# files used by makefiles other than Makefile.am
#
# -DUSE_MUNMAP causes memory to be returned to the OS under the right
# circumstances. This currently disables VM-based incremental collection.
# This is currently experimental, and works only under some Unix,
-# Linux and Windows versions.
+# Linux and Windows versions. Requires -DUSE_MMAP, even under Windows,
+# where USE_MMAP doesn't do anything.
# -DMMAP_STACKS (for Solaris threads) Use mmap from /dev/zero rather than
# GC_scratch_alloc() to get stack memory.
# -DPRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever
# the GC_debug_ functions, or through the macros that expand to these,
# or by redirecting malloc to GC_debug_malloc_replacement.
# (Also eliminates the field for the requested object size.)
-# occasionally be useful for debugging of client code. Slows down the
-# collector somewhat, but not drastically.
# -DSAVE_CALL_COUNT=<n> Set the number of call frames saved with objects
# allocated through the debugging interface. Affects the amount of
# information generated in leak reports. Only matters on platforms
# required for applications that store pointers in mmapped segments without
# informaing the collector. But it typically performs poorly, especially
# since it will scan inactive but cached NPTL thread stacks completely.
+# -DNO_PROC_STAT Causes the collector to avoid relying on Linux'
+# /proc/self/stat.
#
CXXFLAGS= $(CFLAGS)
SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.S \
sparc_mach_dep.S include/gc.h include/gc_typed.h include/gc_tiny_fl.h \
- include/private/gc_hdrs.h include/private/gc_priv.h \
+ include/gc_version.h include/private/gc_hdrs.h include/private/gc_priv.h \
include/private/gcconfig.h include/private/gc_pmark.h \
include/gc_inline.h include/gc_mark.h \
threadlibs.c if_mach.c if_not_there.c gc_cpp.cc include/gc_cpp.h \
doc/simple_example.html doc/README.win64
TESTS= tests/test.c tests/test_cpp.cc tests/trace_test.c \
- tests/leak_test.c tests/thread_leak_test.c tests/middle.c
+ tests/leak_test.c tests/thread_leak_test.c tests/middle.c \
+ tests/smash_test.c tests/huge_test.c
GNU_BUILD_FILES= configure.ac Makefile.am configure acinclude.m4 \
libtool.m4 install-sh configure.host Makefile.in \
BCC_MAKEFILE EMX_MAKEFILE WCC_MAKEFILE Makefile.dj \
PCR-Makefile SMakefile.amiga Makefile.DLLs \
digimars.mak Makefile.direct NT_STATIC_THREADS_MAKEFILE \
- NT_X64_STATIC_THREADS_MAKEFILE configure_atomic_ops.sh
+ NT_X64_STATIC_THREADS_MAKEFILE NT_X64_THREADS_MAKEFILE \
+ configure_atomic_ops.sh
# Makefile and Makefile.direct are copies of each other.
OTHER_FILES= Makefile setjmp_t.c callprocs \
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 \
- version.h AmigaOS.c mscvc_dbg.c include/private/msvc_dbg.h \
+ AmigaOS.c mscvc_dbg.c include/private/msvc_dbg.h \
$(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES)
CORD_INCLUDE_FILES= $(srcdir)/include/gc.h $(srcdir)/include/cord.h \
# the default location, and we need to build
$(AO_INSTALL_DIR):
CC=$(CC) $(srcdir)/configure_atomic_ops.sh
- cd $(AO_SRC_DIR); make CC=$(CC) install
+ cd $(AO_SRC_DIR); $(MAKE) CC=$(CC) install
LEAKFLAGS=$(CFLAGS) -DFIND_LEAK
# Work-around for DEC optimizer tail recursion elimination bug.
# The ALPHA-specific line should be removed if gcc is used.
-alloc.o: version.h
+alloc.o: include/gc_version.h
cord:
mkdir cord
./setjmp_test
./gctest
-add_gc_prefix: $(srcdir)/add_gc_prefix.c $(srcdir)/version.h
+add_gc_prefix: $(srcdir)/add_gc_prefix.c $(srcdir)/include/gc_version.h
$(CC) -o add_gc_prefix $(srcdir)/add_gc_prefix.c
-gcname: $(srcdir)/gcname.c $(srcdir)/version.h
+gcname: $(srcdir)/gcname.c $(srcdir)/include/gc_version.h
$(CC) -o gcname $(srcdir)/gcname.c
#We assume this is being done from source directory.
cp Makefile Makefile.old
cp Makefile.direct Makefile
CC=$(CC) ./configure_atomic_ops.sh
- cd $(AO_SRC_DIR); make dist
+ cd $(AO_SRC_DIR); $(MAKE) dist
if test $(srcdir)/libatomic_ops-$(AO_VERSION) = $(AO_SRC_DIR); \
then \
mv $(AO_SRC_DIR) $(AO_SRC_DIR).bak ; \
CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o
SRCS= $(CSRCS) mips_sgi_mach_dep.S rs6000_mach_dep.s alpha_mach_dep.S \
- sparc_mach_dep.S include/gc.h include/gc_typed.h \
+ sparc_mach_dep.S include/gc.h include/gc_version.h include/gc_typed.h \
include/private/gc_hdrs.h include/private/gc_priv.h \
include/private/gcconfig.h include/private/gc_mark.h \
include/gc_inline.h gc.man \
Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \
add_gc_prefix.c README.solaris2 README.sgi README.hp README.uts \
win32_threads.c NT_THREADS_MAKEFILE gc.mak README.dj Makefile.dj \
- README.alpha README.linux README.MacOSX version.h Makefile.DLLs \
+ README.alpha README.linux README.MacOSX Makefile.DLLs \
WCC_MAKEFILE nursery.c include/gc_nursery.h include/gc_copy_descr.h
CORD_INCLUDE_FILES= $(srcdir)/include/gc.h $(srcdir)/include/cord.h \
# Work-around for DEC optimizer tail recursion elimination bug.
# The ALPHA-specific line should be removed if gcc is used.
-alloc.o: version.h
+alloc.o: include/gc_version.h
cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES)
$(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordbscs.c
# Makefile for Windows NT. Assumes Microsoft compiler.
-# DLLs are included in the root set under NT, but not under win32S.
+# DLLs are included in the root set.
# Use "nmake nodebug=1 all" for optimized versions of library, gctest and editor.
MY_CPU=AMD64
#include "config.h"
# include <stdio.h>
-# include "version.h"
+# include <gc.h>
int main(argc, argv, envp)
int argc;
word GC_free_bytes[N_HBLK_FLS+1] = { 0 };
/* Number of free bytes on each list. */
- /* Is bytes + the number of free bytes on lists n .. N_HBLK_FLS */
- /* > GC_max_large_allocd_bytes? */
+ /* Return the largest n such that */
+ /* Is GC_large_allocd_bytes + the number of free bytes on lists */
+ /* n .. N_HBLK_FLS > GC_max_large_allocd_bytes. */
+ /* If there is no such n, return 0. */
# ifdef __GNUC__
__inline__
# endif
- static GC_bool GC_enough_large_bytes_left(word bytes, int n)
+ static int GC_enough_large_bytes_left(void)
{
- int i;
- for (i = N_HBLK_FLS; i >= n; --i) {
- bytes += GC_free_bytes[i];
- if (bytes > GC_max_large_allocd_bytes) return TRUE;
+ int n;
+ word bytes = GC_large_allocd_bytes;
+
+ GC_ASSERT(GC_max_large_allocd_bytes <= GC_heapsize);
+ for (n = N_HBLK_FLS; n >= 0; --n) {
+ bytes += GC_free_bytes[n];
+ if (bytes >= GC_max_large_allocd_bytes) return n;
}
- return FALSE;
+ return 0;
}
# define INCR_FREE_BYTES(n, b) GC_free_bytes[n] += (b);
int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
struct hblk *second = GC_hblkfreelist[index];
hdr * second_hdr;
-# ifdef GC_ASSERTIONS
+# if defined(GC_ASSERTIONS) && !defined(USE_MUNMAP)
struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz);
hdr * nexthdr = HDR(next);
struct hblk *prev = GC_free_block_ending_at(h);
hdr * prevhdr = HDR(prev);
- GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr) || !IS_MAPPED(nexthdr));
- GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr) || !IS_MAPPED(prevhdr));
+ GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr)
+ || (signed_word)GC_heapsize < 0);
+ /* In the last case, blocks may be too large to merge. */
+ GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr)
+ || (signed_word)GC_heapsize < 0);
# endif
GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0);
GC_hblkfreelist[index] = h;
word sz;
unsigned short last_rec, threshold;
int i;
-# define UNMAP_THRESHOLD 6
+# ifndef MUNMAP_THRESHOLD
+# define MUNMAP_THRESHOLD 6
+# endif
for (i = 0; i <= N_HBLK_FLS; ++i) {
for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) {
hhdr = HDR(h);
if (!IS_MAPPED(hhdr)) continue;
- threshold = (unsigned short)(GC_gc_no - UNMAP_THRESHOLD);
+ threshold = (unsigned short)(GC_gc_no - MUNMAP_THRESHOLD);
last_rec = hhdr -> hb_last_reclaimed;
if ((last_rec > GC_gc_no || last_rec < threshold)
&& threshold < GC_gc_no /* not recently wrapped */) {
next = (struct hblk *)((word)h + size);
GET_HDR(next, nexthdr);
/* Coalesce with successor, if possible */
- if (0 != nexthdr && HBLK_IS_FREE(nexthdr)) {
- nextsize = nexthdr -> hb_sz;
+ if (0 != nexthdr && HBLK_IS_FREE(nexthdr)
+ && (signed_word) (size + (nextsize = nexthdr->hb_sz)) > 0
+ /* no pot. overflow */) {
if (IS_MAPPED(hhdr)) {
GC_ASSERT(!IS_MAPPED(nexthdr));
/* make both consistent, so that we can merge */
}
struct hblk *
-GC_allochblk_nth(size_t sz/* bytes */, int kind, unsigned flags, int n);
+GC_allochblk_nth(size_t sz/* bytes */, int kind, unsigned flags, int n,
+ GC_bool may_split);
/*
* Allocate (and return pointer to) a heap block
word blocks;
int start_list;
int i;
+ struct hblk *result;
+ int split_limit; /* Highest index of free list whose blocks we */
+ /* split. */
GC_ASSERT((sz & (GRANULE_BYTES - 1)) == 0);
blocks = OBJ_SZ_TO_BLOCKS(sz);
+ if ((signed_word)(blocks * HBLKSIZE) < 0) {
+ return 0;
+ }
start_list = GC_hblk_fl_from_blocks(blocks);
- for (i = start_list; i <= N_HBLK_FLS; ++i) {
- struct hblk * result = GC_allochblk_nth(sz, kind, flags, i);
- if (0 != result) {
- return result;
+ /* Try for an exact match first. */
+ result = GC_allochblk_nth(sz, kind, flags, start_list, FALSE);
+ if (0 != result) return result;
+ if (GC_use_entire_heap || GC_dont_gc
+ || USED_HEAP_SIZE < GC_requested_heapsize
+ || TRUE_INCREMENTAL || !GC_should_collect()) {
+ /* Should use more of the heap, even if it requires splitting. */
+ split_limit = N_HBLK_FLS;
+ } else {
+# ifdef USE_MUNMAP
+ /* avoid splitting, since that might require remapping */
+ split_limit = 0;
+# else
+ if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) {
+ /* If we are deallocating lots of memory from */
+ /* finalizers, fail and collect sooner rather */
+ /* than later. */
+ split_limit = 0;
+ } else {
+ /* If we have enough large blocks left to cover any */
+ /* previous request for large blocks, we go ahead */
+ /* and split. Assuming a steady state, that should */
+ /* be safe. It means that we can use the full */
+ /* heap if we allocate only small objects. */
+ split_limit = GC_enough_large_bytes_left();
}
+# endif
+ }
+ if (start_list < UNIQUE_THRESHOLD) {
+ /* No reason to try start_list again, since all blocks are exact */
+ /* matches. */
+ ++start_list;
+ }
+ for (i = start_list; i <= split_limit; ++i) {
+ struct hblk * result = GC_allochblk_nth(sz, kind, flags, i, TRUE);
+ if (0 != result) return result;
}
return 0;
}
* The same, but with search restricted to nth free list.
* Flags is IGNORE_OFF_PAGE or zero.
* Unlike the above, sz is in bytes.
+ * The may_split flag indicates whether it's OK to split larger blocks.
*/
struct hblk *
-GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n)
+GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n, GC_bool may_split)
{
struct hblk *hbp;
hdr * hhdr; /* Header corr. to hbp */
GET_HDR(hbp, hhdr);
size_avail = hhdr->hb_sz;
if (size_avail < size_needed) continue;
- if (size_avail != size_needed
- && !GC_use_entire_heap
- && !GC_dont_gc
- && USED_HEAP_SIZE >= GC_requested_heapsize
- && !TRUE_INCREMENTAL && GC_should_collect()) {
-# ifdef USE_MUNMAP
- continue;
-# else
- /* If we have enough large blocks left to cover any */
- /* previous request for large blocks, we go ahead */
- /* and split. Assuming a steady state, that should */
- /* be safe. It means that we can use the full */
- /* heap if we allocate only small objects. */
- if (!GC_enough_large_bytes_left(GC_large_allocd_bytes, n)) {
- continue;
- }
- /* If we are deallocating lots of memory from */
- /* finalizers, fail and collect sooner rather */
- /* than later. */
- if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) {
- continue;
- }
-# endif /* !USE_MUNMAP */
- }
- /* If the next heap block is obviously better, go on. */
- /* This prevents us from disassembling a single large block */
- /* to get tiny blocks. */
- {
+ if (size_avail != size_needed) {
signed_word next_size;
-
+
+ if (!may_split) continue;
+ /* If the next heap block is obviously better, go on. */
+ /* This prevents us from disassembling a single large block */
+ /* to get tiny blocks. */
thishbp = hhdr -> hb_next;
if (thishbp != 0) {
- GET_HDR(thishbp, thishdr);
+ GET_HDR(thishbp, thishdr);
next_size = (signed_word)(thishdr -> hb_sz);
if (next_size < size_avail
- && next_size >= size_needed
- && !GC_is_black_listed(thishbp, (word)size_needed)) {
- continue;
+ && next_size >= size_needed
+ && !GC_is_black_listed(thishbp, (word)size_needed)) {
+ continue;
}
}
}
struct hblk * prev = hhdr -> hb_prev;
GC_large_free_bytes -= total_size;
+ GC_bytes_dropped += total_size;
GC_remove_from_fl(hhdr, n);
for (h = hbp; h < limit; h++) {
if (h == hbp || 0 != (hhdr = GC_install_header(h))) {
/* Restore hbp to point at free block */
hbp = prev;
if (0 == hbp) {
- return GC_allochblk_nth(sz, kind, flags, n);
+ return GC_allochblk_nth(sz, kind, flags, n, may_split);
}
hhdr = HDR(hbp);
}
if (!IS_MAPPED(hhdr)) {
GC_remap((ptr_t)hbp, hhdr -> hb_sz);
hhdr -> hb_flags &= ~WAS_UNMAPPED;
+ /* Note: This may leave adjacent, mapped free blocks. */
}
# endif
/* hbp may be on the wrong freelist; the parameter n */
GET_HDR(hbp, hhdr);
size = hhdr->hb_sz;
size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(size);
+ if (size <= 0)
+ ABORT("Deallocating excessively large block. Too large an allocation?");
+ /* Probably possible if we try to allocate more than half the address */
+ /* space at once. If we dont catch it here, strange things happen */
+ /* later. */
GC_remove_counts(hbp, (word)size);
hhdr->hb_sz = size;
# ifdef USE_MUNMAP
GET_HDR(next, nexthdr);
prev = GC_free_block_ending_at(hbp);
/* Coalesce with successor, if possible */
- if(0 != nexthdr && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr)) {
+ if(0 != nexthdr && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr)
+ && (signed_word)(hhdr -> hb_sz + nexthdr -> hb_sz) > 0
+ /* no overflow */) {
GC_remove_from_fl(nexthdr, FL_UNKNOWN);
hhdr -> hb_sz += nexthdr -> hb_sz;
GC_remove_header(next);
/* Coalesce with predecessor, if possible. */
if (0 != prev) {
prevhdr = HDR(prev);
- if (IS_MAPPED(prevhdr)) {
+ if (IS_MAPPED(prevhdr)
+ && (signed_word)(hhdr -> hb_sz + prevhdr -> hb_sz) > 0) {
GC_remove_from_fl(prevhdr, FL_UNKNOWN);
prevhdr -> hb_sz += hhdr -> hb_sz;
# ifdef USE_MUNMAP
" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.",
"See source code for details." };
-# include "version.h"
+/* Version macros are now defined in gc_version.h, which is included by */
+/* gc.h, which is included by gc_priv.h". */
+
+#ifndef GC_NO_VERSION_VAR
+
+unsigned GC_version = ((GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | GC_TMP_ALPHA_VERSION);
+
+#endif /* GC_NO_VERSION_VAR */
/* some more variables */
if (stack_size < 0) stack_size = -stack_size;
total_root_size = 2 * stack_size + GC_root_size;
- scan_size = 2 * GC_composite_in_use + GC_atomic_in_use
+ scan_size = 2 * GC_composite_in_use + GC_atomic_in_use/4
+ total_root_size;
if (TRUE_INCREMENTAL) {
return scan_size / (2 * GC_free_space_divisor);
/* managed object should not alter result, assuming the client */
/* is playing by the rules. */
result = (signed_word)GC_bytes_allocd
+ + (signed_word)GC_bytes_dropped
- (signed_word)GC_bytes_freed
+ (signed_word)GC_finalizer_bytes_freed
- expl_managed;
/* Have we allocated enough to amortize a collection? */
GC_bool GC_should_collect(void)
{
- return(GC_adj_bytes_allocd() >= min_bytes_allocd()
+ static word last_min_bytes_allocd;
+ static word last_gc_no;
+ if (last_gc_no != GC_gc_no) {
+ last_gc_no = GC_gc_no;
+ last_min_bytes_allocd = min_bytes_allocd();
+ }
+ return(GC_adj_bytes_allocd() >= last_min_bytes_allocd
|| GC_heapsize >= GC_collect_at_heapsize);
}
* Initiate a garbage collection if appropriate.
* Choose judiciously
* between partial, full, and stop-world collections.
- * Assumes lock held, signals disabled.
*/
void GC_maybe_gc(void)
{
static int n_partial_gcs = 0;
+ GC_ASSERT(I_HOLD_LOCK());
if (GC_should_collect()) {
if (!GC_incremental) {
GC_gcollect_inner();
return(result);
}
+# if !defined(REDIRECT_MALLOC) && (defined(MSWIN32) || defined(MSWINCE))
+ void GC_add_current_malloc_heap();
+# endif
/*
* Assumes lock is held, signals are disabled.
* We stop the world.
if (GC_print_stats)
GET_TIME(start_time);
+# if !defined(REDIRECT_MALLOC) && (defined(MSWIN32) || defined(MSWINCE))
+ GC_add_current_malloc_heap();
+# endif
# if defined(REGISTER_LIBRARIES_EARLY)
GC_cond_register_dynamic_libraries();
# endif
GC_bytes_allocd_before_gc += GC_bytes_allocd;
GC_non_gc_bytes_at_gc = GC_non_gc_bytes;
GC_bytes_allocd = 0;
+ GC_bytes_dropped = 0;
GC_bytes_freed = 0;
GC_finalizer_bytes_freed = 0;
word GC_n_heap_sects = 0; /* Number of sections currently in heap. */
+#ifdef USE_PROC_FOR_LIBRARIES
+ word GC_n_memory = 0; /* Number of GET_MEM allocated memory */
+ /* sections. */
+#endif
+
+#ifdef USE_PROC_FOR_LIBRARIES
+ /* Add HBLKSIZE aligned, GET_MEM-generated block to GC_our_memory. */
+ /* Defined to do nothing if USE_PROC_FOR_LIBRARIES not set. */
+ void GC_add_to_our_memory(ptr_t p, size_t bytes)
+ {
+ if (0 == p) return;
+ GC_our_memory[GC_n_memory].hs_start = p;
+ GC_our_memory[GC_n_memory].hs_bytes = bytes;
+ GC_n_memory++;
+ }
+#endif
/*
* Use the chunk of memory starting at p of size bytes as part of the heap.
* Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE.
void GC_add_to_heap(struct hblk *p, size_t bytes)
{
hdr * phdr;
+ word endp;
if (GC_n_heap_sects >= MAX_HEAP_SECTS) {
ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS");
}
+ while ((word)p <= HBLKSIZE) {
+ /* Can't handle memory near address zero. */
+ ++p;
+ bytes -= HBLKSIZE;
+ if (0 == bytes) return;
+ }
+ endp = (word)p + bytes;
+ if (endp <= (word)p) {
+ /* Address wrapped. */
+ bytes -= HBLKSIZE;
+ if (0 == bytes) return;
+ endp -= HBLKSIZE;
+ }
phdr = GC_install_header(p);
if (0 == phdr) {
/* This is extremely unlikely. Can't add it. This will */
/* which is entirely appropriate. */
return;
}
+ GC_ASSERT(endp > (word)p && endp == (word)p + bytes);
GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p;
GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes;
GC_n_heap_sects++;
/* here. */
}
if ((ptr_t)p + bytes >= (ptr_t)GC_greatest_plausible_heap_addr) {
- GC_greatest_plausible_heap_addr = (void *)((ptr_t)p + bytes);
+ GC_greatest_plausible_heap_addr = (void *)endp;
}
}
void * GC_least_plausible_heap_addr = (void *)ONES;
void * GC_greatest_plausible_heap_addr = 0;
-static INLINE ptr_t GC_max(ptr_t x, ptr_t y)
+static INLINE word GC_max(word x, word y)
{
return(x > y? x : y);
}
-static INLINE ptr_t GC_min(ptr_t x, ptr_t y)
+static INLINE word GC_min(word x, word y)
{
return(x < y? x : y);
}
return(FALSE);
}
space = GET_MEM(bytes);
+ GC_add_to_our_memory((ptr_t)space, bytes);
if( space == 0 ) {
if (GC_print_stats) {
GC_log_printf("Failed to expand heap by %ld bytes\n",
(unsigned long)bytes,
(unsigned long)GC_bytes_allocd);
}
+ /* Adjust heap limits generously for blacklisting to work better. */
+ /* GC_add_to_heap performs minimal adjustment need for correctness. */
expansion_slop = min_bytes_allocd() + 4*MAXHINCR*HBLKSIZE;
if ((GC_last_heap_addr == 0 && !((word)space & SIGNB))
|| (GC_last_heap_addr != 0 && GC_last_heap_addr < (ptr_t)space)) {
/* Assume the heap is growing up */
- GC_greatest_plausible_heap_addr =
- (void *)GC_max((ptr_t)GC_greatest_plausible_heap_addr,
- (ptr_t)space + bytes + expansion_slop);
+ word new_limit = (word)space + bytes + expansion_slop;
+ if (new_limit > (word)space) {
+ GC_greatest_plausible_heap_addr =
+ (void *)GC_max((word)GC_greatest_plausible_heap_addr,
+ (word)new_limit);
+ }
} else {
/* Heap is growing down */
- GC_least_plausible_heap_addr =
- (void *)GC_min((ptr_t)GC_least_plausible_heap_addr,
- (ptr_t)space - expansion_slop);
+ word new_limit = (word)space - expansion_slop;
+ if (new_limit < (word)space) {
+ GC_least_plausible_heap_addr =
+ (void *)GC_min((word)GC_least_plausible_heap_addr,
+ (word)space - expansion_slop);
+ }
}
-# if defined(LARGE_CONFIG)
- if (((ptr_t)GC_greatest_plausible_heap_addr <= (ptr_t)space + bytes
- || (ptr_t)GC_least_plausible_heap_addr >= (ptr_t)space)
- && GC_heapsize > 0) {
- /* GC_add_to_heap will fix this, but ... */
- WARN("Too close to address space limit: blacklisting ineffective\n", 0);
- }
-# endif
GC_prev_heap_addr = GC_last_heap_addr;
GC_last_heap_addr = (ptr_t)space;
GC_add_to_heap(space, bytes);
if (0 == back_edge_space) {
back_edge_space = (back_edges *)
GET_MEM(MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
+ GC_add_to_our_memory((ptr_t)back_edge_space,
+ MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
}
if (0 != avail_back_edges) {
back_edges * result = avail_back_edges;
if (in_progress_size == 0) {
in_progress_size = INITIAL_IN_PROGRESS;
in_progress_space = (ptr_t *)GET_MEM(in_progress_size * sizeof(ptr_t));
+ GC_add_to_our_memory((ptr_t)in_progress_space,
+ in_progress_size * sizeof(ptr_t));
} else {
ptr_t * new_in_progress_space;
in_progress_size *= 2;
new_in_progress_space = (ptr_t *)
GET_MEM(in_progress_size * sizeof(ptr_t));
+ GC_add_to_our_memory((ptr_t)new_in_progress_space,
+ in_progress_size * sizeof(ptr_t));
BCOPY(in_progress_space, new_in_progress_space,
n_in_progress * sizeof(ptr_t));
in_progress_space = new_in_progress_space;
# Initialization
# ==============
-AC_INIT(gc,7.0,Hans.Boehm@hp.com)
+AC_INIT(gc,7.1,Hans.Boehm@hp.com)
## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)?
AC_CONFIG_SRCDIR(gcj_mlc.c)
AC_CANONICAL_TARGET
AC_PREREQ(2.53)
-AC_REVISION($Revision: 1.25 $)
+AC_REVISION($Revision: 1.35 $)
GC_SET_VERSION
AM_INIT_AUTOMAKE([foreign dist-bzip2 subdir-objects nostdinc])
AM_MAINTAINER_MODE
AC_DEFINE([GC_DARWIN_THREADS], 1, [gc darwin threads])
AC_DEFINE([THREAD_LOCAL_ALLOC], 1, [thread local alloc])
AC_MSG_WARN("Explict GC_INIT() calls may be required.");
+ # Parallel-mark is currently unreliable on Darwin; ignore request
+ # if test "${enable_parallel_mark}" = yes; then
+ # AC_DEFINE(PARALLEL_MARK)
+ # fi
if test "${enable_parallel_mark}" = yes; then
AC_DEFINE([PARALLEL_MARK], 1, [parallel mark])
fi
;;
esac
+case "$host" in
+ *-*-hpux*)
+ avoid_cpp_lib=yes;;
+ *)
+ avoid_cpp_lib=no;
+ ;;
+esac
+AM_CONDITIONAL(AVOID_CPP_LIB,test $avoid_cpp_lib = yes)
+
# extra LD Flags which are required for targets
case "${host}" in
*-*-darwin*)
case "$host" in
alpha-*-openbsd*)
enable_shared=no
- AC_MSG_RESULT(no)
;;
*)
- AC_MSG_RESULT(yes)
;;
esac
+AC_MSG_RESULT($enable_shared)
+
# Configuration of machine-dependent code
#
AC_MSG_CHECKING(which machine-dependent code should be used)
else
AC_DEFINE([REDIRECT_MALLOC], GC_malloc, [redirect malloc])
fi
+ AC_DEFINE(GC_USE_DLOPEN_WRAP)
fi
AC_ARG_ENABLE(large-config,
AC_DEFINE([GC_ASSERTIONS], 1, [gc assertions])
fi
+AC_ARG_ENABLE(munmap,
+ [AC_HELP_STRING([--enable-munmap=N],
+ [return page to the os if empty for N collections])],
+ MUNMAP_THRESHOLD=$enableval;
+ [case "$MMAP" in
+ no)
+ AC_MSG_ERROR([--enable-munmap requires --enable-mmap])
+ ;;
+ esac]
+ )
+if test "${enable_munmap}" != ""; then
+ AC_DEFINE(USE_MMAP)
+ AC_DEFINE(USE_MUNMAP)
+ if test "${MUNMAP_THRESHOLD}" = "yes"; then
+ MUNMAP_THRESHOLD=6
+ fi
+ AC_DEFINE_UNQUOTED(MUNMAP_THRESHOLD, ${MUNMAP_THRESHOLD})
+fi
+
AM_CONDITIONAL(USE_LIBDIR, test -z "$with_cross_host")
AC_MSG_NOTICE([Using internal version of libatomic_ops])
dnl Automake does not accept shell variables in AC_CONFIG_SUBDIRS
- test -e ${srcdir}/libatomic_ops \
- || ln -s ${ao_dir} ${srcdir}/libatomic_ops
+ test -e libatomic_ops \
+ || ln -s ${ao_dir} libatomic_ops
AC_CONFIG_SUBDIRS(libatomic_ops)
dnl Also copy the source files to be linked in.
- test -e ${srcdir}/atomic_ops.c \
- || ln -s ${srcdir}/libatomic_ops/src/atomic_ops.c \
- ${srcdir}/atomic_ops.c
+ test -e atomic_ops.c \
+ || ln -s libatomic_ops/src/atomic_ops.c \
+ atomic_ops.c
- test -e ${srcdir}/atomic_ops_sysdeps.S \
- || ln -s ${srcdir}/libatomic_ops/src/atomic_ops_sysdeps.S \
- ${srcdir}/atomic_ops_sysdeps.S
+ test -e atomic_ops_sysdeps.S \
+ || ln -s libatomic_ops/src/atomic_ops_sysdeps.S \
+ atomic_ops_sysdeps.S
dnl This gets the source include files, which is often close enough.
dnl It also makes atomic_ops_sysdeps.S assemble.
- GC_CFLAGS="${GC_CFLAGS} -I \$(top_srcdir)/libatomic_ops/src"
+ GC_CFLAGS="${GC_CFLAGS} -I libatomic_ops/src"
maybe_libatomic_ops="libatomic_ops"
])
it created before stopping show up later.
*/
+ /* FIXME: This seems to erroneously stop the parallel marker threads? */
+
changes = 1;
prev_list = NULL;
prevcount = 0;
# include <stdlib.h>
-# if defined(LINUX) || defined(SOLARIS) \
+# if defined(__GLIBC__) || defined(SOLARIS) \
|| defined(HPUX) || defined(IRIX5) || defined(OSF1)
# define RANDOM() random()
# else
}
#ifndef SHORT_DBG_HDRS
+/* Use GC_err_printf and friends to print a description of the object */
+/* whose client-visible address is p, and which was smashed at */
+/* clobbered_addr. */
void GC_print_smashed_obj(ptr_t p, ptr_t clobbered_addr)
{
register oh * ohdr = (oh *)GC_base(p);
GC_ASSERT(I_DONT_HOLD_LOCK());
- GC_err_printf("%p in object at %p(", clobbered_addr, p);
+ GC_err_printf("%p in or near object at %p(", clobbered_addr, p);
if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz))
|| ohdr -> oh_string == 0) {
GC_err_printf("<smashed>, appr. sz = %ld)\n",
if (GC_n_smashed == 0) return;
GC_err_printf("GC_check_heap_block: found smashed heap objects:\n");
for (i = 0; i < GC_n_smashed; ++i) {
- GC_print_smashed_obj(GC_base(GC_smashed[i]), GC_smashed[i]);
+ GC_print_smashed_obj((ptr_t)GC_base(GC_smashed[i]) + sizeof(oh),
+ GC_smashed[i]);
GC_smashed[i] = 0;
}
GC_n_smashed = 0;
collector. (If you are concerned about such things, I recommend you look
at the notice in config.guess or ltmain.sh.)
-This is version 7.0 of a conservative garbage collector for C and C++.
+This is version 7.1 of a conservative garbage collector for C and C++.
You might find a more recent version of this at
MACRO EXPLANATION
----- -----------
+GC_DEBUG Tested by gc.h. Causes all-upper-case macros to
+ expand to calls to debug versions of collector routines.
+
+GC_NO_THREAD_REDIRECTS Tested by gc.h. Prevents redirection of thread
+ creation routines etc. to GC_ versions. Requires the
+ programmer to explicitly handle thread registration.
+
+GC_NO_THREAD_DECLS Tested by gc.h. MS Windows only. Do not declare
+ Windows thread creation routines and do not include windows.h.
+
__DMC__ Always #define'd by the Digital Mars compiler. Expands
to the compiler version number in hex, i.e. 0x810 is
version 8.1b0
GNU Tools
---------
The collector should be buildable under Cygwin with either the old standard
-Makefile, or possibly with the "configure --diable-shared;make" machinery.
+Makefile, or possibly with the "configure --disable-shared;make" machinery.
(For the latter use --enable-threads=posix for thread support.) The major issue
here seems to be that dynamic library support is not currently enabled for
Cygwin. (This is probably fixable without a great deal of difficulty by
64-bit Windows on AMD64/Intel EM64T is somewhat supported in the 7.0
-release. A collector can be built with Microsoft Visual C++ 2005.
+and later release. A collector can be built with Microsoft Visual C++ 2005.
The resulting test programs have been known to work at least once.
More testing would clearly be helpful.
-Currently only NT_X64_STATIC_THREADS_MAKEFILE has been used in
+NT_X64_STATIC_THREADS_MAKEFILE has been used in
this environment. Copy this file to MAKEFILE, and then type "nmake"
in a Visual C++ command line window to build the static library
and the usual test programs. To verify that the colllector is
This process is completely analogous to NT_STATIC_THREADS_MAKEFILE
for the 32-bit version.
+A similar procedure using NT_X64_THREADS_MAKEFILE should be usable to
+build the dynamic library. Test_cpp.exe did not seem to run correctly this
+way. It seems that we're getting the wrong instances of operator new/delete
+in some cases. The C tests seemed OK.
+
Note that currently a few warnings are still generated by default,
and a number of others have been explicitly turned off in the makefile.
<I>from the main executable, not from a dynamic library,</i> before
the initial invocation of a GC routine. It is recommended that this be done
in portable code, though we try to ensure that it expands to a no-op
-on as many platforms as possible. As of GC 7.0, it is required if
+on as many platforms as possible. In GC 7.0, it was required if
thread-local allocation is enabled in the collector build, and <TT>malloc</tt>
is not redirected to <TT>GC_malloc</tt>.
<DT> <B> void GC_gcollect(void) </b>
The easiest way to ensure that collectable objects are properly referenced
is to allocate only collectable objects. This requires that every
allocation go through one of the following interfaces, each one of
-which replaces a standard C++ allocation mechanism:
+which replaces a standard C++ allocation mechanism. Note that
+this requires that all STL containers be explicitly instantiated with
+<TT>gc_allocator</tt>.
<DL>
<DT> <B> STL allocators </b>
<DD>
<P>
-Recent versions of the collector also include a more standard-conforming
+Recent versions of the collector include a hopefully standard-conforming
allocator implementation in <TT>gc_allocator.h</tt>. It defines
<UL>
-<LI> traceable_allocator
-<LI> gc_allocator
+<LI> <TT>traceable_allocator</tt>
+<LI> <TT>gc_allocator</tt>
</ul>
which may be used either directly to allocate memory or to instantiate
container templates.
<P>
Users of the <A HREF="http://www.sgi.com/tech/stl">SGI extended STL</a>
or its derivatives (including most g++ versions)
-can alternatively include <TT>new_gc_alloc.h</tt> before including
-STL header files.
+may instead be able to include <TT>new_gc_alloc.h</tt> before including
+STL header files. This is increasingly discouraged.
(<TT>gc_alloc.h</tt> corresponds to now obsolete versions of the
SGI STL.) This interface is no longer recommended, but it has existed
for much longer.
<P>
This defines SGI-style allocators
<UL>
-<LI> alloc
-<LI> single_client_alloc
-<LI> gc_alloc
-<LI> single_client_gc_alloc
+<LI> <TT>alloc</tt>
+<LI> <TT>single_client_alloc</tt>
+<LI> <TT>gc_alloc</tt>
+<LI> <TT>single_client_gc_alloc</tt>
</ul>
The first two allocate uncollectable but traced
memory, while the second two allocate collectable memory.
-The single_client versions are not safe for concurrent access by
+The <TT>single_client</tt> versions are not safe for concurrent access by
multiple threads, but are faster.
<P>
For an example, click <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_alloc_exC.txt">here</a>.
-<DT> <B> Class inheritance based interface </b>
+<DT> <B> Class inheritance based interface for new-based allocation</b>
<DD>
Users may include gc_cpp.h and then cause members of classes to
be allocated in garbage collectable memory by having those classes
built, and memory can be cleared, by more than one thread concurrently.
<LI>
Building the collector with -DTHREAD_LOCAL_ALLOC adds support for thread
-local allocation. It does not, by itself, cause thread local allocation
-to be used. It simply allows the use of the interface in
-<TT>gc_local_alloc.h</tt>.
+local allocation. Before GC version 7.0, it did not, by itself, cause
+thread local allocation to be used. It simply allowed the use of the
+interface in <TT>gc_local_alloc.h</tt>. Since version 7.0, this causes
+GC_malloc, GC_malloc_atomic, and GC_gcj_malloc to be redefined to perform
+thread-local allocation.
<P>
Memory returned from thread-local allocators is completely interchangeable
with that returned by the standard allocators. It may be used by other
is greatly reduced.
</ul>
<P>
-The easiest way to switch an application to thread-local allocation is to
+The easiest way to switch an application to thread-local allocation
+in a pre-version-7.0 collector was to
<OL>
<LI> Define the macro <TT>GC_REDIRECT_TO_LOCAL</tt>,
and then include the <TT>gc.h</tt>
char *GC_get_maps(void);
/* From os_dep.c */
+/* Sort an array of HeapSects by start address. */
+/* Unfortunately at least some versions of */
+/* Linux qsort end up calling malloc by way of sysconf, and hence can't */
+/* be used in the colector. Hence we roll our own. Should be */
+/* reasonably fast if the array is already mostly sorted, as we expect */
+/* it to be. */
+void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
+{
+ signed_word n = (signed_word)number_of_elements;
+ signed_word nsorted = 1;
+ signed_word i;
+
+ while (nsorted < n) {
+ while (nsorted < n &&
+ base[nsorted-1].hs_start < base[nsorted].hs_start)
+ ++nsorted;
+ if (nsorted == n) break;
+ GC_ASSERT(base[nsorted-1].hs_start > base[nsorted].hs_start);
+ i = nsorted - 1;
+ while (i >= 0 && base[i].hs_start > base[i+1].hs_start) {
+ struct HeapSect tmp = base[i];
+ base[i] = base[i+1];
+ base[i+1] = tmp;
+ --i;
+ }
+ GC_ASSERT(base[nsorted-1].hs_start < base[nsorted].hs_start);
+ ++nsorted;
+ }
+}
+
word GC_register_map_entries(char *maps)
{
char *prot;
unsigned i;
ptr_t datastart = (ptr_t)(DATASTART);
- /* Compute heap bounds. FIXME: Should work if heap and roots are */
- /* interleaved? */
- least_ha = (ptr_t)(word)(-1);
- greatest_ha = 0;
- for (i = 0; i < GC_n_heap_sects; ++i) {
- ptr_t sect_start = GC_heap_sects[i].hs_start;
- ptr_t sect_end = sect_start + GC_heap_sects[i].hs_bytes;
- if (sect_start < least_ha) least_ha = sect_start;
- if (sect_end > greatest_ha) greatest_ha = sect_end;
- }
- if (greatest_ha < (ptr_t)GC_scratch_last_end_ptr)
- greatest_ha = (ptr_t)GC_scratch_last_end_ptr;
+ GC_ASSERT(I_HOLD_LOCK());
+ sort_heap_sects(GC_our_memory, GC_n_memory);
+ least_ha = GC_our_memory[0].hs_start;
+ greatest_ha = GC_our_memory[GC_n_memory-1].hs_start
+ + GC_our_memory[GC_n_memory-1].hs_bytes;
for (;;) {
buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot, &maj_dev, 0);
/* That can fail because the stack may disappear while */
/* we're marking. Thus the marker is, and has to be */
/* prepared to recover from segmentation faults. */
+
if (GC_segment_is_thread_stack(start, end)) continue;
- /* FIXME: REDIRECT_MALLOC actually works with threads on */
- /* LINUX/IA64 if we omit this check. The problem is that */
+
+ /* FIXME: NPTL squirrels */
+ /* away pointers in pieces of the stack segment that we */
+ /* don't scan. We work around this */
+ /* by treating anything allocated by libpthread as */
+ /* uncollectable, as we do in some other cases. */
+ /* A specifically identified problem is that */
/* thread stacks contain pointers to dynamic thread */
/* vectors, which may be reused due to thread caching. */
- /* Currently they may not be marked if the thread is */
- /* still live. */
- /* For dead threads, we trace the whole stack, which is */
+ /* They may not be marked if the thread is still live. */
+ /* This specific instance should be addressed by */
+ /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite */
+ /* seem to suffice. */
+ /* We currently trace entire thread stacks, if they are */
+ /* are currently cached but unused. This is */
/* very suboptimal for performance reasons. */
# endif
/* We no longer exclude the main data segment. */
- if (start < least_ha && end > least_ha) {
- end = least_ha;
+ if (end <= least_ha || start >= greatest_ha) {
+ /* The easy case; just trace entire segment */
+ GC_add_roots_inner((char *)start, (char *)end, TRUE);
+ continue;
}
- if (start < greatest_ha && end > greatest_ha) {
- start = greatest_ha;
- }
- if (start >= least_ha && end <= greatest_ha) continue;
- GC_add_roots_inner((char *)start, (char *)end, TRUE);
+ /* Add sections that dont belong to us. */
+ i = 0;
+ while (GC_our_memory[i].hs_start + GC_our_memory[i].hs_bytes
+ < start)
+ ++i;
+ GC_ASSERT(i < GC_n_memory);
+ if (GC_our_memory[i].hs_start <= start) {
+ start = GC_our_memory[i].hs_start
+ + GC_our_memory[i].hs_bytes;
+ ++i;
+ }
+ while (i < GC_n_memory && GC_our_memory[i].hs_start < end
+ && start < end) {
+ if ((char *)start < GC_our_memory[i].hs_start)
+ GC_add_roots_inner((char *)start,
+ GC_our_memory[i].hs_start, TRUE);
+ start = GC_our_memory[i].hs_start
+ + GC_our_memory[i].hs_bytes;
+ ++i;
+ }
+ if (start < end)
+ GC_add_roots_inner((char *)start, (char *)end, TRUE);
}
}
return 1;
const struct GC_MACH_SECTION *sec;
if (GC_no_dls) return;
for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
-# if defined (__LP64__)
- sec = getsectbynamefromheader_64(
-# else
- sec = getsectbynamefromheader(
-# endif
- hdr, GC_dyld_sections[i].seg,
- GC_dyld_sections[i].sect);
+ sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
+ GC_dyld_sections[i].sect);
if(sec == NULL || sec->size == 0) continue;
start = slide + sec->addr;
end = start + sec->size;
unsigned long start,end,i;
const struct GC_MACH_SECTION *sec;
for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
-# if defined (__LP64__)
- sec = getsectbynamefromheader_64(
-# else
- sec = getsectbynamefromheader(
-# endif
- hdr, GC_dyld_sections[i].seg,
- GC_dyld_sections[i].sect);
+ sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
+ GC_dyld_sections[i].sect);
if(sec == NULL || sec->size == 0) continue;
start = slide + sec->addr;
end = start + sec->size;
(size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL);
if (new_table == 0) {
- if (table == 0) {
+ if (*table == 0) {
ABORT("Insufficient space for initial table allocation");
} else {
return;
GC_oom_fn(sizeof(struct disappearing_link));
if (0 == new_dl) {
GC_finalization_failures++;
- return(0);
+ return(2);
}
/* It's not likely we'll make it here, but ... */
# ifdef THREADS
}
new_fo = (struct finalizable_object *)
GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL);
- GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
if (EXPECT(0 == new_fo, FALSE)) {
# ifdef THREADS
UNLOCK();
LOCK();
# endif
}
+ GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
new_fo -> fo_hidden_base = (word)HIDE_POINTER(base);
new_fo -> fo_fn = fn;
new_fo -> fo_client_data = (ptr_t)cd;
Authors: John R. Ellis and Jesse Hull
**************************************************************************/
-/* Boehm, December 20, 1994 7:26 pm PST */
-#include "gc_cpp.h"
+#include <gc_cpp.h>
void* operator new( size_t size ) {
return GC_MALLOC_UNCOLLECTABLE( size );}
#include "config.h"
#include <stdio.h>
-#include "version.h"
+#include <gc.h>
int main()
{
bytes_to_get &= ~(GC_page_size - 1);
# endif
result = (ptr_t)GET_MEM(bytes_to_get);
+ GC_add_to_our_memory(result, bytes_to_get);
scratch_free_ptr -= bytes;
GC_scratch_last_end_ptr = result + bytes;
return(result);
}
result = (ptr_t)GET_MEM(bytes_to_get);
+ GC_add_to_our_memory(result, bytes_to_get);
if (result == 0) {
if (GC_print_stats)
GC_printf("Out of memory - trying to allocate less\n");
bytes_to_get += GC_page_size - 1;
bytes_to_get &= ~(GC_page_size - 1);
# endif
- return((ptr_t)GET_MEM(bytes_to_get));
+ result = (ptr_t)GET_MEM(bytes_to_get);
+ GC_add_to_our_memory(result, bytes_to_get);
+ return result;
}
scratch_free_ptr = result;
GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get;
# define _GC_H
+# include "gc_version.h"
+ /* Define version numbers here to allow test on build machine */
+ /* for cross-builds. Note that this defines the header */
+ /* version number, which may or may not match that of the */
+ /* dynamic library. The GC_version variable can be used */
+ /* to obtain the latter. */
+
# include "gc_config_macros.h"
# ifdef __cplusplus
/* Public procedures */
-/* Initialize the collector. This is only required when using thread-local
- * allocation, since unlike the regular allocation routines, GC_local_malloc
- * is not self-initializing. If you use GC_local_malloc you should arrange
- * to call this somehow (e.g. from a constructor) before doing any allocation.
- * For win32 threads, it needs to be called explicitly.
+/* Initialize the collector. Portable clients should call GC_INIT() from
+ * the main program instead.
*/
GC_API void GC_init(void);
#if defined(__linux__) || defined(__GLIBC__)
# include <features.h>
# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \
- && !defined(__ia64__)
+ && !defined(__ia64__) && !defined(__UCLIBC__)
# ifndef GC_HAVE_BUILTIN_BACKTRACE
/* # define GC_HAVE_BUILTIN_BACKTRACE */
# endif
/* be allowed here, instead of just clearing a pointer. */
/* But this causes problems if that action alters, or */
/* examines connectivity. */
- /* Returns 1 if link was already registered, 0 */
- /* otherwise. */
+ /* Returns 1 if link was already registered, 0 if */
+ /* registration succeeded, 2 if it failed for lack of */
+ /* memory, and GC_oom_fn did not handle the problem. */
/* Only exists for backward compatibility. See below: */
GC_API int GC_general_register_disappearing_link (void * * link, void * obj);
/* somewhere in the GC_call_with_stack_base frame. This often can */
/* be used to provide a sufficiently accurate stack base. And we */
/* implement it everywhere. */
-void * GC_call_with_stack_base(GC_stack_base_func fn, void *arg);
+GC_API void * GC_call_with_stack_base(GC_stack_base_func fn, void *arg);
/* Register the current thread, with the indicated stack base, as */
/* a new thread whose stack(s) should be traced by the GC. If a */
#define GC_DUPLICATE 1 /* Was already registered. */
#define GC_NO_THREADS 2 /* No thread support in GC. */
#define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform. */
-int GC_register_my_thread(struct GC_stack_base *);
+GC_API int GC_register_my_thread(struct GC_stack_base *);
/* Unregister the current thread. The thread may no longer allocate */
/* garbage collected memory or manipulate pointers to the */
/* pointer to the garbage-collected heap to another thread, it must */
/* do this before calling GC_unregister_my_thread, most probably */
/* by saving it in a global data structure. */
-int GC_unregister_my_thread(void);
+GC_API int GC_unregister_my_thread(void);
/* Attempt to fill in the GC_stack_base structure with the stack base */
/* for this thread. This appears to be required to implement anything */
/* threads are not automatically registered with the collector. */
/* It is also unfortunately hard to implement well on many platforms. */
/* Returns GC_SUCCESS or GC_UNIMPLEMENTED. */
-int GC_get_stack_base(struct GC_stack_base *);
+GC_API int GC_get_stack_base(struct GC_stack_base *);
/* The following routines are primarily intended for use with a */
/* preprocessor which inserts calls to check C pointer arithmetic. */
# define GC_PTR_STORE(p, q) \
(*(void **)GC_is_visible(p) = GC_is_valid_displacement(q))
#else /* !GC_DEBUG */
-# define GC_PTR_STORE(p, q) *((p) = (q))
+# define GC_PTR_STORE(p, q) (*(p) = (q))
#endif
/* Functions called to report pointer checking errors */
/* For pthread support, we generally need to intercept a number of */
/* thread library calls. We do that here by macro defining them. */
-#if !defined(GC_USE_LD_WRAP) && \
- (defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS))
+#if !defined(GC_USE_LD_WRAP) && !defined(GC_NO_THREAD_REDIRECTS) \
+ && defined(GC_PTHREADS)
# include "gc_pthread_redirects.h"
#endif
void * GC_malloc_many(size_t lb);
#define GC_NEXT(p) (*(void * *)(p)) /* Retrieve the next element */
/* in returned list. */
-extern void GC_thr_init(void); /* Needed for Solaris/X86 ?? */
#endif /* THREADS */
} /* Including windows.h in an extern "C" context no longer works. */
#endif
+#ifndef GC_NO_THREAD_DECLS
# include <windows.h>
#ifdef __cplusplus
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
+# if defined(_MSC_VER) && _MSC_VER >= 1200 && !defined(_UINTPTR_T_DEFINED)
+ typedef unsigned long uintptr_t;
+# endif
GC_API uintptr_t GC_beginthreadex(
void *security, unsigned stack_size,
# define WinMain GC_WinMain
# endif
# endif /* defined(_WIN32_WCE) */
+#endif /* !GC_NO_THREAD_DECLS */
/*
* Use implicit thread registration via DllMain.
+ * Must be called before GC_INIT and other GC routines.
+ * Should be avoided if GC_beginthreadex and friends can be called
+ * instead.
*/
GC_API void GC_use_DllMain(void);
-# define CreateThread GC_CreateThread
-# define ExitThread GC_ExitThread
-# define _beginthreadex GC_beginthreadex
-# define _endthreadex GC_endthreadex
-# define _beginthread { > "Please use _beginthreadex instead of _beginthread" < }
+# ifndef GC_NO_THREAD_REDIRECTS
+# define CreateThread GC_CreateThread
+# define ExitThread GC_ExitThread
+# define _beginthreadex GC_beginthreadex
+# define _endthreadex GC_endthreadex
+# define _beginthread { > "Please use _beginthreadex instead of _beginthread" < }
+# endif /* !GC_NO_THREAD_REDIRECTS */
#endif /* defined(GC_WIN32_THREADS) && !cygwin */
* no-op and the collector self-initializes. But a number of platforms
* make that too hard.
* A GC_INIT call is required if the collector is built with THREAD_LOCAL_ALLOC
- * defined and the initial allocation call is not to GC_malloc().
+ * defined and the initial allocation call is not to GC_malloc() or
+ * GC_malloc_atomic().
*/
#if defined(__CYGWIN32__) || defined (_AIX)
/*
# define GC_MIN(x,y) ((x) < (y) ? (x) : (y))
# define GC_DATASTART ((void *) GC_MIN(_data_start__, _bss_start__))
# define GC_DATAEND ((void *) GC_MAX(_data_end__, _bss_end__))
-# if defined(GC_DLL)
-# define GC_INIT() { GC_add_roots(GC_DATASTART, GC_DATAEND); \
+# define GC_INIT() { GC_add_roots(GC_DATASTART, GC_DATAEND); \
GC_gcollect(); /* For blacklisting. */}
-# else
- /* Main program init not required */
-# define GC_INIT() { GC_init(); }
-# endif
+ /* Required at least if GC is in dll. And doesn't hurt. */
# endif
# if defined(_AIX)
extern int _data[], _end[];
# include "gc_amiga_redirects.h"
#endif
-#if defined(GC_REDIRECT_TO_LOCAL) && !defined(GC_LOCAL_ALLOC_H)
-# include "gc_local_alloc.h"
+#if defined(GC_REDIRECT_TO_LOCAL)
+ /* Now redundant; that's the default with THREAD_LOCAL_ALLOC */
#endif
#ifdef __cplusplus
/* Must be redefined here, since the other overloadings */
/* hide the global definition. */
inline void operator delete( void* obj );
-# ifdef GC_PLACEMENT_DELETE
+# ifdef GC_PLACEMENT_DELETE
+ inline void operator delete( void*, GCPlacement );
+ /* called if construction fails. */
inline void operator delete( void*, void* );
# endif
inline void* operator new[]( size_t size, void *p );
inline void operator delete[]( void* obj );
# ifdef GC_PLACEMENT_DELETE
+ inline void operator delete[]( void*, GCPlacement );
inline void operator delete[]( void*, void* );
# endif
#endif /* GC_OPERATOR_NEW_ARRAY */
classes derived from "gc_cleanup" or containing members derived
from "gc_cleanup". */
+# ifdef GC_PLACEMENT_DELETE
+ inline void operator delete( void*, GCPlacement, GCCleanUpFunc, void * );
+# endif
#ifdef _MSC_VER
/** This ensures that the system default operator new[] doesn't get
* undefined, which is what seems to happen on VC++ 6 for some reason
* if we define a multi-argument operator new[].
- * There seems to be really redirect new in this environment without
+ * There seems to be no way to redirect new in this environment without
* including this everywhere.
*/
void *operator new[]( size_t size );
#ifdef GC_PLACEMENT_DELETE
inline void gc::operator delete( void*, void* ) {}
+
+ inline void gc::operator delete( void* p, GCPlacement gcp ) {
+ GC_FREE(p);
+ }
#endif
#ifdef GC_OPERATOR_NEW_ARRAY
#ifdef GC_PLACEMENT_DELETE
inline void gc::operator delete[]( void*, void* ) {}
+
+ inline void gc::operator delete[]( void* p, GCPlacement gcp ) {
+ gc::operator delete(p); }
+
#endif
#endif /* GC_OPERATOR_NEW_ARRAY */
obj = GC_MALLOC_UNCOLLECTABLE( size );};
return obj;}
+# ifdef GC_PLACEMENT_DELETE
+inline void operator delete (
+ void *p,
+ GCPlacement gcp,
+ GCCleanUpFunc cleanup,
+ void* clientData )
+{
+ GC_FREE(p);
+}
+# endif
#ifdef GC_OPERATOR_NEW_ARRAY
#endif /* __GNUC__ */
/* The ultimately general inline allocation macro. Allocate an object */
-/* of size bytes, putting the resulting pointer in result. Tiny_fl is */
-/* a "tiny" free list array, which will be used first, if the size */
-/* is appropriate. If bytes is too large, we allocate with */
+/* of size granules, putting the resulting pointer in result. Tiny_fl */
+/* is a "tiny" free list array, which will be used first, if the size */
+/* is appropriate. If granules is too large, we allocate with */
/* default_expr instead. If we need to refill the free list, we use */
/* GC_generic_malloc_many with the indicated kind. */
/* Tiny_fl should be an array of GC_TINY_FREELISTS void * pointers. */
/* be initialized to (void *)0. */
/* We rely on much of this hopefully getting optimized away in the */
/* num_direct = 0 case. */
-/* Particularly if bytes is constant, this should generate a small */
+/* Particularly if granules is constant, this should generate a small */
/* amount of code. */
# define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct,\
kind,default_expr,init) \
/* Entry contains counter or NULL */ \
if ((GC_word)my_entry - 1 < num_direct) { \
/* Small counter value, not NULL */ \
- *my_fl = (ptr_t)my_entry + granules + 1; \
+ *my_fl = (char *)my_entry + granules + 1; \
result = default_expr; \
goto out; \
} else { \
/* Large counter or NULL */ \
GC_generic_malloc_many(((granules) == 0? GC_GRANULE_BYTES : \
- RAW_BYTES_FROM_INDEX(granules)), \
+ GC_RAW_BYTES_FROM_INDEX(granules)), \
kind, my_fl); \
my_entry = *my_fl; \
if (my_entry == 0) { \
- result = GC_oom_fn(bytes); \
+ result = GC_oom_fn(granules*GC_GRANULE_BYTES); \
goto out; \
} \
} \
*my_fl = next; \
init; \
PREFETCH_FOR_WRITE(next); \
- GC_ASSERT(GC_size(result) >= bytes + EXTRA_BYTES); \
+ GC_ASSERT(GC_size(result) >= granules*GC_GRANULE_BYTES); \
GC_ASSERT((kind) == PTRFREE || ((GC_word *)result)[1] == 0); \
out: ; \
} \
/* a global array. */
# define GC_MALLOC_WORDS(result,n,tiny_fl) \
{ \
- size_t grans = WORDS_TO_WHOLE_GRANULES(n); \
+ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
- NORMAL, GC_malloc(grans*GRANULE_BYTES), \
+ NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
*(void **)result = 0); \
}
# define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \
{ \
- size_t grans = WORDS_TO_WHOLE_GRANULES(n); \
+ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
- PTRFREE, GC_malloc_atomic(grans*GRANULE_BYTES), \
+ PTRFREE, GC_malloc_atomic(grans*GC_GRANULE_BYTES), \
/* no initialization */); \
}
/* And once more for two word initialized objects: */
# define GC_CONS(result, first, second, tiny_fl) \
{ \
- size_t grans = WORDS_TO_WHOLE_GRANULES(2); \
+ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(2); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
- NORMAL, GC_malloc(grans*GRANULE_BYTES), \
+ NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
*(void **)result = (void *)(first)); \
((void **)(result))[1] = (void *)(second); \
}
*/
/*
- * We always set GRANULE_BYTES to twice the length of a pointer.
+ * We always set GC_GRANULE_BYTES to twice the length of a pointer.
* This means that all allocation requests are rounded up to the next
* multiple of 16 on 64-bit architectures or 8 on 32-bit architectures.
* This appears to be a reasonable compromise between fragmentation overhead
#if GC_GRANULE_WORDS == 2
# define GC_WORDS_TO_GRANULES(n) ((n)>>1)
#else
-# define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GRANULE_BYTES)
+# define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GC_GRANULE_BYTES)
#endif
/* A "tiny" free list header contains TINY_FREELISTS pointers to */
# endif
#endif /* !GC_TINY_FREELISTS */
-/* The ith free list corresponds to size i*GRANULE_BYTES */
+/* The ith free list corresponds to size i*GC_GRANULE_BYTES */
/* Internally to the collector, the index can be computed with */
/* ROUNDED_UP_GRANULES. Externally, we don't know whether */
/* DONT_ADD_BYTE_AT_END is set, but the client should know. */
/* Convert a free list index to the actual size of objects */
/* on that list, including extra space we added. Not an */
/* inverse of the above. */
-#define RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES)
+#define GC_RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES)
#endif /* GC_TINY_FL_H */
include/gc_amiga_redirects.h \
include/gc_pthread_redirects.h \
include/gc_config_macros.h \
- include/gc_tiny_fl.h
+ include/gc_tiny_fl.h \
+ include/gc_version.h
# headers which are not installed
#
include/cord.h \
include/ec.h \
include/javaxfc.h
+
+# unprefixed header
+include_HEADERS += \
+ include/extra/gc.h \
+ include/extra/gc_cpp.h
extern PCR_Th_ML GC_allocate_ml;
# define DCL_LOCK_STATE \
PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask
-# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
-# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
+# define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)
+# define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
# endif
# if !defined(AO_HAVE_test_and_set_acquire) && defined(GC_PTHREADS)
# undef NUMERIC_THREAD_ID_UNIQUE
# endif
# endif
-# define NO_THREAD (-1l)
+# define NO_THREAD ((unsigned long)(-1l))
/* != NUMERIC_THREAD_ID(pthread_self()) for any thread */
# if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS)
#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8)
typedef struct GC_ms_entry {
- ptr_t mse_start; /* First word of object */
+ ptr_t mse_start; /* First word of object, word aligned */
GC_word mse_descr; /* Descriptor; low order two bits are tags, */
- /* identifying the upper 30 bits as one of the */
- /* following: */
+ /* as described in gc_mark.h. */
} mse;
extern size_t GC_mark_stack_size;
/* push the contents of the object on the mark stack. Current points */
/* to the bginning of the object. We rely on the fact that the */
/* preceding header calculation will succeed for a pointer past the */
-/* forst page of an object, only if it is in fact a valid pointer */
+/* first page of an object, only if it is in fact a valid pointer */
/* to the object. Thus we can omit the otherwise necessary tests */
-/* here. Note in particular tha the "displ" value is the displacement */
-/* from the beggining of the heap block, which may itself be in the */
+/* here. Note in particular that the "displ" value is the displacement */
+/* from the beginning of the heap block, which may itself be in the */
/* interior of a large object. */
#ifdef MARK_BIT_PER_GRANULE
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT
# define GC_MACH_HEADER mach_header
# define GC_MACH_SECTION section
+# define GC_GETSECTBYNAME getsectbynamefromheader
# else
# define GC_THREAD_STATE_T ppc_thread_state64_t
# define GC_MACH_THREAD_STATE PPC_THREAD_STATE64
# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT
# define GC_MACH_HEADER mach_header_64
# define GC_MACH_SECTION section_64
+# define GC_GETSECTBYNAME getsectbynamefromheader_64
# endif
# elif defined(I386) || defined(X86_64)
# if CPP_WORDSZ == 32
# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
# define GC_MACH_HEADER mach_header
# define GC_MACH_SECTION section
+# define GC_GETSECTBYNAME getsectbynamefromheader
# else
# define GC_THREAD_STATE_T x86_thread_state64_t
# define GC_MACH_THREAD_STATE x86_THREAD_STATE64
# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
# define GC_MACH_HEADER mach_header_64
# define GC_MACH_SECTION section_64
+# define GC_GETSECTBYNAME getsectbynamefromheader_64
# endif
# else
# error define GC_THREAD_STATE_T
/* */
/*********************/
-/* heap block size, bytes. Should be power of 2 */
+/* Heap block size, bytes. Should be power of 2. */
+/* Incremental GC with MPROTECT_VDB currently requires the */
+/* page size to be a multiple of HBLKSIZE. Since most modern */
+/* architectures support variable page sizes down to 4K, and */
+/* X86 is generally 4K, we now default to 4K, except for */
+/* Alpha: Seems to be used with 8K pages. */
+/* SMALL_CONFIG: Want less block-level fragmentation. */
#ifndef HBLKSIZE
# ifdef SMALL_CONFIG
# define CPP_LOG_HBLKSIZE 10
# else
-# if (CPP_WORDSZ == 32) || (defined(HPUX) && defined(HP_PA))
- /* HPUX/PA seems to use 4K pages with the 64 bit ABI */
-# define CPP_LOG_HBLKSIZE 12
-# else
+# if defined(ALPHA)
# define CPP_LOG_HBLKSIZE 13
+# else
+# define CPP_LOG_HBLKSIZE 12
# endif
# endif
#else
# endif
# undef HBLKSIZE
#endif
+
# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE)
# define LOG_HBLKSIZE ((size_t)CPP_LOG_HBLKSIZE)
# define HBLKSIZE ((size_t)CPP_HBLKSIZE)
*/
# ifdef LARGE_CONFIG
-# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */
+# if CPP_WORDSZ == 32
+# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */
/* which is >= 4GB. Each table takes */
/* 128KB, some of which may never be */
/* touched. */
+# else
+# define LOG_PHT_ENTRIES 21 /* Collisions likely at 2M blocks, */
+ /* which is >= 8GB. Each table takes */
+ /* 256KB, some of which may never be */
+ /* touched. */
+# endif
# else
# ifdef SMALL_CONFIG
-# define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */
- /* to more than 16K hblks = 64MB. */
- /* Each hash table occupies 2K bytes. */
+# define LOG_PHT_ENTRIES 15 /* Collisions are likely if heap grows */
+ /* to more than 32K hblks = 128MB. */
+ /* Each hash table occupies 4K bytes. */
# else /* default "medium" configuration */
-# define LOG_PHT_ENTRIES 16 /* Collisions are likely if heap grows */
- /* to more than 64K hblks >= 256MB. */
- /* Each hash table occupies 8K bytes. */
+# define LOG_PHT_ENTRIES 18 /* Collisions are likely if heap grows */
+ /* to more than 256K hblks >= 1GB. */
+ /* Each hash table occupies 32K bytes. */
/* Even for somewhat smaller heaps, */
/* say half that, collisions may be an */
/* issue because we blacklist */
/* changed. */
size_t hb_sz; /* If in use, size in bytes, of objects in the block. */
/* if free, the size in bytes of the whole block */
+ /* We assume that this is convertible to signed_word */
+ /* without generating a negative result. We avoid */
+ /* generating free blocks larger than that. */
word hb_descr; /* object descriptor for marking. See */
/* mark.h. */
# ifdef MARK_BIT_PER_OBJ
/* MAX_ROOT_SETS is the maximum number of ranges that can be */
/* registered as static roots. */
# ifdef LARGE_CONFIG
-# define MAX_ROOT_SETS 4096
+# define MAX_ROOT_SETS 8192
# else
- /* GCJ LOCAL: MAX_ROOT_SETS increased to permit more shared */
- /* libraries to be loaded. */
-# define MAX_ROOT_SETS 1024
+# ifdef SMALL_CONFIG
+# define MAX_ROOT_SETS 512
+# else
+# define MAX_ROOT_SETS 2048
+# endif
# endif
# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4)
word _bytes_allocd;
/* Number of words allocated during this collection cycle */
# endif
+ word _bytes_dropped;
+ /* Number of black-listed bytes dropped during GC cycle */
+ /* as a result of repeated scanning during allocation */
+ /* attempts. These are treated largely as allocated, */
+ /* even though they are not useful to the client. */
word _bytes_finalized;
/* Approximate number of bytes in objects (and headers) */
/* That became ready for finalization in the last */
# endif
struct HeapSect {
ptr_t hs_start; size_t hs_bytes;
- } _heap_sects[MAX_HEAP_SECTS];
+ } _heap_sects[MAX_HEAP_SECTS]; /* Heap segments potentially */
+ /* client objects. */
+# if defined(USE_PROC_FOR_LIBRARIES)
+ struct HeapSect _our_memory[MAX_HEAP_SECTS];
+ /* All GET_MEM allocated */
+ /* memory. Includes block */
+ /* headers and the like. */
+# endif
# if defined(MSWIN32) || defined(MSWINCE)
ptr_t _heap_bases[MAX_HEAP_SECTS];
/* Start address of memory regions obtained from kernel. */
# define GC_large_free_bytes GC_arrays._large_free_bytes
# define GC_large_allocd_bytes GC_arrays._large_allocd_bytes
# define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes
+# define GC_bytes_dropped GC_arrays._bytes_dropped
# define GC_bytes_finalized GC_arrays._bytes_finalized
# define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc
# define GC_bytes_freed GC_arrays._bytes_freed
# define GC_requested_heapsize GC_arrays._requested_heapsize
# define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc
# define GC_heap_sects GC_arrays._heap_sects
+# ifdef USE_PROC_FOR_LIBRARIES
+# define GC_our_memory GC_arrays._our_memory
+# endif
# define GC_last_stack GC_arrays._last_stack
#ifdef ENABLE_TRACE
#define GC_trace_addr GC_arrays._trace_addr
extern word GC_n_heap_sects; /* Number of separately added heap */
/* sections. */
+#ifdef USE_PROC_FOR_LIBRARIES
+ extern word GC_n_memory; /* Number of GET_MEM allocated memory */
+ /* sections. */
+#endif
+
extern word GC_page_size;
# if defined(MSWIN32) || defined(MSWINCE)
# ifdef THREADS
extern void GC_push_thread_structures (void);
# endif
+ extern void (*GC_push_typed_structures) (void);
+ /* A pointer such that we can avoid linking in */
+ /* the typed allocation support if unused. */
extern void (*GC_start_call_back) (void);
/* Called at start of full collections. */
/* Not called if 0. Called with allocation */
void GC_add_to_heap(struct hblk *p, size_t bytes);
/* Add a HBLKSIZE aligned chunk to the heap. */
+
+#ifdef USE_PROC_FOR_LIBRARIES
+ void GC_add_to_our_memory(ptr_t p, size_t bytes);
+ /* Add a chunk to GC_our_memory. */
+ /* If p == 0, do nothing. */
+#else
+# define GC_add_to_our_memory(p, bytes)
+#endif
void GC_print_obj(ptr_t p);
/* P points to somewhere inside an object with */
# endif
# define mach_type_known
# endif
-# if defined(__ia64) && defined(_HPUX_SOURCE)
+# if defined(__ia64) && (defined(_HPUX_SOURCE) || defined(__HP_aCC))
# define IA64
# ifndef HPUX
# define HPUX
* If STACKBOTTOM is defined, then it's value will be used directly as the
* stack base. If LINUX_STACKBOTTOM is defined, then it will be determined
* with a method appropriate for most Linux systems. Currently we look
- * first for __libc_stack_end, and if that fails read it from /proc.
+ * first for __libc_stack_end (currently only id USE_LIBC_PRIVATES is
+ * defined), and if that fails read it from /proc. (If USE_LIBC_PRIVATES
+ * is not defined and NO_PROC_STAT is defined, we revert to HEURISTIC2.)
* If either of the last two macros are defined, then STACKBOTTOM is computed
* during collector startup using one of the following two heuristics:
* HEURISTIC1: Take an address inside GC_init's frame, and round it up to
# define DATAEND (_end)
extern int __data_start[];
# define DATASTART ((ptr_t)(__data_start))
-# define ALIGNMENT 4
+# ifdef _MIPS_SZPTR
+# define CPP_WORDSZ _MIPS_SZPTR
+# define ALIGNMENT (_MIPS_SZPTR/8)
+# else
+# define ALIGNMENT 4
+# endif
# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2
# define LINUX_STACKBOTTOM
# else
# endif
# endif
+#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \
+ && !defined(USE_LIBC_PRIVATES)
+ /* This combination will fail, since we have no way to get */
+ /* the stack base. Use HEURISTIC2 instead. */
+# undef LINUX_STACKBOTTOM
+# define HEURISTIC2
+ /* This may still fail on some architectures like IA64. */
+ /* We tried ... */
+#endif
+
#if defined(LINUX) && defined(USE_MMAP)
/* The kernel may do a somewhat better job merging mappings etc. */
/* with anonymous mappings. */
/* large. Sometimes we're lucky and the process just dies ... */
/* There seems to be a similar issue with some other memory */
/* allocated by the dynamic loader. */
- /* This can be avoided by either: */
+ /* This should be avoidable by either: */
/* - Defining USE_PROC_FOR_LIBRARIES here. */
/* That performs very poorly, precisely because we end up */
/* scanning cached stacks. */
- /* - Have calloc look at its callers. That is currently what we do.*/
+ /* - Have calloc look at its callers. */
/* In spite of the fact that it is gross and disgusting. */
-/* # define USE_PROC_FOR_LIBRARIES */
+ /* In fact neither seems to suffice, probably in part because */
+ /* even with USE_PROC_FOR_LIBRARIES, we don't scan parts of stack */
+ /* segments that appear to be out of bounds. Thus we actually */
+ /* do both, which seems to yield the best results. */
+
+# define USE_PROC_FOR_LIBRARIES
#endif
# ifndef STACK_GROWS_UP
# if defined(GC_NETBSD_THREADS) && !defined(NETBSD)
--> inconsistent configuration
# endif
+# if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD)
+ --> inconsistent configuration
+# endif
# if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS)
--> inconsistent configuration
# endif
/* We assume 0 == success, msft does the opposite. */
# define GC_key_create(key, d) \
((d) != 0? (ABORT("Destructor unsupported by TlsAlloc"),0) \
- : (*(key) = TlsAlloc(), 0))
+ : ((*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES? \
+ (ABORT("Out of tls"), 0): \
+ 0))
# define GC_remove_specific(key) /* No need for cleanup on thread exit. */
/* Need TlsFree on process exit/detach ? */
typedef DWORD GC_key_t;
+2008-02-11 Hans Boehm <Hans.Boehm@hp.com>
+ (Really Ian Wienand & Debian maintainers)
+ * src/atomic_ops/sysdeps/gcc/x86.h
+ (AO_compare_double_and_swap_double_full): Correctly account for
+ ebx usage with PIC.
+
+2008-01-09 Hans Boehm <Hans.Boehm@hp.com>
+ * src/atomic_ops/sysdeps/standard_ao_double_t.h: Let
+ double_ptr_storage default to long long; define everywhere.
+
+2008-01-08 Hans Boehm <Hans.Boehm@hp.com> (Really mostly Joerg Wagner)
+ * src/atomic_ops/sysdeps/msftc/x86.h: Conditionally add
+ compare_double_and_swap_double.
+
+2008-01-06 Hans Boehm <Hans.Boehm@hp.com> (Really mostly Joerg Wagner)
+ * src/atomic_ops/generalize.h: Add test_and_set generalizations,
+ Add AO_double_compare_and_swap generalizations.
+ * src/atomic_ops/sysdeps/armcc/arm_v6.h: New file.
+ * src/atomic_ops/sysdeps/gcc/arm.h: Handle V6 and V7.
+ * src/atomic_ops/sysdeps/gcc/x86.h,
+ src/atomic_ops/sysdeps/{gcc,msftc}/x86_64.h: Conditionally add
+ compare_double_and_swap_double, commented out for msftc.
+ * src/atomic_ops/sysdeps/standard_ao_double_t.h: Add
+ double_ptr_storage field.
+
+2008-01-03 Hans Boehm <Hans.Boehm@hp.com>
+ (Merge from separate atomic_ops tree)
+ * src/atomic_ops/sysdeps/gcc/x86.h: Define correct macro for
+ double-width cas, and fix its implementation.
+ * doc/README.txt: Clarify use of _full. Add more warnings about
+ data dependencies.
+
+2008-01-02 Hans Boehm <Hans.Boehm@hp.com>
+ * src/atomic_ops/sysdeps/gcc/powerpc.h (AO_load_acquire): Add
+ %X1 modifier to support indexed addressing.
+
+2007-07-23 Hans Boehm <Hans.Boehm@hp.com> (really Jim Marshall)
+ * src/atomic_ops/sysdeps/msftc/x86.h (_InterlockedExchangeAdd): Define
+ for VC++6.
+
+2007-07-05 Andreas Tobler <a.tobler@schweiz.org>
+ * src/atomic_ops.h: Check for __powerpc64__ and __ppc64__ to include
+ powerpc.h.
+
2007-06-26 Hans Boehm <Hans.Boehm@hp.com> (really Luca Barbato)
* src/atomic_ops/sysdeps/gcc/powerpc.h (AO_load_acquire): Add
64-bit version.
_write: Earlier writes become visible before writes during or after
the atomic operation. Rarely useful for clients?
_full: Ordered with respect to both earlier and later memops.
+ AO_store_full or AO_nop_full are the normal ways to force a store
+ to be ordered with respect to a later load.
_release_write: Ordered with respect to earlier writes. This is
normally implemented as either a _write or _release
barrier.
second value, with the expectation that the second
read is ordered after the first one. On most architectures,
this is equivalent to no barrier. (This is very
- hard to define precisely. It should probably be avoided.)
+ hard to define precisely. It should probably be avoided.
+ A major problem is that optimizers tend to try to
+ eliminate dependencies from the generated code, since
+ dependencies force the hardware to execute the code
+ serially.)
_release_read: Ordered with respect to earlier reads. Useful for
implementing read locks. Can be implemented as _release,
but not as _read, since _read groups the current operation
# if defined(__m68k__)
# include "atomic_ops/sysdeps/gcc/m68k.h"
# endif /* __m68k__ */
-# if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
+# if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) \
+ || defined(__powerpc64__) || defined(__ppc64__)
# include "atomic_ops/sysdeps/gcc/powerpc.h"
# endif /* __powerpc__ */
# if defined(__arm__) && !defined(AO_USE_PTHREAD_DEFS)
# define AO_HAVE_store_full
#endif
+/* NEC LE-IT: Test and set */
+#if defined(AO_HAVE_test_and_set) && \
+ defined(AO_HAVE_nop_full) && \
+ !defined(AO_HAVE_test_and_set_release)
+# define AO_test_and_set_release(addr) \
+ (AO_nop_full(), AO_test_and_set(addr))
+# define AO_HAVE_test_and_set_release
+#endif
+
+#if defined(AO_HAVE_test_and_set) && \
+ defined(AO_HAVE_nop_full) && \
+ !defined(AO_HAVE_test_and_set_acquire)
+AO_INLINE AO_TS_t
+AO_test_and_set_acquire(volatile AO_TS_t *addr)
+{
+ AO_TS_t res = AO_test_and_set(addr);
+ AO_nop_full();
+ return res;
+}
+# define AO_HAVE_test_and_set_acquire
+#endif
+
/* Fetch_and_add */
/* We first try to implement fetch_and_add variants in terms */
# define AO_HAVE_compare_and_swap_double_dd_acquire_read
# endif
#endif
+
+/* NEC LE-IT: Convenience functions for AO_double compare and swap which */
+/* types and reads easier in code */
+#if defined(AO_HAVE_compare_double_and_swap_double_release) && \
+ !defined(AO_HAVE_double_compare_and_swap_release)
+AO_INLINE int
+AO_double_compare_and_swap_release(volatile AO_double_t *addr,
+ AO_double_t old_val, AO_double_t new_val)
+{
+ return AO_compare_double_and_swap_double_release(addr,
+ old_val.AO_val1, old_val.AO_val2,
+ new_val.AO_val1, new_val.AO_val2);
+}
+#define AO_HAVE_double_compare_and_swap_release
+#endif
+
+#if defined(AO_HAVE_compare_double_and_swap_double_acquire) && \
+ !defined(AO_HAVE_double_compare_and_swap_acquire)
+AO_INLINE int
+AO_double_compare_and_swap_acquire(volatile AO_double_t *addr,
+ AO_double_t old_val, AO_double_t new_val)
+{
+ return AO_compare_double_and_swap_double_acquire(addr,
+ old_val.AO_val1, old_val.AO_val2,
+ new_val.AO_val1, new_val.AO_val2);
+}
+#define AO_HAVE_double_compare_and_swap_acquire
+#endif
+
+#if defined(AO_HAVE_compare_double_and_swap_double_full) && \
+ !defined(AO_HAVE_double_compare_and_swap_full)
+AO_INLINE int
+AO_double_compare_and_swap_full(volatile AO_double_t *addr,
+ AO_double_t old_val, AO_double_t new_val)
+{
+ return AO_compare_double_and_swap_double_full(addr,
+ old_val.AO_val1, old_val.AO_val2,
+ new_val.AO_val1, new_val.AO_val2);
+}
+#define AO_HAVE_double_compare_and_swap_full
+#endif
*
*/
-/* There exist multiprocessor SoC ARM processors, so this matters. */
-/* This needs to be augmented for later ARM (e.g. V7) procesors. */
+#include "../read_ordered.h"
+
+#include "../test_and_set_t_is_ao_t.h" /* Probably suboptimal */
+
+/* NEC LE-IT: ARMv6 is the first architecture providing support for simple LL/SC
+ * A data memory barrier must be raised via CP15 command (see documentation).
+ *
+ * ARMv7 is compatible to ARMv6 but has a simpler command for issuing a
+ * memory barrier (DMB). Raising it via CP15 should still work as told me by the
+ * support engineers. If it turns out to be much quicker than we should implement
+ * custom code for ARMv7 using the asm { dmb } command.
+ *
+ * If only a single processor is used, we can define AO_UNIPROCESSOR
+ * and do not need to access CP15 for ensuring a DMB
+*/
+
+/* NEC LE-IT: gcc has no way to easily check the arm architecture
+ * but defines only one of __ARM_ARCH_x__ to be true */
+#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_7__)
+AO_INLINE void
+AO_nop_full()
+{
+#ifndef AO_UNIPROCESSOR
+ /* issue an data memory barrier (keeps ordering of memory transactions */
+ /* before and after this operation) */
+ unsigned int dest=0;
+ __asm__ __volatile__("mcr p15,0,%0,c7,c10,5" :"=&r"(dest) : : "memory");
+#endif
+}
+
+#define AO_HAVE_nop_full
+
+/* NEC LE-IT: AO_t load is simple reading */
+AO_INLINE AO_t
+AO_load(volatile AO_t *addr)
+{
+ /* Cast away the volatile for architectures like IA64 where */
+ /* volatile adds barrier semantics. */
+ return (*(AO_t *)addr);
+}
+#define AO_HAVE_load
+
+/* NEC LE-IT: atomic "store" - according to ARM documentation this is
+ * the only safe way to set variables also used in LL/SC environment.
+ * A direct write won't be recognized by the LL/SC construct on the _same_ CPU.
+ * Support engineers response for behaviour of ARMv6:
+ *
+ Core1 Core2 SUCCESS
+ ===================================
+ LDREX(x)
+ STREX(x) Yes
+ -----------------------------------
+ LDREX(x)
+ STR(x)
+ STREX(x) No
+ -----------------------------------
+ LDREX(x)
+ STR(x)
+ STREX(x) Yes
+ -----------------------------------
+
+ * ARMv7 behaves similar, see documentation CortexA8 TRM, point 8.5
+ *
+ * HB: I think this is only a problem if interrupt handlers do not clear
+ * the reservation, as they almost certainly should. Probably change this back
+ * in a while?
+*/
+AO_INLINE void AO_store(volatile AO_t *addr, AO_t value)
+{
+ unsigned long tmp;
+
+ __asm__ __volatile__("@AO_store\n"
+"1: ldrex %0, [%1]\n"
+" strex %0, %2, [%1]\n"
+" teq %0, #0\n"
+" bne 1b"
+ : "=&r"(tmp)
+ : "r" (addr), "r"(value)
+ : "cc","memory");
+}
+#define AO_HAVE_store
+
+/* NEC LE-IT: replace the SWAP as recommended by ARM:
+
+ "Applies to: ARM11 Cores
+ Though the SWP instruction will still work with ARM V6 cores, it is
+ recommended to use the new V6 synchronization instructions. The SWP
+ instruction produces ‘locked’ read and write accesses which are atomic,
+ i.e. another operation cannot be done between these locked accesses which
+ ties up external bus (AHB,AXI) bandwidth and can increase worst case
+ interrupt latencies. LDREX,STREX are more flexible, other instructions can
+ be done between the LDREX and STREX accesses.
+ "
+*/
+AO_INLINE AO_TS_t
+AO_test_and_set(volatile AO_TS_t *addr) {
+
+ AO_TS_t oldval;
+ unsigned long tmp;
+
+ __asm__ __volatile__("@AO_test_and_set\n"
+"1: ldrex %0, [%2]\n"
+" strex %1, %3, [%2]\n"
+" teq %1, #0\n"
+" bne 1b\n"
+ : "=&r"(oldval),"=&r"(tmp)
+ : "r"(addr), "r"(1)
+ : "memory","cc");
+
+ return oldval;
+}
+
+#define AO_HAVE_test_and_set
+
+/* NEC LE-IT: fetch and add for ARMv6 */
+AO_INLINE AO_t
+AO_fetch_and_add(volatile AO_t *p, AO_t incr)
+{
+ unsigned long tmp,tmp2;
+ AO_t result;
+
+ __asm__ __volatile__("@AO_fetch_and_add\n"
+"1: ldrex %0, [%4]\n" /* get original */
+" add %2, %3, %0\n" /* sum up */
+" strex %1, %2, [%4]\n" /* store them */
+" teq %1, #0\n"
+" bne 1b\n"
+ : "=&r"(result),"=&r"(tmp),"=&r"(tmp2)
+ : "r"(incr), "r"(p)
+ : "cc","memory");
+
+ return result;
+}
+
+#define AO_HAVE_fetch_and_add
+
+/* NEC LE-IT: fetch and add1 for ARMv6 */
+AO_INLINE AO_t
+AO_fetch_and_add1(volatile AO_t *p)
+{
+ unsigned long tmp,tmp2;
+ AO_t result;
+
+ __asm__ __volatile__("@AO_fetch_and_add1\n"
+"1: ldrex %0, [%3]\n" /* get original */
+" add %1, %0, #1\n" /* increment */
+" strex %2, %1, [%3]\n" /* store them */
+" teq %2, #0\n"
+" bne 1b\n"
+ : "=&r"(result), "=&r"(tmp), "=&r"(tmp2)
+ : "r"(p)
+ : "cc","memory");
+
+ return result;
+}
+
+#define AO_HAVE_fetch_and_add1
+
+/* NEC LE-IT: fetch and sub for ARMv6 */
+AO_INLINE AO_t
+AO_fetch_and_sub1(volatile AO_t *p)
+{
+ unsigned long tmp,tmp2;
+ AO_t result;
+
+ __asm__ __volatile__("@ AO_fetch_and_sub1\n"
+"1: ldrex %0, [%3]\n" /* get original */
+" sub %1, %0, #1\n" /* increment */
+" strex %2, %1, [%3]\n" /* store them */
+" teq %2, #0\n"
+" bne 1b\n"
+ : "=&r"(result), "=&r"(tmp), "=&r"(tmp2)
+ : "r"(p)
+ : "cc","memory");
+
+ return result;
+}
+
+#define AO_HAVE_fetch_and_sub1
+/* NEC LE-IT: compare and swap */
+/* Returns nonzero if the comparison succeeded. */
+AO_INLINE int
+AO_compare_and_swap(volatile AO_t *addr,
+ AO_t old_val, AO_t new_val)
+{
+ AO_t result,tmp;
+
+ __asm__ __volatile__("@ AO_compare_and_swap\n"
+"1: ldrex %1, [%2]\n" /* get original */
+" mov %0, #2\n" /* store a flag */
+" teq %1, %3\n" /* see if match */
+" strexeq %0, %4, [%2]\n" /* store new one if matched */
+" teq %0, #1\n"
+" beq 1b\n" /* if update failed, repeat */
+" eor %0, %0, #2\n" /* if succeded, return 2, else 0 */
+ : "=&r"(result), "=&r"(tmp)
+ : "r"(addr), "r"(old_val), "r"(new_val)
+ : "cc","memory");
+
+ return (result>>1);
+}
+#define AO_HAVE_compare_and_swap
+
+#else
+/* pre ARMv6 architecures ... */
+
/* I found a slide set that, if I read it correctly, claims that */
/* Loads followed by either a Load or Store are ordered, but nothing */
/* else is. */
/* It appears that SWP is the only simple memory barrier. */
#include "../all_atomic_load_store.h"
-#include "../read_ordered.h"
-
-#include "../test_and_set_t_is_ao_t.h" /* Probably suboptimal */
-
-
AO_INLINE AO_TS_VAL_t
AO_test_and_set_full(volatile AO_TS_t *addr) {
AO_TS_VAL_t oldval;
#define AO_HAVE_test_and_set_full
-
-
+#endif // __ARM_ARCH_x
/* registers. I always got "impossible constraint" when I */
/* tried the "y" constraint. */
__asm__ __volatile__ (
- "lwz %0,%1\n"
+ "lwz%X1 %0,%1\n"
"cmpw cr7,%0,%0\n"
"bne- cr7,1f\n"
"1: isync\n"
AO_t new_val1, AO_t new_val2)
{
char result;
- __asm__ __volatile__("lock; cmpxchg8b %0; setz %1"
+ #if __PIC__
+ /* If PIC is turned on, we can't use %ebx as it is reserved for the
+ GOT poiner. We can save and restore %ebx because GCC won't be
+ using it for anything else (such as any of the m operands) */
+ __asm__ __volatile__("pushl %%ebx;" /* save ebx used for PIC GOT ptr */
+ "movl %6,%%ebx;" /* move new_val2 to %ebx */
+ "lock; cmpxchg8b %0; setz %1;"
+ "pop %%ebx;" /* restore %ebx */
+ : "=m"(*addr), "=q"(result)
+ : "m"(*addr), "d" (old_val2), "a" (old_val1),
+ "c" (new_val2), "m" (new_val1) : "memory");
+ #else
+ /* We can't just do the same thing in non-PIC mode, because GCC
+ * might be using %ebx as the memory operand. We could have ifdef'd
+ * in a clobber, but there's no point doing the push/pop if we don't
+ * have to. */
+ __asm__ __volatile__("lock; cmpxchg8b %0; setz %1;"
: "=m"(*addr), "=q"(result)
- : "m"(*addr), "d" (old_val1), "a" (old_val2),
- "c" (new_val1), "b" (new_val2) : "memory");
+ : "m"(*addr), "d" (old_val2), "a" (old_val1),
+ "c" (new_val2), "b" (new_val1) : "memory");
+ #endif
return (int) result;
}
-#define AO_HAVE_double_compare_and_swap_full
+#define AO_HAVE_compare_double_and_swap_double_full
#include "../ao_t_is_int.h"
#include "../test_and_set_t_is_char.h"
+#include "../standard_ao_double_t.h"
+
#if defined(AO_USE_PENTIUM4_INSTRS)
AO_INLINE void
AO_nop_full()
/* Returns nonzero if the comparison succeeded. */
AO_INLINE int
AO_compare_and_swap_full(volatile AO_t *addr,
- AO_t old, AO_t new_val)
+ AO_t old, AO_t new_val)
{
char result;
__asm__ __volatile__("lock; cmpxchgq %3, %0; setz %1"
#define AO_HAVE_compare_and_swap_full
-/* FIXME: The Intel version has a 16byte CAS instruction. */
+#ifdef AO_CMPXCHG16B_AVAILABLE
+/* NEC LE-IT: older AMD Opterons are missing this instruction.
+ * On these machines SIGILL will be thrown. Define AO_CASDOUBLE_MISSING
+ * to have an emulated (lock based) version available */
+/* HB: Changed this to not define either by default. There are
+ * enough machines and tool chains around on which cmpxchg16b
+ * doesn't work. And the emulation is unsafe by our usual rules.
+ * Hoewever both are clearly useful in certain cases.
+ */
+AO_INLINE int
+AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
+ AO_t old_val1, AO_t old_val2,
+ AO_t new_val1, AO_t new_val2)
+{
+ char result;
+ __asm__ __volatile__("lock; cmpxchg16b %0; setz %1"
+ : "=m"(*addr), "=q"(result)
+ : "m"(*addr),
+ "d" (old_val1),
+ "a" (old_val2),
+ "c" (new_val1),
+ "b" (new_val2) : "memory");
+ return (int) result;
+}
+#define AO_HAVE_compare_double_and_swap_double_full
+#else
+/* this one provides spinlock based emulation of CAS implemented in */
+/* atomic_ops.c. We probably do not want to do this here, since it is */
+/* not attomic with respect to other kinds of updates of *addr. On the */
+/* other hand, this may be a useful facility on occasion. */
+#ifdef AO_WEAK_DOUBLE_CAS_EMULATION
+int AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr,
+ AO_t old_val1, AO_t old_val2,
+ AO_t new_val1, AO_t new_val2);
+
+AO_INLINE int
+AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
+ AO_t old_val1, AO_t old_val2,
+ AO_t new_val1, AO_t new_val2)
+{
+ return AO_compare_double_and_swap_double_emulation(addr,
+ old_val1, old_val2,
+ new_val1, new_val2);
+}
+#define AO_HAVE_compare_double_and_swap_double_full
+#endif /* AO_WEAK_DOUBLE_CAS_EMULATION */
+#endif /* AO_CMPXCHG16B_AVAILABLE */
* SOFTWARE.
*/
-/* The following really assume we have a 486 or better. */
+/* The following really assume we have a 486 or better. */
/* If ASSUME_WINDOWS98 is defined, we assume Windows 98 or newer. */
+/* If ASSUME_VISTA is defined, we assume Windows Server 2003, Vista */
+/* or later. */
#include "../all_aligned_atomic_load_store.h"
#define _InterlockedIncrement InterlockedIncrement
#define _InterlockedDecrement InterlockedDecrement
#define _InterlockedExchange InterlockedExchange
+#define _InterlockedExchangeAdd InterlockedExchangeAdd
#define _InterlockedCompareExchange InterlockedCompareExchange
#else
# error wrong architecture
#endif
+#ifdef ASSUME_VISTA
+/* NEC LE-IT: whenever we run on a pentium class machine we have that
+ * certain function */
+
+#include "../standard_ao_double_t.h"
+#pragma intrinsic (_InterlockedCompareExchange64)
+/* Returns nonzero if the comparison succeeded. */
+AO_INLINE int
+AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
+ AO_t old_val1, AO_t old_val2,
+ AO_t new_val1, AO_t new_val2)
+{
+ __int64 oldv = (__int64)old_val2 | ((__int64)old_val1 << 32);
+ __int64 newv = (__int64)new_val2 | ((__int64)new_val1 << 32);
+ return _InterlockedCompareExchange64((__int64 volatile *)addr,
+ newv, oldv) == oldv;
+}
+#define AO_HAVE_compare_double_and_swap_double_full
+
+#ifdef __cplusplus
+AO_INLINE int
+AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
+ AO_double_t old_val,
+ AO_double_t new_val)
+{
+ return _InterlockedCompareExchange64((__int64 volatile *)addr,
+ new_val.AO_whole, old_val.AO_whole) == old_val.AO_whole;
+}
+#define AO_HAVE_double_compare_and_swap_full
+#endif // __cplusplus
+#endif /* ASSUME_VISTA */
+
#include "../ao_t_is_int.h"
}
#define AO_HAVE_test_and_set_full
-#endif
+
+FIXME: (__asm not supported)
+NEC LE-IT: Don't have a working Win64 environment here at the moment.
+AO_compare_double_and_swap_double_full needs implementation for Win64
+But there is no _InterlockedCompareExchange128 in the WinAPI, so we
+need basically whats given below.
+Also see gcc/x86_64.h for partial old opteron workaround:
+
+#ifndef AO_CASDOUBLE_MISSING
+
+AO_INLINE int
+AO_compare_double_and_swap_double_full(volatile AO_double_t *addr,
+ AO_t old_val1, AO_t old_val2,
+ AO_t new_val1, AO_t new_val2)
+{
+ char result;
+ __asm
+ {
+ mov rdx,QWORD PTR [old_val]
+ mov rax,QWORD PTR [old_val + 8]
+ mov rcx,QWORD PTR [new_val]
+ mov rbx,QWORD PTR [new_val + 8]
+ lock cmpxchg16b [addr]
+ setz result;
+ }
+ return result;
+}
+#endif // AO_CASDOUBLE_MISSING
+#define AO_HAVE_compare_double_and_swap_double_full
+
+#endif /* 0 */
+/* NEC LE-IT: For 64Bit OS we extend the double type to hold two int64's
+*
+* x86-64: __m128 serves as placeholder which also requires the compiler
+* to align it on 16 byte boundary (as required by cmpxchg16.
+* Similar things could be done for PowerPC 64bit using a VMX data type... */
+
+#if defined(__GNUC__)
+# if defined(__x86_64__)
+# include<xmmintrin.h>
+ typedef __m128 double_ptr_storage;
+# define AO_HAVE_DOUBLE_PTR_STORAGE
+# endif /* __x86_64__ */
+#endif
+
+#ifdef _MSC_VER
+# ifdef _WIN64
+ typedef __m128 double_ptr_storage;
+# define AO_HAVE_DOUBLE_PTR_STORAGE
+# elif _WIN32
+ typedef unsigned __int64 double_ptr_storage;
+# define AO_HAVE_DOUBLE_PTR_STORAGE
+# endif
+#endif
+
+#ifndef AO_HAVE_DOUBLE_PTR_STORAGE
+ typedef unsigned long long double_ptr_storage;
+#endif
+
typedef union {
- unsigned long long AO_whole;
+ double_ptr_storage AO_whole;
struct {AO_t AO_v1; AO_t AO_v2;} AO_parts;
} AO_double_t;
+
#define AO_HAVE_double_t
#define AO_val1 AO_parts.AO_v1
#define AO_val2 AO_parts.AO_v2
-
# if defined(HAVE_PUSH_REGS)
GC_push_regs();
-# elif defined(UNIX_LIKE) && !defined(DARWIN) && !defined(ARM32)
+# elif defined(UNIX_LIKE) && !defined(DARWIN) && !defined(ARM32) && \
+ !defined(HURD)
/* Older versions of Darwin seem to lack getcontext(). */
/* ARM Linux often doesn't support a real getcontext(). */
ucontext_t ctxt;
/* _setjmp won't, but is less portable. */
# endif
# endif /* !HAVE_PUSH_REGS ... */
+ /* FIXME: context here is sometimes just zero. At the moment the callees */
+ /* don't really need it. */
fn(arg, context);
/* Strongly discourage the compiler from treating the above */
/* as a tail-call, since that would pop the register */
return((void *)REDIRECT_MALLOC(lb));
}
-#ifdef GC_LINUX_THREADS
+#if defined(GC_LINUX_THREADS) /* && !defined(USE_PROC_FOR_LIBRARIES) */
static ptr_t GC_libpthread_start = 0;
static ptr_t GC_libpthread_end = 0;
static ptr_t GC_libld_start = 0;
void GC_init_lib_bounds(void)
{
if (GC_libpthread_start != 0) return;
- if (!GC_text_mapping("/lib/tls/libpthread-",
- &GC_libpthread_start, &GC_libpthread_end)
- && !GC_text_mapping("/lib/libpthread-",
- &GC_libpthread_start, &GC_libpthread_end)) {
+ if (!GC_text_mapping("libpthread-",
+ &GC_libpthread_start, &GC_libpthread_end)) {
WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0);
/* This might still work with some versions of libpthread, */
/* so we don't abort. Perhaps we should. */
/* Generate message only once: */
GC_libpthread_start = (ptr_t)1;
}
- if (!GC_text_mapping("/lib/ld-", &GC_libld_start, &GC_libld_end)) {
+ if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) {
WARN("Failed to find ld.so text mapping: Expect crash\n", 0);
}
}
void * calloc(size_t n, size_t lb)
{
-# if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES)
+# if defined(GC_LINUX_THREADS) /* && !defined(USE_PROC_FOR_LIBRARIES) */
/* libpthread allocated some memory that is only pointed to by */
/* mmapped thread stacks. Make sure it's not collectable. */
{
if (p == 0) return;
/* Required by ANSI. It's not my fault ... */
+# ifdef LOG_ALLOCS
+ GC_err_printf("GC_free(%p): %d\n", p, GC_gc_no);
+# endif
h = HBLKPTR(p);
hhdr = HDR(h);
sz = hhdr -> hb_sz;
ngranules = BYTES_TO_GRANULES(sz);
- GC_ASSERT(GC_base(p) == p);
# if defined(REDIRECT_MALLOC) && \
(defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \
|| defined(MSWIN32))
/* Don't try to deallocate that memory. */
if (0 == hhdr) return;
# endif
+ GC_ASSERT(GC_base(p) == p);
knd = hhdr -> hb_obj_kind;
ok = &GC_obj_kinds[knd];
if (EXPECT((ngranules <= MAXOBJGRANULES), 1)) {
*flh = (ptr_t)p;
UNLOCK();
} else {
+ size_t nblocks = OBJ_SZ_TO_BLOCKS(sz);
LOCK();
GC_bytes_freed += sz;
if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
+ if (nblocks > 1) {
+ GC_large_allocd_bytes -= nblocks * HBLKSIZE;
+ }
GC_freehblk(h);
UNLOCK();
}
obj_link(p) = *flh;
*flh = (ptr_t)p;
} else {
+ size_t nblocks = OBJ_SZ_TO_BLOCKS(sz);
GC_bytes_freed += sz;
if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= sz;
+ if (nblocks > 1) {
+ GC_large_allocd_bytes -= nblocks * HBLKSIZE;
+ }
GC_freehblk(h);
}
}
ptr_t caller = (ptr_t)__builtin_return_address(0);
/* This test does not need to ensure memory visibility, since */
/* the bounds will be set when/if we create another thread. */
- if (caller >= GC_libpthread_start && caller > GC_libpthread_end) {
+ if (caller >= GC_libpthread_start && caller < GC_libpthread_end
+ || (caller >= GC_libld_start && caller < GC_libld_end)) {
GC_free(p);
return;
}
continue;
}
descr = *(word *)(type_descr
- - (descr - (GC_DS_PER_OBJECT
- - GC_INDIR_PER_OBJ_BIAS)));
+ - (descr + (GC_INDIR_PER_OBJ_BIAS
+ - GC_DS_PER_OBJECT)));
}
if (0 == descr) {
/* Can happen either because we generated a 0 descriptor */
}
#endif /* SMALL_CONFIG */
-/* Similar to GC_push_next_marked, but return address of next block */
+/* Similar to GC_push_marked, but skip over unallocated blocks */
+/* and return address of next plausible block. */
struct hblk * GC_push_next_marked(struct hblk *h)
{
hdr * hhdr = HDR(h);
GC_remove_root_at_pos(i);
} else {
i++;
- }
+ }
}
#if !defined(MSWIN32) && !defined(MSWINCE)
GC_rebuild_root_index();
# endif /* !THREADS */
}
+void (*GC_push_typed_structures) (void) = NULL;
+
/*
* Push GC internal roots. Only called if there is some reason to believe
* these would not otherwise get registered.
# if defined(THREADS)
GC_push_thread_structures();
# endif
+ if( GC_push_typed_structures )
+ GC_push_typed_structures();
}
#ifdef THREAD_LOCAL_ALLOC
extern void GC_init_parallel(void);
# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
+/* FIXME: The GC_init/GC_init_inner distinction should go away. */
void GC_init(void)
{
- DCL_LOCK_STATE;
-
-#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
- if (!GC_is_initialized) {
- BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
- HMODULE hK32 = GetModuleHandleA("kernel32.dll");
- if (hK32)
- pfn = (BOOL (WINAPI *) (LPCRITICAL_SECTION, DWORD))
- GetProcAddress (hK32,
- "InitializeCriticalSectionAndSpinCount");
- if (pfn)
- pfn(&GC_allocate_ml, 4000);
- else
- InitializeCriticalSection (&GC_allocate_ml);
- }
-#endif /* MSWIN32 */
-
- LOCK();
+ /* LOCK(); -- no longer does anything this early. */
GC_init_inner();
- UNLOCK();
-
-# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
- /* Make sure marker threads and started and thread local */
- /* allocation is initialized, in case we didn't get */
- /* called from GC_init_parallel(); */
- {
- 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
+ /* UNLOCK(); */
}
#if defined(MSWIN32) || defined(MSWINCE)
extern void GC_setpagesize();
-
#ifdef MSWIN32
extern GC_bool GC_no_win32_dlls;
#else
#endif
+#if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)
+ void GC_thr_init(void);
+#endif
+
void GC_init_inner()
{
# if !defined(THREADS) && defined(GC_ASSERTIONS)
word initial_heap_sz = (word)MINHINCR;
if (GC_is_initialized) return;
+
+ /* Note that although we are nominally called with the */
+ /* allocation lock held, the allocation lock is now */
+ /* only really acquired once a second thread is forked.*/
+ /* And the initialization code needs to run before */
+ /* then. Thus we really don't hold any locks, and can */
+ /* in fact safely initialize them here. */
+# ifdef THREADS
+ GC_ASSERT(!GC_need_to_lock);
+# endif
+# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
+ if (!GC_is_initialized) {
+ BOOL (WINAPI *pfn) (LPCRITICAL_SECTION, DWORD) = NULL;
+ HMODULE hK32 = GetModuleHandleA("kernel32.dll");
+ if (hK32)
+ pfn = (BOOL (WINAPI *) (LPCRITICAL_SECTION, DWORD))
+ GetProcAddress (hK32,
+ "InitializeCriticalSectionAndSpinCount");
+ if (pfn)
+ pfn(&GC_allocate_ml, 4000);
+ else
+ InitializeCriticalSection (&GC_allocate_ml);
+ }
+#endif /* MSWIN32 */
# if defined(MSWIN32) || defined(MSWINCE)
InitializeCriticalSection(&GC_write_cs);
# endif
if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner();
# ifdef STUBBORN_ALLOC
GC_stubborn_init();
-# endif
-# if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC)
- {
- extern void GC_init_lib_bounds(void);
- GC_init_lib_bounds();
- }
# endif
/* Convince lint that some things are used */
# ifdef LINT
GC_register_finalizer_no_order);
}
# endif
+
+ /* The rest of this again assumes we don't really hold */
+ /* the allocation lock. */
+# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
+ /* Make sure marker threads and started and thread local */
+ /* allocation is initialized, in case we didn't get */
+ /* called from GC_init_parallel(); */
+ {
+ 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
}
void GC_enable_incremental(void)
return result;
}
-void * GC_call_with_stack_base(GC_stack_base_func fn, void *arg)
+GC_API void * GC_call_with_stack_base(GC_stack_base_func fn, void *arg)
{
int dummy;
struct GC_stack_base base;
// Save and restore current directory around SymLoadModule, see KB article Q189780
GetCurrentDirectoryA(sizeof(curDir), curDir);
GetModuleFileNameA(NULL, exePath, sizeof(exePath));
+#if defined(_MSC_VER) && _MSC_VER == 1200
+ /* use strcat for VC6 */
+ strcat(exePath, "\\..");
+#else
strcat_s(exePath, sizeof(exePath), "\\..");
+#endif /* _MSC_VER >= 1200 */
SetCurrentDirectoryA(exePath);
#ifdef _DEBUG
GetCurrentDirectoryA(sizeof(exePath), exePath);
new_map = (short *)GC_scratch_alloc(MAP_LEN * sizeof(short));
if (new_map == 0) return(FALSE);
if (GC_print_stats)
- GC_printf("Adding block map for size of %u granules (%u bytes)\n",
+ GC_log_printf("Adding block map for size of %u granules (%u bytes)\n",
(unsigned)granules, (unsigned)(GRANULES_TO_BYTES(granules)));
if (granules == 0) {
for (displ = 0; displ < BYTES_TO_GRANULES(HBLKSIZE); displ++) {
return FALSE;
}
-/* Find the text(code) mapping for the library whose name starts with nm. */
+#if defined(REDIRECT_MALLOC)
+/* Find the text(code) mapping for the library whose name, after */
+/* stripping the directory part, starts with nm. */
GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp)
{
size_t nm_len = strlen(nm);
&prot, &maj_dev, &map_path);
if (buf_ptr == NULL) return FALSE;
- if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x' &&
- strncmp(nm, map_path, nm_len) == 0) {
+ if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') {
+ char *p = map_path;
+ /* Set p to point just past last slash, if any. */
+ while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p;
+ while (*p != '/' && p >= map_path) --p;
+ ++p;
+ if (strncmp(nm, p, nm_len) == 0) {
*startp = my_start;
*endp = my_end;
return TRUE;
+ }
}
}
return FALSE;
}
+#endif /* REDIRECT_MALLOC */
#ifdef IA64
static ptr_t backing_store_base_from_proc(void)
return(buf.RegionSize);
}
-int GC_get_stack_base(struct GC_stack_base *sb)
+GC_API int GC_get_stack_base(struct GC_stack_base *sb)
{
int dummy;
ptr_t sp = (ptr_t)(&dummy);
unsigned i;
# ifndef REDIRECT_MALLOC
- static word last_gc_no = (word)(-1);
-
- if (last_gc_no != GC_gc_no) {
- GC_add_current_malloc_heap();
- last_gc_no = GC_gc_no;
- }
if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size;
if (GC_is_malloc_heap_base(p)) return TRUE;
# endif
/* If I read the documentation correctly, this can */
/* only happen if HBLKSIZE > 64k or not a power of 2. */
if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections");
- GC_heap_bases[GC_n_heap_bases++] = result;
+ if (0 != result) GC_heap_bases[GC_n_heap_bases++] = result;
return(result);
}
/* int cacao_suspendhandler(void *); */
#if defined(IA64) || defined(HP_PA) || defined(M68K)
+#ifdef SA_SIGINFO
void GC_suspend_handler(int sig, siginfo_t *info, void *context)
+#else
+void GC_suspend_handler(int sig)
+#endif
{
int old_errno = errno;
GC_with_callee_saves_pushed(GC_suspend_handler_inner, (ptr_t)(word)sig);
#else
/* We believe that in all other cases the full context is already */
/* in the signal handler frame. */
+#ifdef SA_SIGINFO
void GC_suspend_handler(int sig, siginfo_t *info, void *context)
+#else
+void GC_suspend_handler(int sig)
+#endif
{
int old_errno = errno;
+# ifndef SA_SIGINFO
+ void *context = 0;
+# endif
GC_suspend_handler_inner((ptr_t)(word)sig, context);
errno = old_errno;
}
for (i = 0; i < n_live_threads; i++)
while (0 != (code = sem_wait(&GC_restart_ack_sem)))
if (errno != EINTR) {
- GC_err_printf1("sem_wait() returned %ld\n",
- (unsigned long)code);
+ GC_err_printf("sem_wait() returned %d\n",
+ code);
ABORT("sem_wait() for restart handler failed");
}
# endif
ABORT("sem_init failed");
# endif
- act.sa_flags = SA_RESTART | SA_SIGINFO;
+ act.sa_flags = SA_RESTART
+# ifdef SA_SIGINFO
+ | SA_SIGINFO
+# endif
+ ;
if (sigfillset(&act.sa_mask) != 0) {
ABORT("sigfillset() failed");
}
GC_remove_allowed_signals(&act.sa_mask);
/* SIG_THR_RESTART is set in the resulting mask. */
/* It is unmasked by the handler when necessary. */
+# ifdef SA_SIGINFO
act.sa_sigaction = GC_suspend_handler;
+# else
+ act.sa_handler = GC_suspend_handler;
+# endif
if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
ABORT("Cannot set SIG_SUSPEND handler");
}
+# ifdef SA_SIGINFO
act.sa_flags &= ~ SA_SIGINFO;
+# endif
act.sa_handler = GC_restart_handler;
if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
ABORT("Cannot set SIG_THR_RESTART handler");
#if defined(GC_USE_DL_WRAP) || defined(GC_USE_DLOPEN_WRAP)
/* Define GC_ functions as aliases for the plain ones, which will */
/* be intercepted. This allows files which include gc.h, and hence */
-/* generate referemces to the GC_ symbols, to see the right symbols. */
+/* generate references to the GC_ symbols, to see the right symbols. */
int GC_pthread_create(pthread_t * t, const pthread_attr_t * a,
void * (* fn)(void *), void * arg) {
return pthread_create(t, a, fn, arg);
# endif /* HANDLE_FORK */
# if defined(INCLUDE_LINUX_THREAD_DESCR)
/* Explicitly register the region including the address */
- /* of a thread local variable. This should included thread */
+ /* of a thread local variable. This should include thread */
/* locals for the main thread, except for those allocated */
/* in response to dlopen calls. */
{
GC_nprocs = pthread_num_processors_np();
# endif
# if defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
- || defined(GC_SOLARIS_THREADS)
+ || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS)
GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
if (GC_nprocs <= 0) GC_nprocs = 1;
# endif
# if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
GC_nprocs = GC_get_nprocs();
# endif
-# if defined(GC_GNU_THREADS)
- if (GC_nprocs <= 0) GC_nprocs = 1;
-# endif
}
if (GC_nprocs <= 0) {
WARN("GC_get_nprocs() returned %ld\n", GC_nprocs);
signed_word GC_bytes_found = 0;
/* Number of bytes of memory reclaimed */
+ /* minus the number of bytes originally */
+ /* on free lists which we had to drop. */
#if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
word GC_fl_builder_count = 0;
# if defined(MSWIN32) || defined(MSWINCE)
# include <windows.h>
+# ifdef GC_DLL
+# define GC_print_stats 0 /* Not exported from DLL */
+ /* Redefine to 1 to generate output. */
+# endif
# endif
# ifdef PCR
# include <stdarg.h>
#endif
+/* Call GC_INIT only on platforms on which we think we really need it, */
+/* so that we can test automatic initialization on the rest. */
+#if defined(CYGWIN32) || defined (AIX) || defined(DARWIN)
+# define GC_COND_INIT() GC_INIT()
+#else
+# define GC_COND_INIT()
+#endif
/* Allocation Statistics */
int stubborn_count = 0;
void run_one_test()
{
char *x;
+ char **z;
# ifdef LINT
char *y = 0;
# else
char *y = (char *)(size_t)fail_proc1;
# endif
+ CLOCK_TYPE start_time;
+ CLOCK_TYPE reverse_time;
+ CLOCK_TYPE typed_time;
+ CLOCK_TYPE tree_time;
+ unsigned long time_diff;
DCL_LOCK_STATE;
# ifdef FIND_LEAK
- (void)GC_printf(
+ GC_printf(
"This test program is not designed for leak detection mode\n");
- (void)GC_printf("Expect lots of problems.\n");
+ GC_printf("Expect lots of problems.\n");
# endif
GC_FREE(0);
# ifndef DBG_HDRS_ALL
if ((GC_size(GC_malloc(7)) != 8 &&
GC_size(GC_malloc(7)) != MIN_WORDS * sizeof(GC_word))
|| GC_size(GC_malloc(15)) != 16) {
- (void)GC_printf("GC_size produced unexpected results\n");
+ GC_printf("GC_size produced unexpected results\n");
FAIL;
}
collectable_count += 1;
if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) {
- (void)GC_printf("GC_malloc(0) failed: GC_size returns %ld\n",
+ GC_printf("GC_malloc(0) failed: GC_size returns %ld\n",
(unsigned long)GC_size(GC_malloc(0)));
- FAIL;
+ FAIL;
}
collectable_count += 1;
if (GC_size(GC_malloc_uncollectable(0)) != MIN_WORDS * sizeof(GC_word)) {
- (void)GC_printf("GC_malloc_uncollectable(0) failed\n");
- FAIL;
+ GC_printf("GC_malloc_uncollectable(0) failed\n");
+ FAIL;
}
GC_is_valid_displacement_print_proc = fail_proc1;
GC_is_visible_print_proc = fail_proc1;
collectable_count += 1;
x = GC_malloc(16);
if (GC_base(x + 13) != x) {
- (void)GC_printf("GC_base(heap ptr) produced incorrect result\n");
+ GC_printf("GC_base(heap ptr) produced incorrect result\n");
FAIL;
}
# ifndef PCR
if (GC_base(y) != 0) {
- (void)GC_printf("GC_base(fn_ptr) produced incorrect result\n");
+ GC_printf("GC_base(fn_ptr) produced incorrect result\n");
FAIL;
}
# endif
if (GC_same_obj(x+5, x) != x + 5) {
- (void)GC_printf("GC_same_obj produced incorrect result\n");
+ GC_printf("GC_same_obj produced incorrect result\n");
FAIL;
}
if (GC_is_visible(y) != y || GC_is_visible(x) != x) {
- (void)GC_printf("GC_is_visible produced incorrect result\n");
+ GC_printf("GC_is_visible produced incorrect result\n");
FAIL;
}
+ z = GC_malloc(8);
+ GC_PTR_STORE(z, x);
+ if (*z != x) {
+ GC_printf("GC_PTR_STORE failed: %p != %p\n", *z, x);
+ FAIL;
+ }
if (!TEST_FAIL_COUNT(1)) {
-# if!(defined(RS6000) || defined(POWERPC) || defined(IA64)) || defined(M68K)
- /* ON RS6000s function pointers point to a descriptor in the */
+# if!(defined(POWERPC) || defined(IA64)) || defined(M68K)
+ /* On POWERPCs function pointers point to a descriptor in the */
/* data segment, so there should have been no failures. */
/* The same applies to IA64. Something similar seems to */
/* be going on with NetBSD/M68K. */
- (void)GC_printf("GC_is_visible produced wrong failure indication\n");
+ GC_printf("GC_is_visible produced wrong failure indication\n");
FAIL;
# endif
}
if (GC_is_valid_displacement(y) != y
|| GC_is_valid_displacement(x) != x
|| GC_is_valid_displacement(x + 3) != x + 3) {
- (void)GC_printf(
+ GC_printf(
"GC_is_valid_displacement produced incorrect result\n");
FAIL;
}
if (GC_all_interior_pointers && !TEST_FAIL_COUNT(1)
|| !GC_all_interior_pointers && !TEST_FAIL_COUNT(2)) {
# endif
- (void)GC_printf("GC_is_valid_displacement produced wrong failure indication\n");
+ GC_printf("GC_is_valid_displacement produced wrong failure indication\n");
FAIL;
}
# endif
# endif /* DBG_HDRS_ALL */
/* Test floating point alignment */
- collectable_count += 2;
+ collectable_count += 2;
*(double *)GC_MALLOC(sizeof(double)) = 1.0;
*(double *)GC_MALLOC(sizeof(double)) = 1.0;
+ /* Test size 0 allocation a bit more */
+ {
+ size_t i;
+ for (i = 0; i < 10000; ++i) {
+ GC_MALLOC(0);
+ GC_FREE(GC_MALLOC(0));
+ GC_MALLOC_ATOMIC(0);
+ GC_FREE(GC_MALLOC_ATOMIC(0));
+ }
+ }
# ifdef GC_GCJ_SUPPORT
GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *));
GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc);
GC_free(GC_malloc(0));
GC_free(GC_malloc_atomic(0));
/* Repeated list reversal test. */
+ GET_TIME(start_time);
reverse_test();
-# ifdef PRINTSTATS
- GC_printf("-------------Finished reverse_test\n");
-# endif
+ if (GC_print_stats) {
+ GET_TIME(reverse_time);
+ time_diff = MS_TIME_DIFF(reverse_time, start_time);
+ GC_log_printf("-------------Finished reverse_test at time %u (%p)\n",
+ (unsigned) time_diff, &start_time);
+ }
# ifndef DBG_HDRS_ALL
typed_test();
-# ifdef PRINTSTATS
- GC_printf("-------------Finished typed_test\n");
-# endif
+ if (GC_print_stats) {
+ GET_TIME(typed_time);
+ time_diff = MS_TIME_DIFF(typed_time, start_time);
+ GC_log_printf("-------------Finished typed_test at time %u (%p)\n",
+ (unsigned) time_diff, &start_time);
+ }
# endif /* DBG_HDRS_ALL */
tree_test();
+ if (GC_print_stats) {
+ GET_TIME(tree_time);
+ time_diff = MS_TIME_DIFF(tree_time, start_time);
+ GC_log_printf("-------------Finished tree_test at time %u (%p)\n",
+ (unsigned) time_diff, &start_time);
+ }
LOCK();
n_tests++;
UNLOCK();
GC_gcollect();
tiny_reverse_test(0);
GC_gcollect();
- GC_printf("Finished a child process\n");
+ if (GC_print_stats)
+ GC_log_printf("Finished a child process\n");
exit(0);
}
# endif
- /* GC_printf("Finished %x\n", pthread_self()); */
+ if (GC_print_stats)
+ GC_log_printf("Finished %p\n", &start_time);
}
void check_heap_stats()
/* Cheat and let stdio initialize toolbox for us. */
printf("Testing GC Macintosh port.\n");
# endif
- GC_INIT(); /* Only needed on a few platforms. */
- (void) GC_set_warn_proc(warn_proc);
+ GC_COND_INIT();
+ GC_set_warn_proc(warn_proc);
# if (defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(GWW_VDB)) \
&& !defined(MAKE_BACK_GRAPH) && !defined(NO_INCREMENTAL)
GC_enable_incremental();
- (void) GC_printf("Switched to incremental mode\n");
+ GC_printf("Switched to incremental mode\n");
# if defined(MPROTECT_VDB)
- (void)GC_printf("Emulating dirty bits with mprotect/signals\n");
+ GC_printf("Emulating dirty bits with mprotect/signals\n");
# else
# ifdef PROC_VDB
- (void)GC_printf("Reading dirty bits from /proc\n");
+ GC_printf("Reading dirty bits from /proc\n");
# else
- (void)GC_printf("Using DEFAULT_VDB dirty bit implementation\n");
+ GC_printf("Using DEFAULT_VDB dirty bit implementation\n");
# endif
# endif
# endif
run_one_test();
check_heap_stats();
# ifndef MSWINCE
- (void)fflush(stdout);
+ fflush(stdout);
# endif
# ifdef LINT
/* Entry points we should be testing, but aren't. */
GC_use_DllMain(); /* Test with implicit thread registration if possible. */
GC_printf("Using DllMain to track threads\n");
# endif
- GC_INIT();
+ GC_COND_INIT();
# ifndef NO_INCREMENTAL
GC_enable_incremental();
# endif
pthread_win32_process_attach_np ();
pthread_win32_thread_attach_np ();
# endif
- GC_INIT();
+ GC_COND_INIT();
pthread_attr_init(&attr);
# if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \
# modified is included with the above copyright notice.
-## FIXME: trace_test don't works on macosx 10.3
+## FIXME: trace_test doesn't work on macosx 10.3
## gcc -g -O2 -o .libs/tracetest trace_test.o ../.libs/libgc.dylib -lpthread
## ld: Undefined symbols:
## _GC_generate_random_backtrace
middletest_SOURCES = tests/middle.c
middletest_LDADD = $(test_ldadd)
+TESTS += smashtest$(EXEEXT)
+check_PROGRAMS += smashtest
+smashtest_SOURCES = tests/smash_test.c
+smashtest_LDADD = $(test_ldadd)
+
+TESTS += hugetest$(EXEEXT)
+check_PROGRAMS += hugetest
+hugetest_SOURCES = tests/huge_test.c
+hugetest_LDADD = $(test_ldadd)
+
#TESTS += tracetest$(EXEEXT)
#check_PROGRAMS += tracetest
#tracetest_SOURCES = tests/trace_test.c
TESTS += test_cpp$(EXEEXT)
check_PROGRAMS += test_cpp
test_cpp_SOURCES = tests/test_cpp.cc
+if AVOID_CPP_LIB
+test_cpp_LDADD = gc_cpp.o $(test_ldadd)
+else
test_cpp_LDADD = libgccpp.la $(test_ldadd)
endif
+endif
/* Each thread structure must be initialized. */
/* This call must be made from the new thread. */
-/* Caller holds allocation lock. */
void GC_init_thread_local(GC_tlfs p)
{
int i;
+ GC_ASSERT(I_HOLD_LOCK());
if (!keys_initialized) {
if (0 != GC_key_create(&GC_thread_key, 0)) {
ABORT("Failed to create key for local allocator");
void *result;
void **tiny_fl;
-# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
+# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
GC_key_t k = GC_thread_key;
if (EXPECT(0 == k, 0)) {
/* We haven't yet run GC_init_parallel. That means */
}
tsd = GC_getspecific(k);
# else
- GC_ASSERT(GC_is_initialized);
tsd = GC_getspecific(GC_thread_key);
# endif
-# if defined(REDIRECT_MALLOC) && defined(USE_PTHREAD_SPECIFIC)
- if (EXPECT(NULL == tsd, 0)) {
+# if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
+ if (EXPECT(0 == tsd, 0)) {
return GC_core_malloc(bytes);
}
# endif
+ GC_ASSERT(GC_is_initialized);
# ifdef GC_ASSERTIONS
/* We can't check tsd correctly, since we don't have access to */
/* the right declarations. But we can check that it's close. */
tiny_fl = ((GC_tlfs)tsd) -> normal_freelists;
GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
+# ifdef LOG_ALLOCS
+ GC_err_printf("GC_malloc(%d) = %p : %d\n", bytes, result, GC_gc_no);
+# endif
return result;
}
void * GC_malloc_atomic(size_t bytes)
{
size_t granules = ROUNDED_UP_GRANULES(bytes);
+ void *tsd;
void *result;
void **tiny_fl;
+# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
+ GC_key_t k = GC_thread_key;
+ if (EXPECT(0 == k, 0)) {
+ /* We haven't yet run GC_init_parallel. That means */
+ /* we also aren't locking, so this is fairly cheap. */
+ return GC_core_malloc(bytes);
+ }
+ tsd = GC_getspecific(k);
+# else
+ tsd = GC_getspecific(GC_thread_key);
+# endif
+# if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
+ if (EXPECT(0 == tsd, 0)) {
+ return GC_core_malloc(bytes);
+ }
+# endif
GC_ASSERT(GC_is_initialized);
- tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
- -> ptrfree_freelists;
- GC_FAST_MALLOC_GRANS(result, bytes, tiny_fl, DIRECT_GRANULES,
+ tiny_fl = ((GC_tlfs)tsd) -> ptrfree_freelists;
+ GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
PTRFREE, GC_core_malloc_atomic(bytes), 0/* no init */);
return result;
}
/* are not necessarily free. And there may be cache fill order issues. */
/* For now, we punt with incremental GC. This probably means that */
/* incremental GC should be enabled before we fork a second thread. */
+/* Unlike the other thread local allocation calls, we assume that the */
+/* collector has been explicitly initialized. */
void * GC_gcj_malloc(size_t bytes,
void * ptr_to_struct_containing_descr)
{
void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
-> gcj_freelists;
GC_ASSERT(GC_gcj_malloc_initialized);
- GC_FAST_MALLOC_GRANS(result, bytes, tiny_fl, DIRECT_GRANULES,
+ GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
GC_gcj_kind,
GC_core_gcj_malloc(bytes,
ptr_to_struct_containing_descr),
int GC_typed_mark_proc_index; /* Indices of my mark */
int GC_array_mark_proc_index; /* procedures. */
+static void GC_push_typed_structures_proc (void)
+{
+ GC_push_all((ptr_t)&GC_ext_descriptors, (ptr_t)&GC_ext_descriptors + sizeof(word));
+}
+
/* Add a multiword bitmap to GC_ext_descriptors arrays. Return */
/* starting index. */
/* Returns -1 on failure. */
size_t new_size;
word ed_size = GC_ed_size;
- UNLOCK();
if (ed_size == 0) {
+ GC_push_typed_structures = GC_push_typed_structures_proc;
+ UNLOCK();
new_size = ED_INITIAL_SIZE;
} else {
+ UNLOCK();
new_size = 2 * ed_size;
if (new_size > MAX_ENV) return(-1);
}
# endif
GC_ASSERT(!parallel_initialized);
GC_win32_dll_threads = TRUE;
+ GC_init_parallel();
}
#else
GC_API void GC_use_DllMain(void)
/* thread being deleted. */
void GC_delete_gc_thread(GC_vthread gc_id)
{
+ CloseHandle(gc_id->handle);
if (GC_win32_dll_threads) {
/* This is intended to be lock-free. */
/* It is either called synchronously from the thread being deleted, */
/* or by the joining thread. */
/* In this branch asynchronosu changes to *gc_id are possible. */
- CloseHandle(gc_id->handle);
gc_id -> stack_base = 0;
gc_id -> id = 0;
# ifdef CYGWIN32
prev = p;
p = p -> next;
}
+ CloseHandle(p->handle);
if (prev == 0) {
GC_threads[hv] = p -> next;
} else {
}
}
-int GC_register_my_thread(struct GC_stack_base *sb) {
+GC_API int GC_register_my_thread(struct GC_stack_base *sb) {
DWORD t = GetCurrentThreadId();
if (0 == GC_lookup_thread(t)) {
}
}
-int GC_unregister_my_thread(void)
+GC_API int GC_unregister_my_thread(void)
{
DWORD t = GetCurrentThreadId();
# ifndef GC_PTHREADS
/* this breaks pthread_join on Cygwin, which is guaranteed to */
/* only see user pthreads */
- AO_store(&(t -> in_use), FALSE);
- CloseHandle(t -> handle);
+ GC_ASSERT(GC_win32_dll_threads);
+ GC_delete_gc_thread(t);
# endif
return;
}
unsigned ( __stdcall *start_address )( void * ),
void *arglist, unsigned initflag, unsigned *thrdaddr)
{
- uintptr_t thread_h = -1L;
+ uintptr_t thread_h;
thread_args *args;
/* Handed off to and deallocated by child thread. */
if (0 == args) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- return (uintptr_t)(-1);
+ return (uintptr_t)(-1L);
}
/* set up thread arguments */
if (!GC_is_initialized) GC_init();
if (GC_win32_dll_threads) {
GC_need_to_lock = TRUE;
- /* Cannot intercept thread creation. Hence we don't know if other */
- /* threads exist. However, client is not allowed to create other */
- /* threads before collector initialization. Thus it's OK not to */
- /* lock before this. */
+ /* Cannot intercept thread creation. Hence we don't know if */
+ /* other threads exist. However, client is not allowed to */
+ /* create other threads before collector initialization. */
+ /* Thus it's OK not to lock before this. */
}
/* Initialize thread local free lists if used. */
# if defined(THREAD_LOCAL_ALLOC)