summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore23
-rw-r--r--AUTHORS23
-rw-r--r--CMakeLists.txt6
-rw-r--r--ChangeLog187
-rw-r--r--Mac_files/dataend.c9
-rw-r--r--Mac_files/datastart.c9
-rw-r--r--Makefile.am12
-rw-r--r--Makefile.direct11
-rw-r--r--Makefile.dj8
-rw-r--r--NT_X64_THREADS_MAKEFILE2
-rw-r--r--README.QUICK8
-rw-r--r--README.md (renamed from README)555
-rw-r--r--SMakefile.amiga20
-rw-r--r--TODO68
-rw-r--r--WCC_MAKEFILE2
-rw-r--r--allchblk.c280
-rw-r--r--alloc.c260
-rwxr-xr-xautogen.sh40
-rw-r--r--backgraph.c24
-rw-r--r--blacklst.c56
-rw-r--r--build/s60v3/bld.inf11
-rw-r--r--build/s60v3/libgc.mmp76
-rw-r--r--checksums.c21
-rw-r--r--configure.ac143
-rw-r--r--cord/cordbscs.c14
-rw-r--r--cord/cordprnt.c10
-rw-r--r--cord/cordxtra.c55
-rw-r--r--cord/tests/cordtest.c25
-rw-r--r--cord/tests/de.c2
-rw-r--r--cord/tests/de_win.c7
-rw-r--r--darwin_stop_world.c38
-rw-r--r--dbg_mlc.c302
-rw-r--r--digimars.mak4
-rw-r--r--doc/README.Mac8
-rw-r--r--doc/README.amiga32
-rw-r--r--doc/README.cords6
-rw-r--r--doc/README.environment4
-rw-r--r--doc/README.hp2
-rw-r--r--doc/README.linux8
-rw-r--r--doc/README.macros23
-rw-r--r--doc/README.solaris28
-rw-r--r--doc/README.symbian13
-rw-r--r--doc/README.win326
-rw-r--r--doc/debugging.html8
-rw-r--r--doc/doc.am3
-rw-r--r--doc/gcdescr.html12
-rw-r--r--doc/gcinterface.html30
-rw-r--r--doc/leak.html3
-rw-r--r--doc/overview.html6
-rw-r--r--doc/porting.html6
-rw-r--r--doc/simple_example.html2
-rw-r--r--dyn_load.c178
-rw-r--r--extra/AmigaOS.c546
-rw-r--r--extra/MacOS.c131
-rw-r--r--extra/Mac_files/MacOS_config.h (renamed from Mac_files/MacOS_config.h)0
-rw-r--r--extra/Mac_files/dataend.c9
-rw-r--r--extra/Mac_files/datastart.c9
-rw-r--r--extra/gc.c4
-rw-r--r--extra/msvc_dbg.c16
-rw-r--r--extra/symbian.cpp55
-rw-r--r--extra/symbian/global_end.cpp16
-rw-r--r--extra/symbian/global_start.cpp16
-rw-r--r--extra/symbian/init_global_static_roots.cpp33
-rw-r--r--finalize.c551
-rw-r--r--fnlz_mlc.c6
-rw-r--r--gc_cpp.cc30
-rw-r--r--gcj_mlc.c12
-rw-r--r--headers.c15
-rw-r--r--include/cord.h2
-rw-r--r--include/ec.h14
-rw-r--r--include/gc.h501
-rw-r--r--include/gc_allocator.h6
-rw-r--r--include/gc_backptr.h6
-rw-r--r--include/gc_config_macros.h55
-rw-r--r--include/gc_cpp.h99
-rw-r--r--include/gc_disclaim.h5
-rw-r--r--include/gc_gcj.h21
-rw-r--r--include/gc_inline.h20
-rw-r--r--include/gc_mark.h64
-rw-r--r--include/gc_pthread_redirects.h5
-rw-r--r--include/gc_tiny_fl.h3
-rw-r--r--include/gc_typed.h19
-rw-r--r--include/gc_version.h2
-rw-r--r--include/new_gc_alloc.h6
-rw-r--r--include/private/darwin_semaphore.h20
-rw-r--r--include/private/dbg_mlc.h2
-rw-r--r--include/private/gc_hdrs.h38
-rw-r--r--include/private/gc_locks.h112
-rw-r--r--include/private/gc_pmark.h141
-rw-r--r--include/private/gc_priv.h441
-rw-r--r--include/private/gcconfig.h298
-rw-r--r--include/private/pthread_stop_world.h2
-rw-r--r--include/private/pthread_support.h13
-rw-r--r--include/private/specific.h26
-rw-r--r--include/private/thread_local_alloc.h31
-rw-r--r--mach_dep.c21
-rw-r--r--malloc.c98
-rw-r--r--mallocx.c51
-rw-r--r--mark.c457
-rw-r--r--mark_rts.c155
-rw-r--r--misc.c637
-rw-r--r--new_hblk.c23
-rw-r--r--obj_map.c8
-rw-r--r--os_dep.c924
-rw-r--r--pcr_interface.c44
-rw-r--r--pthread_start.c5
-rw-r--r--pthread_stop_world.c202
-rw-r--r--pthread_support.c381
-rw-r--r--ptr_chck.c61
-rw-r--r--real_malloc.c2
-rw-r--r--reclaim.c65
-rw-r--r--specific.c37
-rw-r--r--tests/disclaim_bench.c1
-rw-r--r--tests/disclaim_test.c11
-rw-r--r--tests/initsecondarythread.c14
-rw-r--r--tests/middle.c8
-rw-r--r--tests/staticrootslib.c56
-rw-r--r--tests/staticrootstest.c82
-rw-r--r--tests/subthread_create.c20
-rw-r--r--tests/test.c286
-rw-r--r--tests/test_cpp.cc67
-rw-r--r--tests/tests.am51
-rw-r--r--tests/thread_leak_test.c10
-rw-r--r--tests/threadkey_test.c9
-rw-r--r--tests/trace_test.c2
-rw-r--r--thread_local_alloc.c22
-rw-r--r--tools/add_gc_prefix.c15
-rw-r--r--tools/gcname.c12
-rw-r--r--tools/if_mach.c6
-rw-r--r--tools/if_not_there.c11
-rw-r--r--tools/setjmp_t.c29
-rw-r--r--tools/threadlibs.c3
-rw-r--r--typd_mlc.c32
-rw-r--r--win32_threads.c664
-rw-r--r--windows-untested/vc60/gc.dsp18
-rw-r--r--windows-untested/vc60/libgc.dsp18
-rw-r--r--windows-untested/vc60/libgcmt.dsp18
-rw-r--r--windows-untested/vc70/gc.vcproj4
-rw-r--r--windows-untested/vc70/libgc.vcproj4
-rw-r--r--windows-untested/vc70/libgcmt.vcproj4
-rw-r--r--windows-untested/vc71/gc.vcproj112
-rw-r--r--windows-untested/vc71/libgc.vcproj4
-rw-r--r--windows-untested/vc71/libgcmt.vcproj4
143 files changed, 6585 insertions, 4253 deletions
diff --git a/.gitignore b/.gitignore
index 53b518a5..14cdcae4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*.dll
*.exe
*.gcda
+*.gch
*.gcno
*.la
*.lib
@@ -12,6 +13,10 @@
*.obj
/*.gc.log
+/*_bench.log
+/*_bench.trs
+/*test.log
+/*test.trs
/.deps/
/.libs/
/Makefile
@@ -40,7 +45,7 @@
/hugetest
/if_mach
/if_not_there
-/initsecondarythread
+/initsecondarythread_test
/leaktest
/libtool
/middletest
@@ -48,19 +53,24 @@
/setjmp_test
/smashtest
/staticrootstest
-/subthread_create
+/subthreadcreate_test
+/test-suite.log
/test_cpp
+/test_cpp.log
+/test_cpp.trs
/threadkey_test
/threadleaktest
/threadlibs
/tracetest
-/include/private/config.h
-/include/private/config.h.in~
-/include/private/stamp-h1
+# Config and stamp files generated by configure:
+config.h
+config.h.in~
+stamp-h1
# External library (without trailing slash to allow symlinks):
/libatomic_ops*
+/pthreads-w32*
# These files are generated by autoreconf:
/Makefile.in
@@ -71,7 +81,7 @@
/config.sub
/configure
/depcomp
-/include/private/config.h.in
+/include/config.h.in
/install-sh
/ltmain.sh
/m4/libtool.m4
@@ -81,6 +91,7 @@
/m4/lt~obsolete.m4
/missing
/mkinstalldirs
+/test-driver
# These files are generated by CMake:
/*.vcxproj
diff --git a/AUTHORS b/AUTHORS
index f67a31b1..2df0acc6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,6 +27,7 @@ Alan J. Demers <ademers@cs.cornell.edu>
Aleksey Demakov
Alexander Belchenko <bialix@ukr.net>
Alexander Gavrilov <angavrilov@gmail.com>
+Alexander Herz <alexander.herz@mytum.de>
Alexandr Petrosian
Alexandr Shadchin <ShadchinAV@mail.ru>
Alistair G. Crooks <agc@uts.amdahl.com>
@@ -61,11 +62,12 @@ Bradley D. LaRonde <brad@ltc.com>
Bradley Smith <brad@brad-smith.co.uk>
Brent Benson <brent@jade.ssd.csd.harris.com>
Brian Alliet <brian@brianweb.net>
-Brian Beuning
+Brian Beuning <bbeuning@corecard.com>
Brian Burton
Brian D. Carlstrom <bdc@clark.lcs.mit.edu>
Brian F. Dennis <xjam@cork.cs.berkeley.edu>
Brian Lewis <btlewis@eng.sun.com>
+Bruce Hoult <bruce@hoult.org>
Bruce Mitchener <bruce.mitchener@gmail.com>
Bryce McKinley
Burkhard Linke <blinke@cebitec.uni-bielefeld.de>
@@ -78,6 +80,7 @@ Christian Limpach
Christian Thalinger
Christoffe Raffali
Clay Spence <cds@peanut.sarnoff.com>
+Colin LeMahieu <clemahieu@gmail.com>
Craig McDaniel
Dai Sato
Dan Bonachea
@@ -104,6 +107,7 @@ Dick Porter
Dietmar Planitzer <dave.pl@ping.at>
Dimitris Vyzovitis
Dimitry Andric
+Djamel Magri <djamel.magri@googlemail.com>
Doug Kaufman
Douglas Steel <doug@wg.icl.co.uk>
Elijah Taylor <elijahtaylor@google.com>
@@ -140,6 +144,7 @@ Ivan Demakov <ivan@tgrad.nsk.su>
Ivan Maidanski <ivmai@mail.ru>
Jaap Boender
Jack Andrews <effbiae@gmail.com>
+Jack Howarth <howarth@bromo.med.uc.edu>
Jacob Navia
Jakub Jelinek
James Clark <jjc@jclark.com>
@@ -164,6 +169,7 @@ Jim Marshall <jim.marshall@wbemsolutions.com>
Jim Meyering <jim@meyering.net>
Joerg Sonnenberger
Johannes Schmidt
+Johannes Totz <jtotz@ic.ac.uk>
John Bowman
John Clements
John Ellis
@@ -174,6 +180,7 @@ Jonathan Chambers <joncham@gmail.com>
Jonathan Clark
Jonathan Pryor <jpryor@novell.com>
Juan Jose Garcia-Ripoll <juanjose.garciaripoll@googlemail.com>
+Kai Tietz <ktietz70@googlemail.com>
Kazu Hirata
Kazuhiro Inaoka
Keith Seitz <keiths@redhat.com>
@@ -187,6 +194,7 @@ Kornel Pal <kornelpal@gmail.com>
Koushik Dutta <koushd@gmail.com>
Krister Walfridsson
Kristian Kristensen
+Kurt Miller <kurt@intricatesoftware.com>
Lars Farm <lars.farm@ite.mh.se>
Laurent Morichetti
Linas Vepstas <linasvepstas@gmail.com>
@@ -220,6 +228,7 @@ Michael Spertus
Michel Schinz <schinz@alphanet.ch>
Miguel de Icaza <miguel@gnome.org>
Mike Gran <spk121@yahoo.com>
+Mike McGaughey <mmcg@cs.monash.edu.au>
Mike Stump <mrs@windriver.com>
Mitch Harris <maharri@uiuc.edu>
Mohan Embar
@@ -228,13 +237,16 @@ Neale Ferguson <neale@mono-cvs.ximian.com>
Neil Sharman <neil@cs.mu.oz.au>
Nicolas Cannasse
Niibe Yutaka <gniibe@fsij.org>
+Niklas Therning <niklas@therning.org>
Noah Lavine <noah.b.lavine@gmail.com>
Oliver Kurth <oliver.kurth@innominate.com>
+Ondrej Bilka <neleai@seznam.cz>
Paolo Molaro <lupus@ximian.com>
Parag Patel <parag@netcom.com>
Patrick Bridges
Patrick C. Beard <beard@netscape.com>
Patrick Doyle
+Patrick Marlier <patrick.marlier@gmail.com>
Paul Brook
Peter Chubb
Peter Colson
@@ -256,6 +268,8 @@ Rainer Orth <ro@cebitec.uni-bielefeld.de>
Raja R Harinath <harinath@hurrynot.org>
Rauli Ruohonen
Regis Cridlig <Regis.Cridlig@cl.cam.ac.uk>
+Reimer Behrends <behrends@gmail.com>
+Renaud Blanch <renaud.blanch@lri.fr>
Rene Girard
Rex Dieter <rdieter@math.unl.edu>
Reza Shahidi
@@ -291,7 +305,7 @@ Takis Psarogiannakopoulos <takis@xfree86.org>
Tatsuya Bizenn
Thiemo Seufer <ths@networkno.de>
Thomas Funke <thf@zelator.in-berlin.de>
-Thomas Klausner
+Thomas Klausner <tk@giga.or.at>
Thomas Maier
Thorsten Glaser <tg@debian.org>
Tilman Vogel <Tilman.Vogel@web.de>
@@ -301,6 +315,7 @@ Todd Berman <tberman@mono-cvs.ximian.com>
Tom Tromey <tromey@cygnus.com>
Toralf Foerster
Toshio Endo
+Tsugutomo Enami <tsugutomo.enami@jp.sony.com>
Tum Nguyen
Tyson Dowd
Uchiyama Yasushi
@@ -308,12 +323,16 @@ Ulrich Drepper
Ulrich Weigand
Uros Bizjak <ubizjak@gmail.com>
Victor Ivrii
+Vitaly Magerya <vmagerya@gmail.com>
Vladimir Tsichevski
Walter Bright
Walter Underwood
Wilson Ho
Wink Saville
+Xi Wang <xi.wang@gmail.com>
Xiaokun Zhu <xiaokun@aero.gla.ac.uk>
Yannis Bres
+Yvan Roux <yvan.roux@linaro.org>
+Zach Saw <zach.saw@gmail.com>
Zhong Shao
Zoltan Varga <vargaz@gmail.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9fedd3c9..a19cabb4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,9 +28,7 @@ PROJECT(gc)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
ADD_DEFINITIONS("-D_CRT_SECURE_NO_DEPRECATE
- -DALL_INTERIOR_POINTERS
- -DGC_BUILD
-")
+ -DALL_INTERIOR_POINTERS")
IF(APPLE)
SET(CMAKE_OSX_ARCHITECTURES "ppc;i386;x86_64" CACHE STRING "Build architectures for Mac OS X" FORCE)
@@ -114,7 +112,6 @@ IF(CMAKE_USE_PTHREADS_INIT)
ENDIF()
IF ( "HOST" MATCHES .*-.*-openbsd.*)
ADD_DEFINITIONS("-DGC_OPENBSD_THREADS")
- #FIXME openbsd_threads=true
ENDIF()
IF ( "HOST" MATCHES .*-.*-freebsd.*)
MESSAGE("FreeBSD does not yet fully support threads with Boehm GC.")
@@ -203,7 +200,6 @@ ENDIF(CMAKE_USE_WIN32_THREADS_INIT)
OPTION(enable_gcj_support "Support for gcj" NO)
IF(enable_gcj_support)
- #TODO AM_CONDITIONAL(ENABLE_GCJ_SUPPORT, [test x"$enable_gcj_support" != xno])
ADD_DEFINITIONS("-DGC_GCJ_SUPPORT")
ENDIF(enable_gcj_support)
diff --git a/ChangeLog b/ChangeLog
index a02c1270..61d3af3a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,37 +1,117 @@
-== [7.3alpha2] (development) ==
+== [7.3alpha4] (development) ==
+
+* Add GC_push_all/conditional() to GC public API.
+* Add note about 'pkg-config' solving problem with autoconf 2.68 or older.
+* Add public GC_set/get_abort_func to replace default GC_on_abort.
+* Add public setter and getter for GC_push_other_roots.
+* Add thread suspend/resume signals public setters (POSIX threads).
+* Call GC_on_abort (with NULL argument) on exit(1).
+* Check GC_base result in GC_print_all_smashed_proc.
+* Check traceable_allocator.allocate result before dereference in test_cpp.
+* Code refactoring of GC_x_printf (move shared code to macro).
+* Define old_bus_handler static variable only if used (Unix).
+* Disable find-leak GC_gcollect on GC abnormal EXIT.
+* Do not define _setjmp/_longjmp macros in mach_dep.c.
+* Elaborate comment on dependencies in autogen.sh.
+* Eliminate 'cast from int to pointer' warning in GC_exclude_static_roots.
+* Eliminate 'missing exception specification' warning in gc_cpp.cc (Clang).
+* Eliminate 'uninitialized variable use' warning in test_printf (cord).
+* Eliminate 'unused result' compiler warning in main() of test_cpp.
+* Eliminate 'unused value' compiler warning in GC_stop_world (Pthreads).
+* Eliminate Clang warning for GC_pthread_exit attribute.
+* Eliminate GCC warning in GC_get_main_stack_base (OpenBSD).
+* Eliminate GCC warnings in setjmp_t.c, test_cpp and cord 'de' app.
+* Eliminate SIGBUS-related dead code in GC_write_fault_handler (Linux).
+* Eliminate warning and simplify expression in GC_init_explicit_typing.
+* Enable 'force GC at every GC_malloc' debug-related functionality.
+* Fix GC_CreateThread and GC_beginthreadex definition for Cygwin.
+* Improve staticrootstest checks (tests).
+* Include "config.h" instead of "private/config.h" on HAVE_CONFIG_H.
+* Include proper header file in 'tools' for configuration macros.
+* Include pthread_np.h from pthread_stop_world.c on OpenBSD.
+* Log error messages to stderr instead of stdout in tests.
+* Minimize code duplication in GC_mark_and_push.
+* Move 'include setjmp.h' from mach_dep.c to gc_priv.h.
+* Move GC_get_suspend/thr_restart_signal to misc.c for NaCl and OpenBSD.
+* Port BDWGC to Android/x86.
+* Postpone the suspend signal in GC_dirty_init only if used to stop world.
+* Put gc_cpp symbols into 'boehmgc' namespace if GC_NAMESPACE defined.
+* Recognize GC_DONT_GC macro in gc.h (causes GC_INIT to turn off GC).
+* Recognize GC_SIG_SUSPEND and GC_SIG_THR_RESTART tuning macros in gc.h.
+* Remove nested EXPECT in GC_core_finalized_malloc.
+* Remove nested always-false ifdef for HPUX and FREEBSD.
+* Replace SIG_SUSPEND/THR_RESTART macros to variables in pthread_stop_world.
+* Replace sprintf with defensive snprintf.
+* Replace var-args GC_noop with GC_noop6 (to eliminate Clang warning).
+* Specify GC_malloc result is unused in some tests.
+* Specify GC_pthread_join result is unused in threadkey_test.
+* Turn off GC_LOOP_ON_ABORT functionality if GC compiled with NO_DEBUGGING.
+* Use memcpy (BCOPY) instead of strcpy (to suppress GCC warning).
+
+
+== [7.3alpha2] 2012-05-11 ==
* Add 'const' qualifier to pointer argument of some API functions.
* Add GC_UNDERSCORE_STDCALL, UNICODE macro templates to configure (Win32).
* Add GC_get_thr_restart_signal, GC_thread_is_registered to GC API.
* Add GC_is_heap_ptr, GC_move_disappearing_link to GC API.
+* Add SHORT_DBG_HDRS macro template to configure.
+* Add Symbian port to mainline.
* Add TODO file.
+* Add assertion ensuring proper alignment of 'pushed' GC symbols.
+* Add assertion in GC_getspecific on qtid.
+* Add assertion to GC_incremental_protection_needs, refine documentation.
+* Add assertion to check GC_large_free_bytes by GC_finish_collection.
* Add configure option to compile all library .c files into single gc.o.
* Add cordtest to make check.
* Add disclaim callbacks for efficient finalization (ENABLE_DISCLAIM).
* Add finalization.html to 'doc' folder.
* Add javaxfc.h to the installation set of GC header files (configure).
* Add on-heap-resize event notification to API.
+* Adjust GC_log_printf format specifiers (regarding signed/unsigned long).
+* Adjust GC_requested_heapsize on GC_init if GC_INITIAL_HEAP_SIZE given.
+* Allow GC_exclude_static_roots() region start to be unaligned.
* Allow Win32 DllMain chaining on the client side.
+* Allow to exclude finalization support by GC_NO_FINALIZATION macro.
+* Allow to get memory via Win32 VirtualAlloc (USE_WINALLOC) on Cygwin.
* Avoid unnecessary GC_find_limit invocation if GC_no_dls.
+* Avoid use of deprecated GC_dont_gc and GC_stackbottom in gctest.
+* Cast pointers to word (instead of unsigned long) in specific.h.
* Changed the order in autogen.sh so ltmain exists in time for automake.
+* Declare privately and use handy GC_base_C() for constant object pointers.
+* Define GC_DLL if DLL_EXPORT at GC build (for Cygwin/MinGW).
* Define GC_READ_ENV_FILE in configure for WinCE unless gc-debug is off.
+* Do not compile backgraph.c unless configure '--enable-gc-debug'.
* Do not compile pthread_stop_world.c for Cygwin/Darwin (configure).
* Do not install ancient new_gc_alloc.h broken for modern STL (configure).
+* Enable GC_MIN_MARKERS to set minimal number of pthread-based markers.
+* Enable PARALLEL_MARK and THREAD_LOCAL_ALLOC for FreeBSD in configure.
* Enable parallel mark by default in configure (Darwin/Linux/Solaris/Win32).
* Export GC_is_marked, GC_clear/set_mark_bit (for mark-bit manipulation).
* Extend thread-related debug messages.
+* Fix 'configure --enable-cplusplus' for Cygwin/MinGW.
+* Fix DATASTART (and other minor improvements) for NaCl target.
+* Fix GC_setspecific to prevent garbage collection inside.
* Fix compiler warning in cordtest.
* Fix minor warnings reported by GCC with '-pedantic' option.
+* Fix static data roots registration on Android (if GC is shared).
+* Implement GC_get_stack_base for Darwin for single-threaded mode.
+* Improve GC_allochblk algorithm of block splitting when unmapping enabled.
* Improve GC_collect_or_expand algorithm for many finalizers registered case.
* In tests, print a message in case a test is a no-op.
* Instruct configure to hide internal libgc.so symbols if supported by GCC.
+* Log amount of unmapped memory (if enabled) on marking-for-collection.
+* Make __data_start a weak symbol to allow loading modules on mips.
* Move "cord" library tests to "cord/tests" folder.
* Move asm machine-dependent files to "src" folder.
* Move build tools sources to "tools" folder.
* Move cord_pos.h to public headers folder.
+* Open log file in APPEND mode on Win32 (similar that on Unix/Cygwin).
* Optimize some functions by moving pthread_self calls out of LOCK section.
* Place only major per-release changes description to ChangeLog (this file).
+* Prevent compiler warnings in GC_FindTopOfStack and GC_ports (Darwin).
+* Recognize GC_LOG_TO_FILE_ALWAYS macro to log to 'gc.log' by default.
* Remove all auto-generated files from the repo.
* Remove binary icon file for de_win.
* Remove cordtest from "cord" library.
@@ -39,43 +119,119 @@
* Remove gc_amiga_redirects.h (included internally) from public headers.
* Remove obsolete Makefile.DLL (superseded by Cygwin/MinGW configure).
* Remove obsolete unused asm files for ALPHA, HPUX, SGI, RS6000, ULTRIX.
+* Remove unsupported MMAP_STACKS (specific to Solaris threads).
* Remove unused ancient SILENT, __STDC__, NO_SIGNALS macros.
* Replace ARGSUSED comment-based annotation with GCC 'unused' attribute.
+* Replace GC_ms_entry declaration with opaque definition for public API.
+* Replace long GC_markers global variable with int GC_markers_m1.
+* Replace pointer relational comparisons with non-pointer ones.
* Replace printf PRIxMAX specifier with '%p' for thread id debug output.
+* Require autoconf 2.61 instead of v2.64.
+* Simplify autogen.sh (use autoreconf).
* Split GC_abort with GC_on_abort and abort() invoked from ABORT.
+* Support GC_ATTR_MALLOC for MS VisualStudio.
+* Tag auxiliary malloc-like API functions with 'malloc' attribute.
+* Tag deprecated variables in GC API.
+* Tag must-be-non-null arguments of GC API functions.
* Turn on "extra" GCC warnings.
* Turn on unused-parameter checking for GCC.
* Update AUTHORS file.
* Use EXPECT for checking various 'initialized' boolean variables.
+* Use USE_COMPILER_TLS on Cygwin.
+* Use pthread_key for thread-local storage on FreeBSD.
+* Use union of AO_t and word to favor strict-aliasing compiler optimization.
-== [7.2] (candidate) ==
+== [7.2e] (candidate) ==
+* Fix GC_clear_stack by declaring 'dummy' local array as volatile.
+* Fix GC_get_stack_base assembly code (Cygwin/Clang).
+* Fix GC_unix_mmap_get_mem for open of /dev/zero failure.
+* Fix min_bytes_allocd preventing potential infinite loop in GC_allocobj.
+* Fix null-pointer dereference in CORD_substr_closure.
+* Fix potential double fclose in test_extras (cordtest).
+* Fix pthread_attr_t resource leak in pthread_create.
+* Fix sizeof in GC_push_thread_structures.
+* Fix unportable '==' test operators in configure.
+* Fix vsprintf_args cleanup in CORD_vsprintf.
+
+
+== [7.2d] 2012-08-09 ==
+
+* Fix GC_call_with_stack_base to prevent its tail-call optimization.
+* Fix all address-of-dummy operations by using GC_approx_sp() instead.
+* Fix stop_info.stack_ptr assignment in GC_suspend_all for OpenBSD.
+* Fix test_cpp (ensure the collector recognizes pointers to interiors).
+* Fix thread-related tests for pthreads-w32.
+* test_cpp: Fix WinMain to prevent SEGV if zero arguments passed (MinGW).
+
+
+== [7.2c] 2012-06-11 ==
+
+* Fix CORD_cat_char_star to prevent SEGV in case of out-of-memory.
+* Fix GC_FirstDLOpenedLinkMap() for NetBSD 6 release.
+* Fix GC_scratch_alloc and GC_get_maps invocations to prevent SEGV.
+* Fix visibility of GC_clear/set_mark_bit (unhide symbols).
+* Fix visibility of GC_push_all/conditional, GC_push_other_roots symbols.
+
+
+== [7.2b] 2012-05-23 ==
+
+* Fix assertion in GC_malloc_[atomic_]uncollectable (THREADS case only).
+
+
+== [7.2] 2012-05-11 ==
+
+* Abort in GC_thr_init on pthread_atfork failure (POSIX threads).
* Add GC_WIN32_PTHREADS target in configure.
+* Add GC_is_disabled new function to GC API.
* Add info that getcontext() resets FPE mask no longer on Linux/x86_64.
+* Add public GC_set_handle_fork to control forked child handling support.
* Add realloc_test.c test.
* Add support for Hexagon target.
* Add thread-safe GC_get_heap_usage_safe to GC API.
+* Change GC_check_fl_marks prototype and implementation.
* Check pthread_create/join result in test.
* Define GC_DLL (in configure) if building only dynamic libraries.
* Define NO_DEBUGGING (in configure) if "--disable-gc-debug" is set.
+* Disable incremental mode on Darwin if fork handling requested.
* Enable parallel marker in configure for Solaris.
* Fix "comparison of signed and unsigned values" compiler warnings.
+* Fix 'volatile' keyword placement in GC_SysVGetDataStart.
+* Fix ALIGNMENT, CPP_WORDSZ, GC_GRANULE_BYTES/WORDS for x32 target.
+* Fix GC_READ_ENV_FILE code for Cygwin.
+* Fix GC_add_roots_inner for Mac OS X (align segment start).
+* Fix GC_check_fl_marks regarding concurrent access.
+* Fix GC_finalizer_nested size to workaround alignment problem in Watcom.
* Fix GC_find_limit_with_bound to always reset fault handler on return.
+* Fix GC_init static assertion for clang/x64 (Darwin).
* Fix GC_init[_lib_bounds] and GC_get_main_stack_base for malloc redirection.
+* Fix GC_push_all/selected boundaries check.
+* Fix GC_register_my_thread marking thread as detached (Cygwin/pthreads-w32).
+* Fix GC_remove_all_threads_but_me to cleanup thread-specific data storage.
* Fix GC_restart_handler to preserve errno if needed.
+* Fix GC_root_size update in GC_add_roots_inner (Win32).
+* Fix GC_unregister_my_thread to ensure no ongoing incremental GC (Win32).
+* Fix GC_with_callee_saves_pushed for clang (disable __builtin_unwind_init).
+* Fix calloc, GC_generic_malloc to check for allocation size overflows.
* Fix compiler warning in GC_dyld_image_add/remove (Darwin).
* Fix configure --enable-cplusplus make install.
* Fix configure to disable GCC aliasing optimization unless forced to.
* Fix duplicate definitions in gcconfig.h for NetBSD.
+* Fix fork() support on Cygwin and Darwin targets.
+* Fix gc.h compatibility regression regarding GC_PTR, GC_I_HIDE_POINTERS.
* Fix gc_cpp.cc for Cygwin (remove duplicate function definition).
+* Fix gcconfig.h to define USE_GET_STACKBASE_FOR_MAIN for Android.
* Fix gcconfig.h to handle mips64-linux target.
+* Fix gctest (for Win32) to avoid GC_print_stats internal variable usage.
* Fix mach_dep.c to include sys/ucontext.h on Mac OS X 10.6.
* Fix tests to check GC_malloc result for NULL (out-of-memory).
* Fix thread model in configure for MinGW ("win32" instead of "posix").
* Fix various warnings reported by LINT-like tools.
+* Fix visibility of some GC internal symbols used by GNU GCJ currently.
* Port some thread tests to Win32.
* Refine API GC setters and getter comments regarding locking.
+* Refine GC_stackbottom description in gc.h.
* Remove duplicate calls in GC_register_dynamic_libraries.
* Remove locking in API GC_get_bytes_since_gc and friends.
* Remove newly-added GC_get_heap_size/free_bytes_inner from API.
@@ -83,6 +239,7 @@
* Support multi-threading for RTEMS target.
* Use global GC_noop_sink variable in GC_noop1 to suppress compiler warning.
* Use pkg-config to pick up libatomic_ops, etc.
+* Workaround some Linux/arm kernels bug to get correct GC_nprocs value.
== [7.2alpha6] 2011-06-14 ==
@@ -632,7 +789,7 @@ variable.
* configure.ac (THREADDLLIBS): Use alternate thread library on
Solaris 8.
-* configure.ac (need_atomic_ops_asm): Set to true only for Sparc
+* configure.ac (need_atomic_ops_asm): Set to true only for SPARC
Solaris.
* configure.ac: Don't use libdl on mips-sgi-irix6.
@@ -5674,7 +5831,7 @@ at least in the past, imposed hard bounds on the number of threads.
win32_threads.c for those two cases.
* Got rid of the FASTLOCK() machinery. It doesn't seem useful on modern
platforms.
-* Cleaned up the uncollectable allocation routines, speeding up the
+* Cleaned up the uncollectible allocation routines, speeding up the
slower paths. The code did enough unnecessary work off the critical path
that the underlying logic was getting hard to extract.
* No longer turn off THREAD_LOCAL_ALLOC with DBG_HDRS_ALL. Indications
@@ -6291,7 +6448,7 @@ is not in gcc3.3.)
* Attempted to sanitize the various DLL macros. GC_USE_DLL disappeared.
GC_DLL is used instead. All internal tests are now on GC_DLL.
README.macros is now more precise about the intended meaning.
-* Include DllMain in the multithreaded win32 version only if the
+* Include DllMain in the multi-threaded win32 version only if the
collector is actually built as a dll.
* Hide the cygwin threadAttach/Detach functions. They were violating our
namespace rules.
@@ -6450,7 +6607,7 @@ The latter is more reliable and easier on Linux with dl_iterate_phdr.
* Integrated Bo Thorsen's X86-64 support.
* STACKBOTTOM definition for LINUX/MIPS was partially changed back.
* Replaced all occurrences of LINUX_DATA_START in gcconfig.h with
-SEARCH_FOR_DATA_START. It doesn't hurt to falll back to a search.
+SEARCH_FOR_DATA_START. It doesn't hurt to fall back to a search.
And __data_start doesn't seem to get defined correctly of the GC
library is loaded with LD_PRELOAD, e.g. for leak detection.
* If the GC_find_leak environment variable is set, do a
@@ -6569,8 +6726,8 @@ declaration.
dllimport/export defines in gc.h.
* GC_malloc_many didn't set hb_last_reclaimed when it called
GC_reclaim_generic. (I'm not sure this matters much, but ...)
-* Allocating uncollectable objects with debug information sometimes
-allocated objects that were one byte too small, since uncollectable
+* Allocating uncollectible objects with debug information sometimes
+allocated objects that were one byte too small, since uncollectible
objects don't have the extra byte added at the end.
* Added a bit more assertion checking to make sure that gcj objects
on free lists never have a nonzero second word.
@@ -6608,7 +6765,7 @@ compatible with Visual C++ 6.
* Some more patches for Linux on HP PA-RISC.
* Added include/gc_allocator.h. It implements (hopefully) standard
conforming (as opposed to SGI-style) allocators that allocate
-collectable (gc_allocator) or GC-traceable, but not collectable
+collectible (gc_allocator) or GC-traceable, but not collectible
(traceable_allocator) objects. This borrows heavily from libstc++,
which borrows heavily from the SGI implementation, this part of
which was written by Matt Austern. Changed test_cpp.cc to very
@@ -6706,7 +6863,7 @@ that combination anymore.
* Added some support for OpenBSD/ELF/Linux.
* Added Jakub Jelinek's patch to use dl_iterate_phdr for dynamic library
traversal to dyn_load.c. Changed it to weakly reference dl_iterate_phdr,
-so that the old code is stilll used with old versions of glibc.
+so that the old code is still used with old versions of glibc.
* Cleaned up feature test macros for various threads packages and
integrated (partially functional) FreeBSD threads code from Loren Rittle.
It's likely that the cleanup broke something, since it touched lots of
@@ -7415,7 +7572,7 @@ correctly.
an out of memory situation.
* Fixed a syntactic error in the DJGPP code. Also fixed a test program
problem with DJGPP.
-* Atomic uncollectable objects were not treated correctly by the
+* Atomic uncollectible objects were not treated correctly by the
incremental collector. This resulted in weird log statistics and
occasional performance problems.
* Fixed some problems resulting from compilers that don't define
@@ -7521,7 +7678,7 @@ memory.
* Calls to GC_base before the collector was initialized failed on a
DEC Alpha.
* Added base pointer checking to GC_REGISTER_FINALIZER in debugging mode.
-* GC_debug_realloc failed for uncollectable objects.
+* GC_debug_realloc failed for uncollectible objects.
* Explicitly typed allocation could crash if it ran out of memory.
* Added minimal support for a DEC Alpha running Linux.
* Fixed a problem with allocation of objects whose size overflowed
@@ -7532,7 +7689,7 @@ ptrdiff_t. (This now fails unconditionally, as it should.)
* Fixed a serious bug in README.solaris2.
Multi-threaded programs must include
gc.h with SOLARIS_THREADS defined.
-* Changed GC_free so it actually deallocates uncollectable objects.
+* Changed GC_free so it actually deallocates uncollectible objects.
* Added Linux ELF support for dynamic libraries.
* Changed the Borland cc configuration so that the assembler is not
required.
@@ -7836,7 +7993,7 @@ in the presence of ALL_INTERIOR_POINTERS.
the single-threaded case.
* Reduced retry count in GC_collect_or_expand for garbage
collecting when out of memory.
-* Made uncollectable allocations bypass black-listing, as they
+* Made uncollectible allocations bypass black-listing, as they
should.
* Fixed a bug in typed_test in test.c that could cause (legitimate)
GC crashes.
@@ -7992,7 +8149,7 @@ out by Dave Detlefs and Al Dosser.
* A more refined heap expansion policy. Less space usage by default.
* Various minor enhancements to reduce space usage, and to reduce
the amount of memory scanned by the collector.
-* Uncollectable allocation without per object overhead.
+* Uncollectible allocation without per object overhead.
* More conscientious handling of out-of-memory conditions.
* Fixed a bug in debugging stubborn allocation.
* Fixed a bug that resulted in occasional erroneous reporting of smashed
diff --git a/Mac_files/dataend.c b/Mac_files/dataend.c
deleted file mode 100644
index 09d47b39..00000000
--- a/Mac_files/dataend.c
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- dataend.c
-
- A hack to get the extent of global data for the Macintosh.
-
- by Patrick C. Beard.
- */
-
-long __dataend;
diff --git a/Mac_files/datastart.c b/Mac_files/datastart.c
deleted file mode 100644
index ec9f9af7..00000000
--- a/Mac_files/datastart.c
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- datastart.c
-
- A hack to get the extent of global data for the Macintosh.
-
- by Patrick C. Beard.
- */
-
-long __datastart;
diff --git a/Makefile.am b/Makefile.am
index 55d82a21..2aacca29 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -54,7 +54,7 @@ libgc_la_SOURCES = extra/gc.c
else
EXTRA_DIST += extra/gc.c
libgc_la_SOURCES = \
- allchblk.c alloc.c backgraph.c blacklst.c checksums.c dbg_mlc.c \
+ allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \
dyn_load.c finalize.c gc_dlopen.c gcj_mlc.c headers.c \
mach_dep.c malloc.c mallocx.c mark.c mark_rts.c misc.c new_hblk.c \
obj_map.c os_dep.c pcr_interface.c ptr_chck.c real_malloc.c reclaim.c \
@@ -76,6 +76,10 @@ endif
endif
endif
+if MAKE_BACK_GRAPH
+libgc_la_SOURCES += backgraph.c
+endif
+
if ENABLE_DISCLAIM
libgc_la_SOURCES += fnlz_mlc.c
endif
@@ -179,7 +183,11 @@ EXTRA_DIST += BCC_MAKEFILE NT_MAKEFILE \
EXTRA_DIST += tools/add_gc_prefix.c tools/gcname.c tools/if_mach.c \
tools/if_not_there.c tools/setjmp_t.c tools/threadlibs.c \
gc.mak extra/MacOS.c extra/AmigaOS.c \
- Mac_files/datastart.c Mac_files/dataend.c Mac_files/MacOS_config.h \
+ extra/symbian/global_end.cpp extra/symbian/global_start.cpp \
+ extra/symbian/init_global_static_roots.cpp extra/symbian.cpp \
+ build/s60v3/bld.inf build/s60v3/libgc.mmp \
+ extra/Mac_files/datastart.c extra/Mac_files/dataend.c \
+ extra/Mac_files/MacOS_config.h \
include/private/msvc_dbg.h extra/msvc_dbg.c tools/callprocs.sh
#
diff --git a/Makefile.direct b/Makefile.direct
index 9fe5e67b..b5c1a642 100644
--- a/Makefile.direct
+++ b/Makefile.direct
@@ -115,8 +115,8 @@ DOC_FILES= README.QUICK TODO doc/README.Mac doc/README.OS2 \
doc/finalization.html doc/porting.html doc/overview.html \
doc/README.dj doc/README.hp doc/README.linux doc/README.rs6000 \
doc/README.sgi doc/README.solaris2 doc/README.uts \
- doc/README.win32 doc/barrett_diagram \
- README AUTHORS doc/gc.man \
+ doc/README.symbian doc/README.win32 doc/barrett_diagram \
+ README.md AUTHORS doc/gc.man \
doc/README.environment doc/tree.html doc/gcdescr.html \
doc/README.autoconf doc/README.macros doc/README.ews4800 \
doc/README.DGUX386 doc/README.arm.cross doc/leak.html \
@@ -142,8 +142,11 @@ OTHER_MAKEFILES= OS2_MAKEFILE NT_MAKEFILE gc.mak \
build_atomic_ops.sh build_atomic_ops.sh.cygwin
OTHER_FILES= tools/setjmp_t.c tools/callprocs.sh extra/MacOS.c \
- Mac_files/datastart.c Mac_files/dataend.c Mac_files/MacOS_config.h \
- tools/add_gc_prefix.c gc_cpp.cpp \
+ extra/Mac_files/datastart.c extra/Mac_files/dataend.c \
+ extra/Mac_files/MacOS_config.h tools/add_gc_prefix.c gc_cpp.cpp \
+ extra/symbian/global_end.cpp extra/symbian/global_start.cpp \
+ extra/symbian/init_global_static_roots.cpp extra/symbian.cpp \
+ build/s60v3/bld.inf build/s60v3/libgc.mmp \
extra/AmigaOS.c extra/msvc_dbg.c include/private/msvc_dbg.h \
$(TESTS) $(GNU_BUILD_FILES) $(OTHER_MAKEFILES)
diff --git a/Makefile.dj b/Makefile.dj
index b7658cca..aea59bfe 100644
--- a/Makefile.dj
+++ b/Makefile.dj
@@ -62,13 +62,13 @@ SRCS= $(CSRCS) \
include/leak_detector.h $(CORD_SRCS)
OTHER_FILES= PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \
- README tests/test.c test_cpp.cc tools/setjmp_t.c SMakefile.amiga \
+ README.md tests/test.c test_cpp.cc tools/setjmp_t.c SMakefile.amiga \
doc/README.amiga doc/README.win32 doc/README.cords \
doc/README.rs6000 README.QUICK TODO tools/callprocs.sh \
pc_excludes barrett_diagram doc/README.OS2 doc/README.Mac \
- extra/MacOS.c EMX_MAKEFILE \
- doc/README.debugging Mac_files/datastart.c Mac_files/dataend.c \
- Mac_files/MacOS_config.h \
+ extra/MacOS.c EMX_MAKEFILE doc/debugging.html \
+ extra/Mac_files/datastart.c extra/Mac_files/dataend.c \
+ extra/Mac_files/MacOS_config.h \
tools/add_gc_prefix.c doc/README.solaris2 doc/README.sgi \
doc/README.hp doc/README.uts win32_threads.c gc.mak doc/README.dj \
Makefile.dj doc/README.alpha doc/README.linux WCC_MAKEFILE
diff --git a/NT_X64_THREADS_MAKEFILE b/NT_X64_THREADS_MAKEFILE
index d56dd5b5..b8495d08 100644
--- a/NT_X64_THREADS_MAKEFILE
+++ b/NT_X64_THREADS_MAKEFILE
@@ -35,7 +35,7 @@ all: gc64.dll gctest.exe cord\de.exe test_cpp.exe
# Disable crt security warnings, since unfortunately they warn about all sorts # of safe uses of strncpy. It would be nice to leave the rest enabled.
.cpp.obj:
- $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DALL_INTERIOR_POINTERS -DGC_DLL -DGC_THREADS -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj
+ $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_DLL -DGC_THREADS -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj
$(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\gc_disclaim.h include\private\msvc_dbg.h
diff --git a/README.QUICK b/README.QUICK
index 30e67245..1f419cfd 100644
--- a/README.QUICK
+++ b/README.QUICK
@@ -18,11 +18,11 @@ that appears not to significantly restrict use of the collector, though
use of those files for a purpose other than building the collector may
require the resulting code to be covered by the GPL.
-For more details and the names of other contributors, see the README,
+For more details and the names of other contributors, see the README.md,
doc/README.*, AUTHORS and include/gc.h files. These files describe typical
use of the collector on a machine that is already supported.
-For the version number, see README or include/gc_version.h files.
+For the version number, see README.md or include/gc_version.h files.
INSTALLATION:
Under UN*X, Linux:
@@ -34,7 +34,7 @@ Alternative 1 (the old way): type "make test" in this directory.
Alternative 2 (the new way): type
"./configure --prefix=<dir>; make; make check; make install".
Link against <dir>/lib/libgc.a or <dir>/lib/libgc.so.
- See README.autoconf for details
+ See doc/README.autoconf for details
Under Windows 95, 98, Me, NT, or 2000:
copy the appropriate makefile to MAKEFILE, read it, and type "nmake test".
@@ -71,7 +71,7 @@ Define GC_DEBUG before including gc.h for additional checking.
More documentation on the collector interface can be found at
http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html,
-in README and other files in the doc directory, and in include/gc.h file.
+in README.md and other files in the doc directory, and in include/gc.h file.
WARNINGS:
diff --git a/README b/README.md
index 5a972f5f..c87b7d15 100644
--- a/README
+++ b/README.md
@@ -1,89 +1,51 @@
-Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers
-Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved.
-Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
-Copyright (c) 1999-2011 by Hewlett-Packard Development Company.
+# Boehm-Demers-Weiser Garbage Collector
-The file linux_threads.c is also
-Copyright (c) 1998 by Fergus Henderson. All rights reserved.
-
-The files Makefile.am, and configure.in are
-Copyright (c) 2001 by Red Hat Inc. All rights reserved.
-
-Several files supporting GNU-style builds are copyrighted by the Free
-Software Foundation, and carry a different license from that given
-below. The files included in the libatomic_ops distribution (included
-here) use either the license below, or a similar MIT-style license,
-or, for some files not actually used by the garbage-collector library, the
-GPL.
+This is version 7.3alpha3 of a conservative garbage collector for C and C++.
-THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
-OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+You might find a more recent version
+[here](http://www.hpl.hp.com/personal/Hans_Boehm/gc).
-Permission is hereby granted to use or copy this program
-for any purpose, provided the above notices are retained on all copies.
-Permission to modify the code and to distribute modified code is granted,
-provided the above notices are retained, and a notice that the code was
-modified is included with the above copyright notice.
+## Overview
-A few of the files needed to use the GNU-style build procedure come with
-slightly different licenses, though they are all similar in spirit. A few
-are GPL'ed, but with an exception that should cover all uses in the
-collector. (If you are concerned about such things, I recommend you look
-at the notice in config.guess or ltmain.sh.)
-
-The atomic_ops library contains some code that is covered by the GNU General
-Public License, but is not needed by, nor linked into the collector library.
-It is included here only because the atomic_ops distribution is, for
-simplicity, included in its entirety.
-
-This is version 7.3alpha1 of a conservative garbage collector for C and C++.
-
-You might find a more recent version of this at
-
-http://www.hpl.hp.com/personal/Hans_Boehm/gc
-
-OVERVIEW
-
- This is intended to be a general purpose, garbage collecting storage
+This is intended to be a general purpose, garbage collecting storage
allocator. The algorithms used are described in:
-Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment",
-Software Practice & Experience, September 1988, pp. 807-820.
+ * Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative
+ Environment", Software Practice & Experience, September 1988, pp. 807-820.
-Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
-Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design
-and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
+ * Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
+ Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design
+ and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
-Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
-of the ACM SIGPLAN '91 Conference on Programming Language Design and
-Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
+ * Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
+ of the ACM SIGPLAN '91 Conference on Programming Language Design and
+ Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
-Boehm H., "Reducing Garbage Collector Cache Misses", Proceedings of the
-2000 International Symposium on Memory Management.
+ * Boehm H., "Reducing Garbage Collector Cache Misses", Proceedings of the
+ 2000 International Symposium on Memory Management.
- Possible interactions between the collector and optimizing compilers are
+Possible interactions between the collector and optimizing compilers are
discussed in
-Boehm, H., and D. Chase, "A Proposal for GC-safe C Compilation",
-The Journal of C Language Translation 4, 2 (December 1992).
+ * Boehm, H., and D. Chase, "A Proposal for GC-safe C Compilation",
+ The Journal of C Language Translation 4, 2 (December 1992).
and
-Boehm H., "Simple GC-safe Compilation", Proceedings
-of the ACM SIGPLAN '96 Conference on Programming Language Design and
-Implementation.
+ * Boehm H., "Simple GC-safe Compilation", Proceedings of the ACM SIGPLAN '96
+ Conference on Programming Language Design and Implementation.
(Some of these are also available from
-http://www.hpl.hp.com/personal/Hans_Boehm/papers/, among other places.)
+[here](http://www.hpl.hp.com/personal/Hans_Boehm/papers/), among other places.)
- Unlike the collector described in the second reference, this collector
+Unlike the collector described in the second reference, this collector
operates either with the mutator stopped during the entire collection
(default) or incrementally during allocations. (The latter is supported
on fewer machines.) On the most common platforms, it can be built
with or without thread support. On a few platforms, it can take advantage
of a multiprocessor to speed up garbage collection.
- Many of the ideas underlying the collector have previously been explored
+Many of the ideas underlying the collector have previously been explored
by others. Notably, some of the run-time systems developed at Xerox PARC
in the early 1980s conservatively scanned thread stacks to locate possible
pointers (cf. Paul Rovner, "On Adding Garbage Collection and Runtime Types
@@ -92,202 +54,203 @@ CSL 84-7). Doug McIlroy wrote a simpler fully conservative collector that
was part of version 8 UNIX (tm), but appears to not have received
widespread use.
- Rudimentary tools for use of the collector as a leak detector are included
-(see http://www.hpl.hp.com/personal/Hans_Boehm/gc/leak.html),
+Rudimentary tools for use of the collector as a leak detector are included
+([link](http://www.hpl.hp.com/personal/Hans_Boehm/gc/leak.html)),
as is a fairly sophisticated string package "cord" that makes use of the
collector. (See doc/README.cords and H.-J. Boehm, R. Atkinson, and M. Plass,
"Ropes: An Alternative to Strings", Software Practice and Experience 25, 12
(December 1995), pp. 1315-1330. This is very similar to the "rope" package
in Xerox Cedar, or the "rope" package in the SGI STL or the g++ distribution.)
-Further collector documentation can be found at
-
-http://www.hpl.hp.com/personal/Hans_Boehm/gc
+Further collector documentation can be found
+[here](http://www.hpl.hp.com/personal/Hans_Boehm/gc).
-GENERAL DESCRIPTION
+## General Description
- This is a garbage collecting storage allocator that is intended to be
+This is a garbage collecting storage allocator that is intended to be
used as a plug-in replacement for C's malloc.
- Since the collector does not require pointers to be tagged, it does not
+Since the collector does not require pointers to be tagged, it does not
attempt to ensure that all inaccessible storage is reclaimed. However,
in our experience, it is typically more successful at reclaiming unused
memory than most C programs using explicit deallocation. Unlike manually
introduced leaks, the amount of unreclaimed memory typically stays
bounded.
- In the following, an "object" is defined to be a region of memory allocated
+In the following, an "object" is defined to be a region of memory allocated
by the routines described below.
- Any objects not intended to be collected must be pointed to either
+Any objects not intended to be collected must be pointed to either
from other such accessible objects, or from the registers,
stack, data, or statically allocated bss segments. Pointers from
the stack or registers may point to anywhere inside an object.
The same is true for heap pointers if the collector is compiled with
-ALL_INTERIOR_POINTERS defined, or GC_all_interior_pointers is otherwise
+`ALL_INTERIOR_POINTERS` defined, or `GC_all_interior_pointers` is otherwise
set, as is now the default.
-Compiling without ALL_INTERIOR_POINTERS may reduce accidental retention
+Compiling without `ALL_INTERIOR_POINTERS` may reduce accidental retention
of garbage objects, by requiring pointers from the heap to the beginning
of an object. But this no longer appears to be a significant
issue for most programs occupying a small fraction of the possible
address space.
There are a number of routines which modify the pointer recognition
-algorithm. GC_register_displacement allows certain interior pointers
-to be recognized even if ALL_INTERIOR_POINTERS is nor defined.
-GC_malloc_ignore_off_page allows some pointers into the middle of large objects
-to be disregarded, greatly reducing the probability of accidental
-retention of large objects. For most purposes it seems best to compile
-with ALL_INTERIOR_POINTERS and to use GC_malloc_ignore_off_page if
-you get collector warnings from allocations of very large objects.
-See README.debugging for details.
-
- WARNING: pointers inside memory allocated by the standard "malloc" are not
+algorithm. `GC_register_displacement` allows certain interior pointers
+to be recognized even if `ALL_INTERIOR_POINTERS` is nor defined.
+`GC_malloc_ignore_off_page` allows some pointers into the middle of
+large objects to be disregarded, greatly reducing the probability of
+accidental retention of large objects. For most purposes it seems
+best to compile with `ALL_INTERIOR_POINTERS` and to use
+`GC_malloc_ignore_off_page` if you get collector warnings from
+allocations of very large objects. See doc/debugging.html for details.
+
+_WARNING_: pointers inside memory allocated by the standard `malloc` are not
seen by the garbage collector. Thus objects pointed to only from such a
region may be prematurely deallocated. It is thus suggested that the
-standard "malloc" be used only for memory regions, such as I/O buffers, that
-are guaranteed not to contain pointers to garbage collectable memory.
+standard `malloc` be used only for memory regions, such as I/O buffers, that
+are guaranteed not to contain pointers to garbage collectible memory.
Pointers in C language automatic, static, or register variables,
-are correctly recognized. (Note that GC_malloc_uncollectable has semantics
-similar to standard malloc, but allocates objects that are traced by the
-collector.)
+are correctly recognized. (Note that `GC_malloc_uncollectable` has
+semantics similar to standard malloc, but allocates objects that are
+traced by the collector.)
- WARNING: the collector does not always know how to find pointers in data
+_WARNING_: the collector does not always know how to find pointers in data
areas that are associated with dynamic libraries. This is easy to
remedy IF you know how to find those data areas on your operating
-system (see GC_add_roots). Code for doing this under SunOS, IRIX 5.X and 6.X,
-HP/UX, Alpha OSF/1, Linux, and win32 is included and used by default. (See
-README.win32 for win32 details.) On other systems pointers from dynamic
-library data areas may not be considered by the collector.
-If you're writing a program that depends on the collector scanning
-dynamic library data areas, it may be a good idea to include at least
-one call to GC_is_visible() to ensure that those areas are visible
-to the collector.
-
- Note that the garbage collector does not need to be informed of shared
+system (see `GC_add_roots`). Code for doing this under SunOS, IRIX
+5.X and 6.X, HP/UX, Alpha OSF/1, Linux, and win32 is included and used
+by default. (See doc/README.win32 for Win32 details.) On other systems
+pointers from dynamic library data areas may not be considered by the
+collector. If you're writing a program that depends on the collector
+scanning dynamic library data areas, it may be a good idea to include
+at least one call to `GC_is_visible` to ensure that those areas are
+visible to the collector.
+
+Note that the garbage collector does not need to be informed of shared
read-only data. However if the shared library mechanism can introduce
discontiguous data areas that may contain pointers, then the collector does
need to be informed.
- Signal processing for most signals may be deferred during collection,
+Signal processing for most signals may be deferred during collection,
and during uninterruptible parts of the allocation process.
Like standard ANSI C mallocs, by default it is unsafe to invoke
malloc (and other GC routines) from a signal handler while another
malloc call may be in progress.
- The allocator/collector can also be configured for thread-safe operation.
+The allocator/collector can also be configured for thread-safe operation.
(Full signal safety can also be achieved, but only at the cost of two system
calls per malloc, which is usually unacceptable.)
-WARNING: the collector does not guarantee to scan thread-local storage
-(e.g. of the kind accessed with pthread_getspecific()). The collector
+
+_WARNING_: the collector does not guarantee to scan thread-local storage
+(e.g. of the kind accessed with `pthread_getspecific`). The collector
does scan thread stacks, though, so generally the best solution is to
ensure that any pointers stored in thread-local storage are also
stored on the thread's stack for the duration of their lifetime.
(This is arguably a longstanding bug, but it hasn't been fixed yet.)
-INSTALLATION AND PORTABILITY
+## Installation and Portability
- As distributed, the collector operates silently
+As distributed, the collector operates silently
In the event of problems, this can usually be changed by defining the
-GC_PRINT_STATS or GC_PRINT_VERBOSE_STATS environment variables. This
+`GC_PRINT_STATS` or `GC_PRINT_VERBOSE_STATS` environment variables. This
will result in a few lines of descriptive output for each collection.
(The given statistics exhibit a few peculiarities.
Things don't appear to add up for a variety of reasons, most notably
fragmentation losses. These are probably much more significant for the
contrived program "test.c" than for your application.)
- On most Un*x-like platforms, the collector can be built either using a
-GNU autoconf-based build infrastructure (type "configure; make" in the
+On most Unix-like platforms, the collector can be built either using a
+GNU autoconf-based build infrastructure (type `configure; make` in the
simplest case), or with a classic makefile by itself (type
-"make -f Makefile.direct"). Here we focus on the latter option.
+`make -f Makefile.direct`). Here we focus on the latter option.
On other platforms, typically only the latter option is available, though
with a different supplied Makefile.)
- For the Makefile.direct-based process, typing "make test" instead of "make"
-will automatically build the collector and then run setjmp_test and gctest.
-Setjmp_test will give you information about configuring the collector, which is
+For the Makefile.direct-based process, typing `make test` instead of `make`
+will automatically build the collector and then run `setjmp_test` and `gctest`.
+`Setjmp_test` will give you information about configuring the collector, which is
useful primarily if you have a machine that's not already supported. Gctest is
a somewhat superficial test of collector functionality. Failure is indicated
by a core dump or a message to the effect that the collector is broken. Gctest
takes about a second to two to run on reasonable 2007 vintage desktops. It may
use up to about 30MB of memory. (The multi-threaded version will use more.
-64-bit versions may use more.) "Make test" will also, as its last step, attempt
+64-bit versions may use more.) `make test` will also, as its last step, attempt
to build and test the "cord" string library.)
- Makefile.direct will generate a library gc.a which you should link against.
+Makefile.direct will generate a library gc.a which you should link against.
Typing "make cords" will add the cord library to gc.a.
- The GNU style build process understands the usual targets. "Make check"
-runs a number of tests. "Make install" installs at least libgc, and libcord.
-Try "./configure --help" to see the configuration options. It is currently
+The GNU style build process understands the usual targets. `make check`
+runs a number of tests. `make install` installs at least libgc, and libcord.
+Try `./configure --help` to see the configuration options. It is currently
not possible to exercise all combinations of build options this way.
- It is suggested that if you need to replace a piece of the collector
+It is suggested that if you need to replace a piece of the collector
(e.g. GC_mark_rts.c) you simply list your version ahead of gc.a on the
ld command line, rather than replacing the one in gc.a. (This will
generate numerous warnings under some versions of AIX, but it still
works.)
- All include files that need to be used by clients will be put in the
-include subdirectory. (Normally this is just gc.h. "Make cords" adds
+All include files that need to be used by clients will be put in the
+include subdirectory. (Normally this is just gc.h. `make cords` adds
"cord.h" and "ec.h".)
- The collector currently is designed to run essentially unmodified on
+The collector currently is designed to run essentially unmodified on
machines that use a flat 32-bit or 64-bit address space.
That includes the vast majority of Workstations and X86 (X >= 3) PCs.
(The list here was deleted because it was getting too long and constantly
out of date.)
- In a few cases (Amiga, OS/2, Win32, MacOS) a separate makefile
+In a few cases (Amiga, OS/2, Win32, MacOS) a separate makefile
or equivalent is supplied. Many of these have separate README.system
files.
- Dynamic libraries are completely supported only under SunOS/Solaris,
+Dynamic libraries are completely supported only under SunOS/Solaris,
(and even that support is not functional on the last Sun 3 release),
Linux, FreeBSD, NetBSD, IRIX 5&6, HP/UX, Win32 (not Win32S) and OSF/1
on DEC AXP machines plus perhaps a few others listed near the top
of dyn_load.c. On other machines we recommend that you do one of
the following:
- 1) Add dynamic library support (and send us the code).
- 2) Use static versions of the libraries.
- 3) Arrange for dynamic libraries to use the standard malloc.
- This is still dangerous if the library stores a pointer to a
- garbage collected object. But nearly all standard interfaces
- prohibit this, because they deal correctly with pointers
- to stack allocated objects. (Strtok is an exception. Don't
- use it.)
+ 1) Add dynamic library support (and send us the code).
+ 2) Use static versions of the libraries.
+ 3) Arrange for dynamic libraries to use the standard malloc.
+ This is still dangerous if the library stores a pointer to a
+ garbage collected object. But nearly all standard interfaces
+ prohibit this, because they deal correctly with pointers
+ to stack allocated objects. (Strtok is an exception. Don't
+ use it.)
- In all cases we assume that pointer alignment is consistent with that
+In all cases we assume that pointer alignment is consistent with that
enforced by the standard C compilers. If you use a nonstandard compiler
you may have to adjust the alignment parameters defined in gc_priv.h.
Note that this may also be an issue with packed records/structs, if those
enforce less alignment for pointers.
- A port to a machine that is not byte addressed, or does not use 32 bit
+A port to a machine that is not byte addressed, or does not use 32 bit
or 64 bit addresses will require a major effort. A port to plain MSDOS
or win16 is hard.
- For machines not already mentioned, or for nonstandard compilers,
-some porting suggestions are provided in the "porting.html" file.
-
-THE C INTERFACE TO THE ALLOCATOR
-
- The following routines are intended to be directly called by the user.
-Note that usually only GC_malloc is necessary. GC_clear_roots and GC_add_roots
-calls may be required if the collector has to trace from nonstandard places
-(e.g. from dynamic library data areas on a machine on which the
-collector doesn't already understand them.) On some machines, it may
-be desirable to set GC_stacktop to a good approximation of the stack base.
-(This enhances code portability on HP PA machines, since there is no
-good way for the collector to compute this value.) Client code may include
-"gc.h", which defines all of the following, plus many others.
-
-1) GC_malloc(nbytes)
- - allocate an object of size nbytes. Unlike malloc, the object is
- cleared before being returned to the user. Gc_malloc will
+For machines not already mentioned, or for nonstandard compilers,
+some porting suggestions are provided in doc/porting.html.
+
+## The C Interface to the Allocator
+
+The following routines are intended to be directly called by the user.
+Note that usually only `GC_malloc` is necessary. `GC_clear_roots` and
+`GC_add_roots` calls may be required if the collector has to trace
+from nonstandard places (e.g. from dynamic library data areas on a
+machine on which the collector doesn't already understand them.) On
+some machines, it may be desirable to set `GC_stacktop` to a good
+approximation of the stack base. (This enhances code portability on
+HP PA machines, since there is no good way for the collector to
+compute this value.) Client code may include "gc.h", which defines
+all of the following, plus many others.
+
+ 1) `GC_malloc(nbytes)`
+ - Allocate an object of size nbytes. Unlike malloc, the object is
+ cleared before being returned to the user. `GC_malloc` will
invoke the garbage collector when it determines this to be appropriate.
GC_malloc may return 0 if it is unable to acquire sufficient
space from the operating system. This is the most probable
@@ -298,117 +261,118 @@ good way for the collector to compute this value.) Client code may include
process will fail and take down the machine. Most of these
possibilities are independent of the malloc implementation.
-2) GC_malloc_atomic(nbytes)
- - allocate an object of size nbytes that is guaranteed not to contain any
- pointers. The returned object is not guaranteed to be cleared.
- (Can always be replaced by GC_malloc, but results in faster collection
- times. The collector will probably run faster if large character
- arrays, etc. are allocated with GC_malloc_atomic than if they are
- statically allocated.)
+ 2) `GC_malloc_atomic(nbytes)`
+ - Allocate an object of size nbytes that is guaranteed not to contain any
+ pointers. The returned object is not guaranteed to be cleared.
+ (Can always be replaced by `GC_malloc`, but results in faster collection
+ times. The collector will probably run faster if large character
+ arrays, etc. are allocated with `GC_malloc_atomic` than if they are
+ statically allocated.)
-3) GC_realloc(object, new_size)
- - change the size of object to be new_size. Returns a pointer to the
+ 3) `GC_realloc(object, new_size)`
+ - Change the size of object to be `new_size`. Returns a pointer to the
new object, which may, or may not, be the same as the pointer to
- the old object. The new object is taken to be atomic iff the old one
- was. If the new object is composite and larger than the original object,
- then the newly added bytes are cleared (we hope). This is very likely
- to allocate a new object, unless MERGE_SIZES is defined in gc_priv.h.
- Even then, it is likely to recycle the old object only if the object
- is grown in small additive increments (which, we claim, is generally bad
- coding practice.)
-
-4) GC_free(object)
- - explicitly deallocate an object returned by GC_malloc or
- GC_malloc_atomic. Not necessary, but can be used to minimize
+ the old object. The new object is taken to be atomic if and only if the
+ old one was. If the new object is composite and larger than the original
+ object,then the newly added bytes are cleared (we hope). This is very
+ likely to allocate a new object, unless `MERGE_SIZES` is defined in
+ gc_priv.h. Even then, it is likely to recycle the old object only if the
+ object is grown in small additive increments (which, we claim, is
+ generally bad coding practice.)
+
+ 4) `GC_free(object)`
+ - Explicitly deallocate an object returned by `GC_malloc` or
+ `GC_malloc_atomic`. Not necessary, but can be used to minimize
collections if performance is critical. Probably a performance
loss for very small objects (<= 8 bytes).
-5) GC_expand_hp(bytes)
- - Explicitly increase the heap size. (This is normally done automatically
- if a garbage collection failed to GC_reclaim enough memory. Explicit
- calls to GC_expand_hp may prevent unnecessarily frequent collections at
- program startup.)
-
-6) GC_malloc_ignore_off_page(bytes)
- - identical to GC_malloc, but the client promises to keep a pointer to
- the somewhere within the first 256 bytes of the object while it is
- live. (This pointer should normally be declared volatile to prevent
- interference from compiler optimizations.) This is the recommended
- way to allocate anything that is likely to be larger than 100Kbytes
- or so. (GC_malloc may result in failure to reclaim such objects.)
-
-7) GC_set_warn_proc(proc)
- - Can be used to redirect warnings from the collector. Such warnings
- should be rare, and should not be ignored during code development.
-
-8) GC_enable_incremental()
- - Enables generational and incremental collection. Useful for large
- heaps on machines that provide access to page dirty information.
- Some dirty bit implementations may interfere with debugging
- (by catching address faults) and place restrictions on heap arguments
- to system calls (since write faults inside a system call may not be
- handled well).
-
-9) Several routines to allow for registration of finalization code.
- User supplied finalization code may be invoked when an object becomes
- unreachable. To call (*f)(obj, x) when obj becomes inaccessible, use
- GC_register_finalizer(obj, f, x, 0, 0);
- For more sophisticated uses, and for finalization ordering issues,
- see gc.h.
-
- The global variable GC_free_space_divisor may be adjusted up from its
-default value of 4 to use less space and more collection time, or down for
-the opposite effect. Setting it to 1 or 0 will effectively disable collections
+ 5) `GC_expand_hp(bytes)`
+ - Explicitly increase the heap size. (This is normally done automatically
+ if a garbage collection failed to `GC_reclaim` enough memory. Explicit
+ calls to `GC_expand_hp` may prevent unnecessarily frequent collections at
+ program startup.)
+
+ 6) `GC_malloc_ignore_off_page(bytes)`
+ - Identical to `GC_malloc`, but the client promises to keep a pointer to
+ the somewhere within the first 256 bytes of the object while it is
+ live. (This pointer should normally be declared volatile to prevent
+ interference from compiler optimizations.) This is the recommended
+ way to allocate anything that is likely to be larger than 100 Kbytes
+ or so. (`GC_malloc` may result in failure to reclaim such objects.)
+
+ 7) `GC_set_warn_proc(proc)`
+ - Can be used to redirect warnings from the collector. Such warnings
+ should be rare, and should not be ignored during code development.
+
+ 8) `GC_enable_incremental()`
+ - Enables generational and incremental collection. Useful for large
+ heaps on machines that provide access to page dirty information.
+ Some dirty bit implementations may interfere with debugging
+ (by catching address faults) and place restrictions on heap arguments
+ to system calls (since write faults inside a system call may not be
+ handled well).
+
+ 9) Several routines to allow for registration of finalization code.
+ User supplied finalization code may be invoked when an object becomes
+ unreachable. To call `(*f)(obj, x)` when obj becomes inaccessible, use
+ `GC_register_finalizer(obj, f, x, 0, 0);`
+ For more sophisticated uses, and for finalization ordering issues,
+ see gc.h.
+
+The global variable `GC_free_space_divisor` may be adjusted up from it
+default value of 3 to use less space and more collection time, or down for
+the opposite effect. Setting it to 1 will almost disable collections
and cause all allocations to simply grow the heap.
- The variable GC_non_gc_bytes, which is normally 0, may be changed to reflect
+The variable `GC_non_gc_bytes`, which is normally 0, may be changed to reflect
the amount of memory allocated by the above routines that should not be
considered as a candidate for collection. Careless use may, of course, result
in excessive memory consumption.
- Some additional tuning is possible through the parameters defined
+Some additional tuning is possible through the parameters defined
near the top of gc_priv.h.
- If only GC_malloc is intended to be used, it might be appropriate to define:
+If only `GC_malloc` is intended to be used, it might be appropriate to define:
-#define malloc(n) GC_malloc(n)
-#define calloc(m,n) GC_malloc((m)*(n))
+ #define malloc(n) GC_malloc(n)
+ #define calloc(m,n) GC_malloc((m)*(n))
- For small pieces of VERY allocation intensive code, gc_inl.h
-includes some allocation macros that may be used in place of GC_malloc
-and friends.
+For small pieces of VERY allocation intensive code, gc_inl.h includes
+some allocation macros that may be used in place of `GC_malloc` and
+friends.
- All externally visible names in the garbage collector start with "GC_".
+All externally visible names in the garbage collector start with `GC_`.
To avoid name conflicts, client code should avoid this prefix, except when
accessing garbage collector routines or variables.
- There are provisions for allocation with explicit type information.
+There are provisions for allocation with explicit type information.
This is rarely necessary. Details can be found in gc_typed.h.
-THE C++ INTERFACE TO THE ALLOCATOR:
+## The C++ Interface to the Allocator
- The Ellis-Hull C++ interface to the collector is included in
+The Ellis-Hull C++ interface to the collector is included in
the collector distribution. If you intend to use this, type
-"make c++" after the initial build of the collector is complete.
+`make c++` after the initial build of the collector is complete.
See gc_cpp.h for the definition of the interface. This interface
tries to approximate the Ellis-Detlefs C++ garbage collection
proposal without compiler changes.
- Very often it will also be necessary to use gc_allocator.h and the
+Very often it will also be necessary to use gc_allocator.h and the
allocator declared there to construct STL data structures. Otherwise
subobjects of STL data structures will be allocated using a system
allocator, and objects they refer to may be prematurely collected.
-USE AS LEAK DETECTOR:
+## Use as Leak Detector
- The collector may be used to track down leaks in C programs that are
+The collector may be used to track down leaks in C programs that are
intended to run with malloc/free (e.g. code with extreme real-time or
-portability constraints). To do so define FIND_LEAK in Makefile.
-This will cause the collector to invoke the report_leak
+portability constraints). To do so define `FIND_LEAK` in Makefile.
+This will cause the collector to invoke the `report_leak`
routine defined near the top of reclaim.c whenever an inaccessible
object is found that has not been explicitly freed. Such objects will
also be automatically reclaimed.
- If all objects are allocated with GC_DEBUG_MALLOC (see next section), then
+
+If all objects are allocated with `GC_DEBUG_MALLOC` (see next section), then
the default version of report_leak will report at least the source file and
line number at which the leaked object was allocated. This may sometimes be
sufficient. (On a few machines, it will also report a cryptic stack trace.
@@ -416,63 +380,64 @@ If this is not symbolic, it can sometimes be called into a symbolic stack
trace by invoking program "foo" with "tools/callprocs.sh foo". It is a short
shell script that invokes adb to expand program counter values to symbolic
addresses. It was largely supplied by Scott Schwartz.)
- Note that the debugging facilities described in the next section can
+
+Note that the debugging facilities described in the next section can
sometimes be slightly LESS effective in leak finding mode, since in
-leak finding mode, GC_debug_free actually results in reuse of the object.
+leak finding mode, `GC_debug_free` actually results in reuse of the object.
(Otherwise the object is simply marked invalid.) Also note that the test
-program is not designed to run meaningfully in FIND_LEAK mode.
+program is not designed to run meaningfully in `FIND_LEAK` mode.
Use "make gc.a" to build the collector.
-DEBUGGING FACILITIES:
+## Debugging Facilities
- The routines GC_debug_malloc, GC_debug_malloc_atomic, GC_debug_realloc,
-and GC_debug_free provide an alternate interface to the collector, which
+The routines `GC_debug_malloc`, `GC_debug_malloc_atomic`, `GC_debug_realloc`,
+and `GC_debug_free` provide an alternate interface to the collector, which
provides some help with memory overwrite errors, and the like.
Objects allocated in this way are annotated with additional
information. Some of this information is checked during garbage
collections, and detected inconsistencies are reported to stderr.
- Simple cases of writing past the end of an allocated object should
+Simple cases of writing past the end of an allocated object should
be caught if the object is explicitly deallocated, or if the
collector is invoked while the object is live. The first deallocation
of an object will clear the debugging info associated with an
-object, so accidentally repeated calls to GC_debug_free will report the
+object, so accidentally repeated calls to `GC_debug_free` will report the
deallocation of an object without debugging information. Out of
-memory errors will be reported to stderr, in addition to returning NULL.
+memory errors will be reported to stderr, in addition to returning `NULL`.
- GC_debug_malloc checking during garbage collection is enabled
-with the first call to GC_debug_malloc. This will result in some
+`GC_debug_malloc` checking during garbage collection is enabled
+with the first call to `GC_debug_malloc`. This will result in some
slowdown during collections. If frequent heap checks are desired,
-this can be achieved by explicitly invoking GC_gcollect, e.g. from
+this can be achieved by explicitly invoking `GC_gcollect`, e.g. from
the debugger.
- GC_debug_malloc allocated objects should not be passed to GC_realloc
-or GC_free, and conversely. It is however acceptable to allocate only
-some objects with GC_debug_malloc, and to use GC_malloc for other objects,
+`GC_debug_malloc` allocated objects should not be passed to `GC_realloc`
+or `GC_free`, and conversely. It is however acceptable to allocate only
+some objects with `GC_debug_malloc`, and to use `GC_malloc` for other objects,
provided the two pools are kept distinct. In this case, there is a very
-low probability that GC_malloc allocated objects may be misidentified as
+low probability that `GC_malloc` allocated objects may be misidentified as
having been overwritten. This should happen with probability at most
-one in 2**32. This probability is zero if GC_debug_malloc is never called.
+one in 2**32. This probability is zero if `GC_debug_malloc` is never called.
- GC_debug_malloc, GC_malloc_atomic, and GC_debug_realloc take two
+`GC_debug_malloc`, `GC_malloc_atomic`, and `GC_debug_realloc` take two
additional trailing arguments, a string and an integer. These are not
interpreted by the allocator. They are stored in the object (the string is
not copied). If an error involving the object is detected, they are printed.
- The macros GC_MALLOC, GC_MALLOC_ATOMIC, GC_REALLOC, GC_FREE, and
-GC_REGISTER_FINALIZER are also provided. These require the same arguments
+The macros `GC_MALLOC`, `GC_MALLOC_ATOMIC`, `GC_REALLOC`, `GC_FREE`, and
+`GC_REGISTER_FINALIZER` are also provided. These require the same arguments
as the corresponding (nondebugging) routines. If gc.h is included
-with GC_DEBUG defined, they call the debugging versions of these
+with `GC_DEBUG` defined, they call the debugging versions of these
functions, passing the current file name and line number as the two
-extra arguments, where appropriate. If gc.h is included without GC_DEBUG
+extra arguments, where appropriate. If gc.h is included without `GC_DEBUG`
defined, then all these macros will instead be defined to their nondebugging
-equivalents. (GC_REGISTER_FINALIZER is necessary, since pointers to
+equivalents. (`GC_REGISTER_FINALIZER` is necessary, since pointers to
objects with debugging information are really pointers to a displacement
of 16 bytes form the object beginning, and some translation is necessary
when finalization routines are invoked. For details, about what's stored
in the header, see the definition of the type oh in debug_malloc.c)
-INCREMENTAL/GENERATIONAL COLLECTION:
+## Incremental/Generational Collection
The collector normally interrupts client code for the duration of
a garbage collection mark phase. This may be unacceptable if interactive
@@ -481,9 +446,9 @@ can also run in a "generational" mode, in which it usually attempts to
collect only objects allocated since the last garbage collection.
Furthermore, in this mode, garbage collections run mostly incrementally,
with a small amount of work performed in response to each of a large number of
-GC_malloc requests.
+`GC_malloc` requests.
-This mode is enabled by a call to GC_enable_incremental().
+This mode is enabled by a call to `GC_enable_incremental`.
Incremental and generational collection is effective in reducing
pause times only if the collector has some way to tell which objects
@@ -491,26 +456,26 @@ or pages have been recently modified. The collector uses two sources
of information:
1. Information provided by the VM system. This may be provided in
-one of several forms. Under Solaris 2.X (and potentially under other
-similar systems) information on dirty pages can be read from the
-/proc file system. Under other systems (currently SunOS4.X) it is
-possible to write-protect the heap, and catch the resulting faults.
-On these systems we require that system calls writing to the heap
-(other than read) be handled specially by client code.
-See os_dep.c for details.
+ one of several forms. Under Solaris 2.X (and potentially under other
+ similar systems) information on dirty pages can be read from the
+ /proc file system. Under other systems (currently SunOS4.X) it is
+ possible to write-protect the heap, and catch the resulting faults.
+ On these systems we require that system calls writing to the heap
+ (other than read) be handled specially by client code.
+ See os_dep.c for details.
2. Information supplied by the programmer. We define "stubborn"
-objects to be objects that are rarely changed. Such an object
-can be allocated (and enabled for writing) with GC_malloc_stubborn.
-Once it has been initialized, the collector should be informed with
-a call to GC_end_stubborn_change. Subsequent writes that store
-pointers into the object must be preceded by a call to
-GC_change_stubborn.
+ objects to be objects that are rarely changed. Such an object
+ can be allocated (and enabled for writing) with `GC_malloc_stubborn`.
+ Once it has been initialized, the collector should be informed with
+ a call to `GC_end_stubborn_change`. Subsequent writes that store
+ pointers into the object must be preceded by a call to
+ `GC_change_stubborn`.
This mechanism performs best for objects that are written only for
initialization, and such that only one stubborn object is writable
at once. It is typically not worth using for short-lived
-objects. Stubborn objects are treated less efficiently than pointerfree
+objects. Stubborn objects are treated less efficiently than pointer-free
(atomic) objects.
A rough rule of thumb is that, in the absence of VM information, garbage
@@ -529,23 +494,69 @@ library routines that do not make the objects themselves available
for writing by client code.
-BUGS:
+## Bugs
- Any memory that does not have a recognizable pointer to it will be
+Any memory that does not have a recognizable pointer to it will be
reclaimed. Exclusive-or'ing forward and backward links in a list
doesn't cut it.
- Some C optimizers may lose the last undisguised pointer to a memory
+
+Some C optimizers may lose the last undisguised pointer to a memory
object as a consequence of clever optimizations. This has almost
never been observed in practice.
- This is not a real-time collector. In the standard configuration,
+
+This is not a real-time collector. In the standard configuration,
percentage of time required for collection should be constant across
heap sizes. But collection pauses will increase for larger heaps.
They will decrease with the number of processors if parallel marking
is enabled.
+
(On 2007 vintage machines, GC times may be on the order of 5 msecs
per MB of accessible memory that needs to be scanned and processor.
Your mileage may vary.) The incremental/generational collection facility
may help in some cases.
- Please address bug reports to gc@linux.hpl.hp.com. If you are
+
+Please address bug reports [here](mailto:gc@linux.hpl.hp.com). If you are
contemplating a major addition, you might also send mail to ask whether
it's already been done (or whether we tried and discarded it).
+
+## Copyright & Warranty
+
+ * Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers
+ * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved.
+ * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
+ * Copyright (c) 1999-2011 by Hewlett-Packard Development Company.
+
+The file linux_threads.c is also
+
+ * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
+
+The files Makefile.am, and configure.in are
+
+* Copyright (c) 2001 by Red Hat Inc. All rights reserved.
+
+Several files supporting GNU-style builds are copyrighted by the Free
+Software Foundation, and carry a different license from that given
+below. The files included in the libatomic_ops distribution (included
+here) use either the license below, or a similar MIT-style license,
+or, for some files not actually used by the garbage-collector library, the
+GPL.
+
+THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+
+Permission is hereby granted to use or copy this program
+for any purpose, provided the above notices are retained on all copies.
+Permission to modify the code and to distribute modified code is granted,
+provided the above notices are retained, and a notice that the code was
+modified is included with the above copyright notice.
+
+A few of the files needed to use the GNU-style build procedure come with
+slightly different licenses, though they are all similar in spirit. A few
+are GPL'ed, but with an exception that should cover all uses in the
+collector. (If you are concerned about such things, I recommend you look
+at the notice in config.guess or ltmain.sh.)
+
+The atomic_ops library contains some code that is covered by the GNU General
+Public License, but is not needed by, nor linked into the collector library.
+It is included here only because the atomic_ops distribution is, for
+simplicity, included in its entirety.
diff --git a/SMakefile.amiga b/SMakefile.amiga
index ebc8eae3..229f184f 100644
--- a/SMakefile.amiga
+++ b/SMakefile.amiga
@@ -52,19 +52,19 @@ CSCOPT= $(OPT) DEFINE AMIGA IGNORE=100 IGNORE=161
all: gctest setjmp_t cord/cordtest
clean:
- delete *.lib gctest setjmp_t *.o *.lnk cord/*.o cord/*.lib cord/*.lnk cord/cordtest
+ delete *.lib gctest setjmp_t *.o *.lnk cord/*.o cord/*.lib cord/*.lnk cord/cordtest
smake
test: setjmp_t gctest cord/cordtest
- setjmp_t
- gctest
- cord/cordtest
+ setjmp_t
+ gctest
+ cord/cordtest
gctest: gc$(CPU).lib GCAmigaOS$(CPU).lib test.o
- $(LINKER) LIB:c.o test.o TO gctest LIB gc$(CPU).lib LIB:sc.lib $(MATHLIB)
+ $(LINKER) LIB:c.o test.o TO gctest LIB gc$(CPU).lib LIB:sc.lib $(MATHLIB)
setjmp_t: setjmp_t.o gc.h
- $(LINKER) LIB:c.o setjmp_t.o to setjmp_t lib LIB:sc.lib
+ $(LINKER) LIB:c.o setjmp_t.o to setjmp_t lib LIB:sc.lib
cord/cordtest: cord/cordtest.o cord/cord$(CPU).lib gc$(CPU).lib
slink LIB:c.o cord/cordtest.o LIB $(MATHLIB) gc$(CPU).lib cord/cord$(CPU).lib LIB:sc.lib TO cord/cordtest
@@ -75,7 +75,7 @@ cord/cordtest: cord/cordtest.o cord/cord$(CPU).lib gc$(CPU).lib
OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.o mallocx.o fnlz_mlc.o
gc$(CPU).lib: $(OBJS)
- $(LIBER) gc$(CPU).lib r $(OBJS)
+ $(LIBER) gc$(CPU).lib r $(OBJS)
COBJS = cord/cordbscs.o cord/cordprnt.o cord/cordxtra.o
@@ -118,7 +118,8 @@ blacklst.o : blacklst.c $(INC)
$(CC) blacklst.c $(SCOPT)
finalize.o : finalize.c $(INC)
- $(CC) finalize.c $(SCOPT) noopt #Could sas/c still have problems with this one? Gctest sometimes fails to finalize all.
+ $(CC) finalize.c $(SCOPT) noopt
+# Could sas/c still have problems with this one? Gctest sometimes fails to finalize all.
new_hblk.o : new_hblk.c $(INC)
$(CC) new_hblk.c $(SCOPT)
@@ -160,8 +161,7 @@ test.o : test.c $(INC)
$(CC) test.c $(SOPT)
setjmp_t: tools/setjmp_t.c gc.h
- $(CC) tools/setjmp_t.c $(SOPT)
-
+ $(CC) tools/setjmp_t.c $(SOPT)
# cords:
diff --git a/TODO b/TODO
index 539666a2..10471b20 100644
--- a/TODO
+++ b/TODO
@@ -7,8 +7,6 @@ compiler runtime, provided it is reliable, or from the future libatomic_ops).
Add a test for libatomic_ops minimal version required (at compile time).
-Merge symbian branch to master.
-
windows-untested: Remove if CMake can generate MS Visual Studio 6.0, 7.0, 8.0
project files.
@@ -22,6 +20,37 @@ auto-generated Makefile) instead. (Same for EMX_MAKEFILE.)
build_atomic_ops.sh[.cygwin]: Remove if really not needed.
+BCC_MAKEFILE, EMX_MAKEFILE, OS2_MAKEFILE, PCR-Makefile, WCC_MAKEFILE,
+SMakefile.amiga, Makefile.dj, digimars.mak: move to "build" folder.
+
+Do type-punning via union (instead of pointer type cast) to enable safe
+'-fstrict-aliasing' compiler optimization option.
+
+Support CAN_HANDLE_FORK if USE_WINALLOC for Cygwin.
+
+Use madvise() on Unix/Cygwin.
+
+Use Linux VM pressure notifications to force GC and unmapping.
+
+Filter overlaps in GC_add_roots for Unix (same as for Win32).
+
+Enable GC_set_handle_fork(1) for Darwin with GC_dirty_maintained on (both
+single and multi-threaded modes).
+
+Add more fields to GC_prof_stats_s (potential candidates are:
+requested_heapsize, max_large_allocd_bytes, large_allocd_bytes, bytes_dropped,
+bytes_finalized, bytes_freed, finalizer_bytes_freed, composite_in_use,
+atomic_in_use, GC_n_heap_sects, GC_n_memory, GC_black_list_spacing,
+GC_root_size, GC_max_root_size, n_root_sets, GC_total_stacksize,
+GC_collect_at_heapsize, GC_fail_count, GC_mark_stack_size, last_fo_entries,
+last_bytes_finalized, last_finalizer_notification_no, GC_dl_entries,
+GC_old_dl_entries, GC_used_heap_size_after_full, GC_total_stack_black_listed,
+signed_log_dl_table_size, GC_n_rescuing_pages, signed_log_fo_table_size,
+GC_excl_table_entries, GC_stack_last_cleared, GC_bytes_allocd_at_reset,
+GC_n_heap_bases, registered_threads_cnt, GC_max_thread_index, GC_block_count,
+GC_unlocked_count, GC_hdr_cache_hits, GC_hdr_cache_misses, GC_spin_count).
+
+Support musl libc (on sabotage linux).
== FIXME tasks ==
@@ -30,15 +59,40 @@ libc.so.1: gctest: fatal: libgcc_s.so.1: open failed: No such file or directory
Solaris/x86[_64]: gctest fails if PROC_VDB.
-Cygwin: crashes with USE_MUNMAP: mmap(PROT_NONE) failed.
+Sun C++ 5.11: test_cpp.cc:237: Error: Too few arguments in call to
+"operator delete(void*, GCPlacement, extern "C" void(*)(void*,void*), void*)".
-Cygwin: autoreconf fails: possibly undefined macro: AC_MSG_ERROR, AS_IF
-at configure.ac:59 and configure.ac:694, respectively (autoreconf-2.68).
+Darwin/x86_64: deadlock might occur between:
+dlclose() -> GC_dyld_image_remove() -> GC_lock() and
+GC_inner_start_routine()+LOCK -> dyld_stub_binder_().
HP-UX 11.00 with the vendor cc fails:
Perhaps GC_push_regs was configured incorrectly? FAIL: gctest.
-OpenBSD 4.9/x86 fails:
-GC_push_all_stacks: sp not set! FAIL: gctest, threadleaktest.
+Android NDK: gcc4.7+ld.gold: gctest crashes due to incorrect __data_start.
+
+Linux/mips64el (N32): threadleaktest crashes once every 3-4 runs (SIGSEGV in
+GC_delete_gc_thread(t=0) called from GC_pthread_join) if configured with
+--disable-shared.
+
+FreeBSD 9.0/x86_64 (gcc-4.2.1-20070831): gctest segfaults sometimes in
+GC_typed_mark_proc if configured with --enable-threads=pthreads.
+
+OpenBSD 5.1/i386: leaktest fails rarely (unless logging redirected to file):
+cannot write to stderr from GC_gcollect invoked from 'atexit' hook.
NetBSD 5.1/x86: threadkey_test hangs sometimes.
+
+Cygwin: subthread_create: exception STATUS_ACCESS_VIOLATION.
+
+Cygwin: gctest: assertion failure at UNLOCK in GC_fork_parent_proc.
+
+Mingw-w32: gctest: "SuspendThread failed" sometimes occurs (if
+GC_DLL+GC_THREADS+GC_ASSERTIONS).
+
+Mingw: gctest (compiled with PARALLEL_MARK): launched in gdb with breakpoint
+at GC_mark_local, after several breakpoint hits, crashes with the messages
+"Caught ACCESS_VIOLATION in marker; memory mapping disappeared" and
+"Tried to start parallel mark in bad state", or enters deadlock.
+
+Mingw: test_cpp: crashes at some iteration if big argument (e.g., 1000) given.
diff --git a/WCC_MAKEFILE b/WCC_MAKEFILE
index d1cca27a..0257f715 100644
--- a/WCC_MAKEFILE
+++ b/WCC_MAKEFILE
@@ -67,7 +67,7 @@ CC=wcc386
CXX=wpp386
# -DUSE_GENERIC is required !
-CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(DLLFLAG) -DGC_BUILD -DUSE_GENERIC $(DEFS)
+CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(DLLFLAG) -DUSE_GENERIC $(DEFS)
CXXFLAGS= $(CFLAGS)
TEST_CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(TEST_DLLFLAG) $(DEFS)
TEST_CXXFLAGS= $(TEST_CFLAGS)
diff --git a/allchblk.c b/allchblk.c
index 72faf5fe..25e11a46 100644
--- a/allchblk.c
+++ b/allchblk.c
@@ -45,26 +45,30 @@
/* In between sizes map this many distinct sizes to a single */
/* bin. */
-# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
- + UNIQUE_THRESHOLD
+# define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD) / FL_COMPRESSION \
+ + UNIQUE_THRESHOLD)
-STATIC struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 };
+#ifndef GC_GCJ_SUPPORT
+ STATIC
+#endif
+ struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 };
/* List of completely empty heap blocks */
/* Linked through hb_next field of */
/* header structure associated with */
- /* block. */
-
-#ifndef USE_MUNMAP
+ /* block. Remains externally visible */
+ /* as used by GNU GCJ currently. */
- STATIC word GC_free_bytes[N_HBLK_FLS+1] = { 0 };
- /* Number of free bytes on each list. */
+#ifndef GC_GCJ_SUPPORT
+ STATIC
+#endif
+ word GC_free_bytes[N_HBLK_FLS+1] = { 0 };
+ /* Number of free bytes on each list. Remains visible to GCJ. */
- /* 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. */
- GC_INLINE int GC_enough_large_bytes_left(void)
- {
+/* Return the largest n such that the number of free bytes on lists */
+/* n .. N_HBLK_FLS is greater or equal to GC_max_large_allocd_bytes */
+/* minus GC_large_allocd_bytes. If there is no such n, return 0. */
+GC_INLINE int GC_enough_large_bytes_left(void)
+{
int n;
word bytes = GC_large_allocd_bytes;
@@ -74,18 +78,7 @@ STATIC struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 };
if (bytes >= GC_max_large_allocd_bytes) return n;
}
return 0;
- }
-
-# define INCR_FREE_BYTES(n, b) GC_free_bytes[n] += (b);
-
-# define FREE_ASSERT(e) GC_ASSERT(e)
-
-#else /* USE_MUNMAP */
-
-# define INCR_FREE_BYTES(n, b)
-# define FREE_ASSERT(e)
-
-#endif /* USE_MUNMAP */
+}
/* Map a number of blocks to the appropriate large block free list index. */
STATIC int GC_hblk_fl_from_blocks(word blocks_needed)
@@ -97,51 +90,62 @@ STATIC int GC_hblk_fl_from_blocks(word blocks_needed)
}
-# define PHDR(hhdr) HDR(hhdr -> hb_prev)
-# define NHDR(hhdr) HDR(hhdr -> hb_next)
+# define PHDR(hhdr) HDR((hhdr) -> hb_prev)
+# define NHDR(hhdr) HDR((hhdr) -> hb_next)
# ifdef USE_MUNMAP
# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
-# else /* !USE_MUNMAP */
-# define IS_MAPPED(hhdr) 1
-# endif /* USE_MUNMAP */
+# else
+# define IS_MAPPED(hhdr) TRUE
+# endif /* !USE_MUNMAP */
+
+#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS)
+ /* Should return the same value as GC_large_free_bytes. */
+ GC_INNER word GC_compute_large_free_bytes(void)
+ {
+ struct hblk * h;
+ hdr * hhdr;
+ word total_free = 0;
+ unsigned i;
+
+ for (i = 0; i <= N_HBLK_FLS; ++i) {
+ for (h = GC_hblkfreelist[i]; h != 0; h = hhdr->hb_next) {
+ hhdr = HDR(h);
+ total_free += hhdr->hb_sz;
+ }
+ }
+ return total_free;
+ }
+#endif /* !NO_DEBUGGING || GC_ASSERTIONS */
# if !defined(NO_DEBUGGING)
void GC_print_hblkfreelist(void)
{
struct hblk * h;
- word total_free = 0;
hdr * hhdr;
- word sz;
unsigned i;
+ word total;
for (i = 0; i <= N_HBLK_FLS; ++i) {
h = GC_hblkfreelist[i];
-# ifdef USE_MUNMAP
- if (0 != h) GC_printf("Free list %u:\n", i);
-# else
- if (0 != h) GC_printf("Free list %u (total size %lu):\n",
- i, (unsigned long)GC_free_bytes[i]);
-# endif
+ if (0 != h) GC_printf("Free list %u (total size %lu):\n",
+ i, (unsigned long)GC_free_bytes[i]);
while (h != 0) {
hhdr = HDR(h);
- sz = hhdr -> hb_sz;
- total_free += sz;
GC_printf("\t%p size %lu %s black listed\n",
- (void *)h, (unsigned long)sz,
+ (void *)h, (unsigned long) hhdr -> hb_sz,
GC_is_black_listed(h, HBLKSIZE) != 0 ? "start" :
GC_is_black_listed(h, hhdr -> hb_sz) != 0 ? "partially" :
"not");
h = hhdr -> hb_next;
}
}
-# ifndef USE_MUNMAP
- if (total_free != GC_large_free_bytes) {
- GC_printf("GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
- (unsigned long) GC_large_free_bytes);
- }
-# endif
- GC_printf("Total of %lu bytes on free list\n", (unsigned long)total_free);
+ GC_printf("GC_large_free_bytes: %lu\n",
+ (unsigned long)GC_large_free_bytes);
+
+ if ((total = GC_compute_large_free_bytes()) != GC_large_free_bytes)
+ GC_err_printf("GC_large_free_bytes INCONSISTENT!! Should be: %lu\n",
+ (unsigned long)total);
}
/* Return the free list index on which the block described by the header */
@@ -180,7 +184,7 @@ void GC_dump_regions(void)
end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes;
}
GC_printf("***Section from %p to %p\n", start, end);
- for (p = start; p < end;) {
+ for (p = start; (word)p < (word)end; ) {
hhdr = HDR(p);
if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
GC_printf("\t%p Missing header!!(%p)\n", p, (void *)hhdr);
@@ -285,33 +289,11 @@ static GC_bool setup_header(hdr * hhdr, struct hblk *block, size_t byte_sz,
return(TRUE);
}
-#define FL_UNKNOWN -1
-/*
- * Remove hhdr from the appropriate free list.
- * We assume it is on the nth free list, or on the size
- * appropriate free list if n is FL_UNKNOWN.
- */
-STATIC void GC_remove_from_fl(hdr *hhdr, int n)
+/* Remove hhdr from the free list (it is assumed to specified by index). */
+STATIC void GC_remove_from_fl_at(hdr *hhdr, int index)
{
- int index;
-
GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0);
-# ifndef USE_MUNMAP
- /* We always need index to mainatin free counts. */
- if (FL_UNKNOWN == n) {
- index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
- } else {
- index = n;
- }
-# endif
if (hhdr -> hb_prev == 0) {
-# ifdef USE_MUNMAP
- if (FL_UNKNOWN == n) {
- index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
- } else {
- index = n;
- }
-# endif
GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr);
GC_hblkfreelist[index] = hhdr -> hb_next;
} else {
@@ -319,8 +301,9 @@ STATIC void GC_remove_from_fl(hdr *hhdr, int n)
GET_HDR(hhdr -> hb_prev, phdr);
phdr -> hb_next = hhdr -> hb_next;
}
- FREE_ASSERT(GC_free_bytes[index] >= hhdr -> hb_sz);
- INCR_FREE_BYTES(index, - (signed_word)(hhdr -> hb_sz));
+ /* We always need index to maintain free counts. */
+ GC_ASSERT(GC_free_bytes[index] >= hhdr -> hb_sz);
+ GC_free_bytes[index] -= hhdr -> hb_sz;
if (0 != hhdr -> hb_next) {
hdr * nhdr;
GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr)));
@@ -329,9 +312,14 @@ STATIC void GC_remove_from_fl(hdr *hhdr, int n)
}
}
-/*
- * Return a pointer to the free block ending just before h, if any.
- */
+/* Remove hhdr from the appropriate free list (we assume it is on the */
+/* size-appropriate free list). */
+GC_INLINE void GC_remove_from_fl(hdr *hhdr)
+{
+ GC_remove_from_fl_at(hhdr, GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz)));
+}
+
+/* Return a pointer to the free block ending just before h, if any. */
STATIC struct hblk * GC_free_block_ending_at(struct hblk *h)
{
struct hblk * p = h - 1;
@@ -359,10 +347,8 @@ STATIC struct hblk * GC_free_block_ending_at(struct hblk *h)
return 0;
}
-/*
- * Add hhdr to the appropriate free list.
- * We maintain individual free lists sorted by address.
- */
+/* Add hhdr to the appropriate free list. */
+/* We maintain individual free lists sorted by address. */
STATIC void GC_add_to_fl(struct hblk *h, hdr *hhdr)
{
int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz));
@@ -379,10 +365,11 @@ STATIC void GC_add_to_fl(struct hblk *h, hdr *hhdr)
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;
- INCR_FREE_BYTES(index, hhdr -> hb_sz);
- FREE_ASSERT(GC_free_bytes[index] <= GC_large_free_bytes)
+ GC_free_bytes[index] += hhdr -> hb_sz;
+ GC_ASSERT(GC_free_bytes[index] <= GC_large_free_bytes);
hhdr -> hb_next = second;
hhdr -> hb_prev = 0;
if (0 != second) {
@@ -474,8 +461,8 @@ GC_INNER void GC_merge_unmapped(void)
GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize);
}
/* If they are both unmapped, we merge, but leave unmapped. */
- GC_remove_from_fl(hhdr, i);
- GC_remove_from_fl(nexthdr, FL_UNKNOWN);
+ GC_remove_from_fl_at(hhdr, i);
+ GC_remove_from_fl(nexthdr);
hhdr -> hb_sz += nexthdr -> hb_sz;
GC_remove_header(next);
GC_add_to_fl(h, hhdr);
@@ -507,7 +494,7 @@ STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr,
hdr * rest_hdr;
GC_ASSERT((total_size & (HBLKSIZE-1)) == 0);
- GC_remove_from_fl(hhdr, index);
+ GC_remove_from_fl_at(hhdr, index);
if (total_size == bytes) return h;
rest = (struct hblk *)((word)h + bytes);
rest_hdr = GC_install_header(rest);
@@ -561,8 +548,8 @@ STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n,
if (0 != next) {
HDR(next) -> hb_prev = n;
}
- INCR_FREE_BYTES(index, -(signed_word)h_size);
- FREE_ASSERT(GC_free_bytes[index] > 0);
+ GC_ASSERT(GC_free_bytes[index] > h_size);
+ GC_free_bytes[index] -= h_size;
# ifdef USE_MUNMAP
hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
# endif
@@ -572,8 +559,9 @@ STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n,
}
STATIC struct hblk *
-GC_allochblk_nth(size_t sz/* bytes */, int kind, unsigned flags, int n,
- GC_bool may_split);
+GC_allochblk_nth(size_t sz /* bytes */, int kind, unsigned flags, int n,
+ int may_split);
+#define AVOID_SPLIT_REMAPPED 2
/*
* Allocate (and return pointer to) a heap block
@@ -589,8 +577,8 @@ GC_allochblk(size_t sz, int kind, unsigned flags/* IGNORE_OFF_PAGE or 0 */)
{
word blocks;
int start_list;
- int i;
struct hblk *result;
+ int may_split;
int split_limit; /* Highest index of free list whose blocks we */
/* split. */
@@ -603,55 +591,53 @@ GC_allochblk(size_t sz, int kind, unsigned flags/* IGNORE_OFF_PAGE or 0 */)
/* Try for an exact match first. */
result = GC_allochblk_nth(sz, kind, flags, start_list, FALSE);
if (0 != result) return result;
+
+ may_split = TRUE;
if (GC_use_entire_heap || GC_dont_gc
|| USED_HEAP_SIZE < GC_requested_heapsize
|| GC_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)) {
+ } 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 {
+ } 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
+# ifdef USE_MUNMAP
+ if (split_limit > 0)
+ may_split = AVOID_SPLIT_REMAPPED;
+# 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;
+ for (; start_list <= split_limit; ++start_list) {
+ result = GC_allochblk_nth(sz, kind, flags, start_list, may_split);
+ if (0 != result)
+ break;
}
- return 0;
+ return result;
}
STATIC long GC_large_alloc_warn_suppressed = 0;
/* Number of warnings suppressed so far. */
-/*
- * 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.
- */
+/* The same, but with search restricted to nth free list. Flags is */
+/* IGNORE_OFF_PAGE or zero. sz is in bytes. The may_split flag */
+/* indicates whether it is OK to split larger blocks (if set to */
+/* AVOID_SPLIT_REMAPPED then memory remapping followed by splitting */
+/* should be generally avoided). */
STATIC struct hblk *
-GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
- GC_bool may_split)
+GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n, int may_split)
{
struct hblk *hbp;
hdr * hhdr; /* Header corr. to hbp */
@@ -675,8 +661,8 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
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. */
+ /* This prevents us from disassembling a single large */
+ /* block to get tiny blocks. */
thishbp = hhdr -> hb_next;
if (thishbp != 0) {
GET_HDR(thishbp, thishdr);
@@ -688,7 +674,7 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
}
}
}
- if ( !IS_UNCOLLECTABLE(kind) && (kind != PTRFREE
+ if (!IS_UNCOLLECTABLE(kind) && (kind != PTRFREE
|| size_needed > (signed_word)MAX_BLACK_LIST_ALLOC)) {
struct hblk * lasthbp = hbp;
ptr_t search_end = (ptr_t)hbp + size_avail - size_needed;
@@ -697,18 +683,22 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
(signed_word)HBLKSIZE
: size_needed;
-
- while ((ptr_t)lasthbp <= search_end
+ while ((word)lasthbp <= (word)search_end
&& (thishbp = GC_is_black_listed(lasthbp,
- (word)eff_size_needed))
- != 0) {
+ (word)eff_size_needed)) != 0) {
lasthbp = thishbp;
}
size_avail -= (ptr_t)lasthbp - (ptr_t)hbp;
thishbp = lasthbp;
if (size_avail >= size_needed) {
- if (thishbp != hbp &&
- 0 != (thishdr = GC_install_header(thishbp))) {
+ if (thishbp != hbp) {
+# ifdef USE_MUNMAP
+ /* Avoid remapping followed by splitting. */
+ if (may_split == AVOID_SPLIT_REMAPPED && !IS_MAPPED(hhdr))
+ continue;
+# endif
+ thishdr = GC_install_header(thishbp);
+ if (0 != thishdr) {
/* Make sure it's mapped before we mangle it. */
# ifdef USE_MUNMAP
if (!IS_MAPPED(hhdr)) {
@@ -723,6 +713,7 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
hhdr = thishdr;
/* We must now allocate thishbp, since it may */
/* be on the wrong free list. */
+ }
}
} else if (size_needed > (signed_word)BL_LIMIT
&& orig_avail - size_needed
@@ -744,7 +735,7 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
/* The block is completely blacklisted. We need */
/* to drop some such blocks, since otherwise we spend */
- /* all our time traversing them if pointerfree */
+ /* all our time traversing them if pointer-free */
/* blocks are unpopular. */
/* A dropped block will be reconsidered at next GC. */
if ((++count & 3) == 0) {
@@ -758,13 +749,14 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
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))) {
- (void) setup_header(
- hhdr, h,
- HBLKSIZE,
- PTRFREE, 0); /* Can't fail */
+ GC_remove_from_fl_at(hhdr, n);
+ for (h = hbp; (word)h < (word)limit; h++) {
+ if (h != hbp) {
+ hhdr = GC_install_header(h);
+ }
+ if (NULL != hhdr) {
+ (void)setup_header(hhdr, h, HBLKSIZE, PTRFREE, 0);
+ /* Can't fail. */
if (GC_debugging_started) {
BZERO(h, HBLKSIZE);
}
@@ -808,8 +800,8 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
}
# ifndef GC_DISABLE_INCREMENTAL
/* Notify virtual dirty bit implementation that we are about to */
- /* write. Ensure that pointerfree objects are not protected if */
- /* it's avoidable. This also ensures that newly allocated */
+ /* write. Ensure that pointer-free objects are not protected */
+ /* if it is avoidable. This also ensures that newly allocated */
/* blocks are treated as dirty. Necessary since we don't */
/* protect free blocks. */
GC_ASSERT((size_needed & (HBLKSIZE-1)) == 0);
@@ -821,7 +813,6 @@ GC_allochblk_nth(size_t sz, int kind, unsigned flags, int n,
GC_fail_count = 0;
GC_large_free_bytes -= size_needed;
-
GC_ASSERT(IS_MAPPED(hhdr));
return( hbp );
}
@@ -837,17 +828,16 @@ GC_INNER void GC_freehblk(struct hblk *hbp)
{
struct hblk *next, *prev;
hdr *hhdr, *prevhdr, *nexthdr;
- signed_word size;
+ word size;
GET_HDR(hbp, hhdr);
- size = hhdr->hb_sz;
- size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(size);
- if (size <= 0)
+ size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr->hb_sz);
+ if ((signed_word)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 don't catch it here, strange things happen */
/* later. */
- GC_remove_counts(hbp, (word)size);
+ GC_remove_counts(hbp, size);
hhdr->hb_sz = size;
# ifdef USE_MUNMAP
hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
@@ -855,22 +845,20 @@ GC_INNER void GC_freehblk(struct hblk *hbp)
/* Check for duplicate deallocation in the easy case */
if (HBLK_IS_FREE(hhdr)) {
- if (GC_print_stats)
- GC_log_printf("Duplicate large block deallocation of %p\n",
- (void *)hbp);
- ABORT("Duplicate large block deallocation");
+ ABORT_ARG1("Duplicate large block deallocation",
+ " of %p", (void *)hbp);
}
GC_ASSERT(IS_MAPPED(hhdr));
hhdr -> hb_flags |= FREE_BLK;
- next = (struct hblk *)((word)hbp + size);
+ next = (struct hblk *)((ptr_t)hbp + size);
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)
&& (signed_word)(hhdr -> hb_sz + nexthdr -> hb_sz) > 0
/* no overflow */) {
- GC_remove_from_fl(nexthdr, FL_UNKNOWN);
+ GC_remove_from_fl(nexthdr);
hhdr -> hb_sz += nexthdr -> hb_sz;
GC_remove_header(next);
}
@@ -879,7 +867,7 @@ GC_INNER void GC_freehblk(struct hblk *hbp)
prevhdr = HDR(prev);
if (IS_MAPPED(prevhdr)
&& (signed_word)(hhdr -> hb_sz + prevhdr -> hb_sz) > 0) {
- GC_remove_from_fl(prevhdr, FL_UNKNOWN);
+ GC_remove_from_fl(prevhdr);
prevhdr -> hb_sz += hhdr -> hb_sz;
# ifdef USE_MUNMAP
prevhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no;
diff --git a/alloc.c b/alloc.c
index 24ef042c..c8623dca 100644
--- a/alloc.c
+++ b/alloc.c
@@ -111,9 +111,9 @@ GC_API unsigned GC_CALL GC_get_version(void)
/* some more variables */
#ifdef GC_DONT_EXPAND
- GC_bool GC_dont_expand = TRUE;
+ int GC_dont_expand = TRUE;
#else
- GC_bool GC_dont_expand = FALSE;
+ int GC_dont_expand = FALSE;
#endif
#ifndef GC_FREE_SPACE_DIVISOR
@@ -181,11 +181,9 @@ GC_API GC_stop_func GC_CALL GC_get_stop_func(void)
GET_TIME(current_time);
time_diff = MS_TIME_DIFF(current_time,GC_start_time);
if (time_diff >= GC_time_limit) {
- if (GC_print_stats) {
- GC_log_printf(
+ GC_COND_LOG_PRINTF(
"Abandoning stopped marking after %lu msecs (attempt %d)\n",
time_diff, GC_n_attempts);
- }
return(1);
}
return(0);
@@ -196,15 +194,16 @@ GC_API GC_stop_func GC_CALL GC_get_stop_func(void)
GC_INNER word GC_total_stacksize = 0; /* updated on every push_all_stacks */
#endif
-/* Return the minimum number of words that must be allocated between */
-/* collections to amortize the collection cost. */
+/* Return the minimum number of bytes that must be allocated between */
+/* collections to amortize the collection cost. Should be non-zero. */
static word min_bytes_allocd(void)
{
- int dummy; /* GC_stackbottom is used only for a single-threaded case. */
+ word result;
# ifdef STACK_GROWS_UP
- word stack_size = (ptr_t)(&dummy) - GC_stackbottom;
+ word stack_size = GC_approx_sp() - GC_stackbottom;
+ /* GC_stackbottom is used only for a single-threaded case. */
# else
- word stack_size = GC_stackbottom - (ptr_t)(&dummy);
+ word stack_size = GC_stackbottom - GC_approx_sp();
# endif
word total_root_size; /* includes double stack size, */
@@ -228,13 +227,17 @@ static word min_bytes_allocd(void)
total_root_size = 2 * stack_size + GC_root_size;
scan_size = 2 * GC_composite_in_use + GC_atomic_in_use / 4
+ total_root_size;
+ result = scan_size / GC_free_space_divisor;
if (GC_incremental) {
- return scan_size / (2 * GC_free_space_divisor);
- } else {
- return scan_size / GC_free_space_divisor;
+ result /= 2;
}
+ return result > 0 ? result : 1;
}
+STATIC word GC_non_gc_bytes_at_gc = 0;
+ /* Number of explicitly managed bytes of storage */
+ /* at last collection. */
+
/* Return the number of bytes allocated, adjusted for explicit storage */
/* management, etc.. This number is used in deciding when to trigger */
/* collections. */
@@ -363,11 +366,9 @@ STATIC void GC_maybe_gc(void)
GC_wait_for_reclaim();
# endif
if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) {
- if (GC_print_stats) {
- GC_log_printf(
- "***>Full mark for collection %lu after %ld allocd bytes\n",
- (unsigned long)GC_gc_no + 1, (long)GC_bytes_allocd);
- }
+ GC_COND_LOG_PRINTF(
+ "***>Full mark for collection #%lu after %lu allocd bytes\n",
+ (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd);
GC_promote_black_lists();
(void)GC_reclaim_all((GC_stop_func)0, TRUE);
GC_notify_full_gc();
@@ -416,10 +417,8 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
ASSERT_CANCEL_DISABLED();
if (GC_dont_gc || (*stop_func)()) return FALSE;
if (GC_incremental && GC_collection_in_progress()) {
- if (GC_print_stats) {
- GC_log_printf(
+ GC_COND_LOG_PRINTF(
"GC_try_to_collect_inner: finishing collection in progress\n");
- }
/* Just finish collection already in progress. */
while(GC_collection_in_progress()) {
if ((*stop_func)()) return(FALSE);
@@ -560,7 +559,7 @@ GC_API int GC_CALL GC_collect_a_little(void)
#ifndef SMALL_CONFIG
/* Variables for world-stop average delay time statistic computation. */
/* "divisor" is incremented every world-stop and halved when reached */
- /* its maximum (or upon "total_time" oveflow). */
+ /* its maximum (or upon "total_time" overflow). */
static unsigned world_stopped_total_time = 0;
static unsigned world_stopped_total_divisor = 0;
# ifndef MAX_TOTAL_TIME_DIVISOR
@@ -571,6 +570,14 @@ GC_API int GC_CALL GC_collect_a_little(void)
# endif
#endif
+#ifdef USE_MUNMAP
+# define IF_USE_MUNMAP(x) x
+# define COMMA_IF_USE_MUNMAP(x) /* comma */, x
+#else
+# define IF_USE_MUNMAP(x) /* empty */
+# define COMMA_IF_USE_MUNMAP(x) /* empty */
+#endif
+
/*
* Assumes lock is held. We stop the world and mark from all roots.
* If stop_func() ever returns TRUE, we may fail and return FALSE.
@@ -579,13 +586,12 @@ GC_API int GC_CALL GC_collect_a_little(void)
STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
{
unsigned i;
- int dummy;
# ifndef SMALL_CONFIG
CLOCK_TYPE start_time = 0; /* initialized to prevent warning. */
CLOCK_TYPE current_time;
# endif
-# if !defined(REDIRECT_MALLOC) && (defined(MSWIN32) || defined(MSWINCE))
+# if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC)
GC_add_current_malloc_heap();
# endif
# if defined(REGISTER_LIBRARIES_EARLY)
@@ -593,7 +599,7 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
# endif
# ifndef SMALL_CONFIG
- if (GC_print_stats)
+ if (GC_PRINT_STATS_FLAG)
GET_TIME(start_time);
# endif
@@ -601,12 +607,10 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = TRUE;
# endif
- if (GC_print_stats) {
/* Output blank line for convenience here */
- GC_log_printf(
- "\n--> Marking for collection %lu after %lu allocated bytes\n",
+ GC_COND_LOG_PRINTF(
+ "\n--> Marking for collection #%lu after %lu allocated bytes\n",
(unsigned long)GC_gc_no + 1, (unsigned long) GC_bytes_allocd);
- }
# ifdef MAKE_BACK_GRAPH
if (GC_print_back_height) {
GC_build_back_graph();
@@ -616,14 +620,13 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
/* Mark from all roots. */
/* Minimize junk left in my registers and on the stack */
GC_clear_a_few_frames();
- GC_noop(0,0,0,0,0,0);
+ GC_noop6(0,0,0,0,0,0);
+
GC_initiate_gc();
for (i = 0;;i++) {
if ((*stop_func)()) {
- if (GC_print_stats) {
- GC_log_printf("Abandoned stopped marking after %u iterations\n",
- i);
- }
+ GC_COND_LOG_PRINTF("Abandoned stopped marking after"
+ " %u iterations\n", i);
GC_deficit = i; /* Give the mutator a chance. */
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = FALSE;
@@ -631,28 +634,27 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
START_WORLD();
return(FALSE);
}
- if (GC_mark_some((ptr_t)(&dummy))) break;
+ if (GC_mark_some(GC_approx_sp())) break;
}
GC_gc_no++;
- if (GC_print_stats) {
- GC_log_printf(
- "Collection %lu reclaimed %ld bytes ---> heapsize = %lu bytes\n",
- (unsigned long)(GC_gc_no - 1), (long)GC_bytes_found,
- (unsigned long)GC_heapsize);
- }
+ GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB"
+ IF_USE_MUNMAP(" (+ %lu KiB unmapped)") "\n",
+ (unsigned long)GC_gc_no, (long)GC_bytes_found,
+ TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) /*, */
+ COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes)));
/* Check all debugged objects for consistency */
- if (GC_debugging_started) {
- (*GC_check_heap)();
- }
+ if (GC_debugging_started) {
+ (*GC_check_heap)();
+ }
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = FALSE;
# endif
START_WORLD();
# ifndef SMALL_CONFIG
- if (GC_print_stats) {
+ if (GC_PRINT_STATS_FLAG) {
unsigned long time_diff;
unsigned total_time, divisor;
GET_TIME(current_time);
@@ -716,20 +718,47 @@ GC_INNER void GC_set_fl_marks(ptr_t q)
}
}
-#ifdef GC_ASSERTIONS
- /* Check that all mark bits for the free list whose first entry is q */
- /* are set. */
- void GC_check_fl_marks(ptr_t q)
+#if defined(GC_ASSERTIONS) && defined(THREADS) && defined(THREAD_LOCAL_ALLOC)
+ /* Check that all mark bits for the free list whose first entry is */
+ /* (*pfreelist) are set. Check skipped if points to a special value. */
+ void GC_check_fl_marks(void **pfreelist)
{
- ptr_t p;
- for (p = q; p != 0; p = obj_link(p)) {
- if (!GC_is_marked(p)) {
- GC_err_printf("Unmarked object %p on list %p\n", p, q);
- ABORT("Unmarked local free list entry");
- }
- }
+# ifdef AO_HAVE_load_acquire_read
+ AO_t *list = (AO_t *)AO_load_acquire_read((AO_t *)pfreelist);
+ /* Atomic operations are used because the world is running. */
+ AO_t *prev;
+ AO_t *p;
+
+ if ((word)list <= HBLKSIZE) return;
+
+ prev = (AO_t *)pfreelist;
+ for (p = list; p != NULL;) {
+ AO_t *next;
+
+ if (!GC_is_marked(p)) {
+ ABORT_ARG2("Unmarked local free list entry",
+ ": object %p on list %p", (void *)p, (void *)list);
+ }
+
+ /* While traversing the free-list, it re-reads the pointer to */
+ /* the current node before accepting its next pointer and */
+ /* bails out if the latter has changed. That way, it won't */
+ /* try to follow the pointer which might be been modified */
+ /* after the object was returned to the client. It might */
+ /* perform the mark-check on the just allocated object but */
+ /* that should be harmless. */
+ next = (AO_t *)AO_load_acquire_read(p);
+ if (AO_load(prev) != (AO_t)p)
+ break;
+ prev = p;
+ p = next;
+ }
+# else
+ /* FIXME: Not implemented (just skipped). */
+ (void)pfreelist;
+# endif
}
-#endif
+#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
/* Clear all mark bits for the free list whose first entry is q */
/* Decrement GC_bytes_found by number of bytes on free list. */
@@ -782,6 +811,15 @@ STATIC void GC_clear_fl_marks(ptr_t q)
GC_on_heap_resize_proc GC_on_heap_resize = 0;
+/* Used for logging only. */
+GC_INLINE int GC_compute_heap_usage_percent(void)
+{
+ word used = GC_composite_in_use + GC_atomic_in_use;
+ word heap_sz = GC_heapsize - GC_unmapped_bytes;
+ return used >= heap_sz ? 0 : used < ((word)-1) / 100 ?
+ (int)((used * 100) / heap_sz) : (int)(used / (heap_sz / 100));
+}
+
/* Finish up a collection. Assumes mark bits are consistent, lock is */
/* held, but the world is otherwise running. */
STATIC void GC_finish_collection(void)
@@ -804,6 +842,10 @@ STATIC void GC_finish_collection(void)
GET_TIME(start_time);
# endif
+# ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+ if (GC_bytes_found > 0)
+ GC_reclaimed_bytes_before_gc += (word)GC_bytes_found;
+# endif
GC_bytes_found = 0;
# if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
if (GETENV("GC_PRINT_ADDRESS_MAP") != 0) {
@@ -828,7 +870,9 @@ STATIC void GC_finish_collection(void)
/* The above just checks; it doesn't really reclaim anything. */
}
- GC_finalize();
+# ifndef GC_NO_FINALIZATION
+ GC_finalize();
+# endif
# ifdef STUBBORN_ALLOC
GC_clean_changing_list();
# endif
@@ -866,18 +910,15 @@ STATIC void GC_finish_collection(void)
}
}
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Bytes recovered before sweep - f.l. count = %ld\n",
- (long)GC_bytes_found);
+ GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count = %ld\n",
+ (long)GC_bytes_found);
/* Reconstruct free lists to contain everything not marked */
GC_start_reclaim(FALSE);
- if (GC_print_stats) {
- GC_log_printf("Heap contains %lu pointer-containing "
- "+ %lu pointer-free reachable bytes\n",
- (unsigned long)GC_composite_in_use,
- (unsigned long)GC_atomic_in_use);
- }
+ GC_DBGLOG_PRINTF("In-use heap: %d%% (%lu KiB pointers + %lu KiB other)\n",
+ GC_compute_heap_usage_percent(),
+ TO_KiB_UL(GC_composite_in_use),
+ TO_KiB_UL(GC_atomic_in_use));
if (GC_is_full_gc) {
GC_used_heap_size_after_full = USED_HEAP_SIZE;
GC_need_full_gc = FALSE;
@@ -886,18 +927,12 @@ STATIC void GC_finish_collection(void)
> min_bytes_allocd();
}
- if (GC_print_stats == VERBOSE) {
-# ifdef USE_MUNMAP
- GC_log_printf("Immediately reclaimed %ld bytes in heap"
- " of size %lu bytes (%lu unmapped)\n",
- (long)GC_bytes_found, (unsigned long)GC_heapsize,
- (unsigned long)GC_unmapped_bytes);
-# else
- GC_log_printf(
- "Immediately reclaimed %ld bytes in heap of size %lu bytes\n",
- (long)GC_bytes_found, (unsigned long)GC_heapsize);
-# endif
- }
+ GC_VERBOSE_LOG_PRINTF("Immediately reclaimed %ld bytes, heapsize:"
+ " %lu bytes" IF_USE_MUNMAP(" (%lu unmapped)") "\n",
+ (long)GC_bytes_found,
+ (unsigned long)GC_heapsize /*, */
+ COMMA_IF_USE_MUNMAP((unsigned long)
+ GC_unmapped_bytes));
/* Reset or increment counters for next cycle */
GC_n_attempts = 0;
@@ -909,17 +944,15 @@ STATIC void GC_finish_collection(void)
GC_bytes_freed = 0;
GC_finalizer_bytes_freed = 0;
-# ifdef USE_MUNMAP
- GC_unmap_old();
-# endif
+ IF_USE_MUNMAP(GC_unmap_old());
# ifndef SMALL_CONFIG
if (GC_print_stats) {
GET_TIME(done_time);
-
- /* A convenient place to output finalization statistics. */
- GC_print_finalization_stats();
-
+# ifndef GC_NO_FINALIZATION
+ /* A convenient place to output finalization statistics. */
+ GC_print_finalization_stats();
+# endif
GC_log_printf("Finalize plus initiate sweep took %lu + %lu msecs\n",
MS_TIME_DIFF(finalize_time,start_time),
MS_TIME_DIFF(done_time,finalize_time));
@@ -932,9 +965,7 @@ STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func,
GC_bool force_unmap GC_ATTR_UNUSED)
{
GC_bool result;
-# ifdef USE_MUNMAP
- int old_unmap_threshold;
-# endif
+ IF_USE_MUNMAP(int old_unmap_threshold;)
IF_CANCEL(int cancel_state;)
DCL_LOCK_STATE;
@@ -951,13 +982,11 @@ STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func,
# endif
ENTER_GC();
/* Minimize junk left in my registers */
- GC_noop(0,0,0,0,0,0);
+ GC_noop6(0,0,0,0,0,0);
result = GC_try_to_collect_inner(stop_func != 0 ? stop_func :
GC_default_stop_func);
EXIT_GC();
-# ifdef USE_MUNMAP
- GC_unmap_threshold = old_unmap_threshold; /* restore */
-# endif
+ IF_USE_MUNMAP(GC_unmap_threshold = old_unmap_threshold); /* restore */
RESTORE_CANCEL(cancel_state);
UNLOCK();
if (result) {
@@ -1049,7 +1078,7 @@ GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes)
phdr -> hb_flags = 0;
GC_freehblk(p);
GC_heapsize += bytes;
- if ((ptr_t)p <= (ptr_t)GC_least_plausible_heap_addr
+ if ((word)p <= (word)GC_least_plausible_heap_addr
|| GC_least_plausible_heap_addr == 0) {
GC_least_plausible_heap_addr = (void *)((ptr_t)p - sizeof(word));
/* Making it a little smaller than necessary prevents */
@@ -1057,7 +1086,7 @@ GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes)
/* itself. There's some unintentional reflection */
/* here. */
}
- if ((ptr_t)p + bytes >= (ptr_t)GC_greatest_plausible_heap_addr) {
+ if ((word)p + bytes >= (word)GC_greatest_plausible_heap_addr) {
GC_greatest_plausible_heap_addr = (void *)endp;
}
}
@@ -1067,14 +1096,17 @@ GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes)
{
unsigned i;
- GC_printf("Total heap size: %lu\n", (unsigned long)GC_heapsize);
+ GC_printf("Total heap size: %lu" IF_USE_MUNMAP(" (%lu unmapped)") "\n",
+ (unsigned long)GC_heapsize /*, */
+ COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes));
+
for (i = 0; i < GC_n_heap_sects; i++) {
ptr_t start = GC_heap_sects[i].hs_start;
size_t len = GC_heap_sects[i].hs_bytes;
struct hblk *h;
unsigned nbl = 0;
- for (h = (struct hblk *)start; h < (struct hblk *)(start + len); h++) {
+ for (h = (struct hblk *)start; (word)h < (word)(start + len); h++) {
if (GC_is_black_listed(h, HBLKSIZE)) nbl++;
}
GC_printf("Section %d from %p to %p %lu/%lu blacklisted\n",
@@ -1097,6 +1129,8 @@ GC_INLINE word GC_min(word x, word y)
return(x < y? x : y);
}
+STATIC word GC_max_heapsize = 0;
+
GC_API void GC_CALL GC_set_max_heap_size(GC_word n)
{
GC_max_heapsize = n;
@@ -1104,13 +1138,10 @@ GC_API void GC_CALL GC_set_max_heap_size(GC_word n)
GC_word GC_max_retries = 0;
-/*
- * this explicitly increases the size of the heap. It is used
- * internally, but may also be invoked from GC_expand_hp by the user.
- * The argument is in units of HBLKSIZE.
- * Tiny values of n are rounded up.
- * Returns FALSE on failure.
- */
+/* This explicitly increases the size of the heap. It is used */
+/* internally, but may also be invoked from GC_expand_hp by the user. */
+/* The argument is in units of HBLKSIZE (tiny values are rounded up). */
+/* Returns FALSE on failure. */
GC_INNER GC_bool GC_expand_hp_inner(word n)
{
word bytes;
@@ -1134,22 +1165,19 @@ GC_INNER GC_bool GC_expand_hp_inner(word n)
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);
- }
+ WARN("Failed to expand heap by %" WARN_PRIdPTR " bytes\n", bytes);
return(FALSE);
}
- if (GC_print_stats) {
- GC_log_printf("Increasing heap size by %lu after %lu allocated bytes\n",
- (unsigned long)bytes, (unsigned long)GC_bytes_allocd);
- }
+ GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n",
+ TO_KiB_UL(GC_heapsize + bytes),
+ (unsigned long)GC_bytes_allocd);
/* Adjust heap limits generously for blacklisting to work better. */
/* GC_add_to_heap performs minimal adjustment needed 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)) {
+ || (GC_last_heap_addr != 0
+ && (word)GC_last_heap_addr < (word)space)) {
/* Assume the heap is growing up */
word new_limit = (word)space + bytes + expansion_slop;
if (new_limit > (word)space) {
@@ -1195,6 +1223,8 @@ GC_API int GC_CALL GC_expand_hp(size_t bytes)
return(result);
}
+word GC_fo_entries = 0; /* used also in extra/MacOS.c */
+
GC_INNER unsigned GC_fail_count = 0;
/* How many consecutive GC/expansion failures? */
/* Reset by GC_allochblk. */
@@ -1274,8 +1304,8 @@ GC_INNER GC_bool GC_collect_or_expand(word needed_blocks,
RESTORE_CANCEL(cancel_state);
return(FALSE);
}
- } else if (GC_fail_count && GC_print_stats) {
- GC_log_printf("Memory available again...\n");
+ } else if (GC_fail_count) {
+ GC_COND_LOG_PRINTF("Memory available again...\n");
}
RESTORE_CANCEL(cancel_state);
return(TRUE);
diff --git a/autogen.sh b/autogen.sh
index ffba18c1..8a614f91 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,32 +1,18 @@
-#! /bin/sh
-
+#!/bin/sh
set -e
-# These version are ok, pre-1.7 is not. Post 1.7 may produce a lot of
-# warnings for unrelated projects, so prefer 1.7 for now.
-am_version=
-for v in 1.10 1.9 1.8 1.7; do
- if type -p &>/dev/null automake-$v; then
- am_version="-$v"
- break
- fi
-done
-if [ -z "$am_version" ]; then
- case "`automake --version`" in
- *\ 0.*|*\ 1.[0-6].*|*\ 1.[0-6]\ *)
- echo "$0: Automake-1.7 or later is needed."
- exit 2
- ;;
- esac
-fi
+# This script creates (or regenerates) configure (as well as aclocal.m4,
+# config.h.in, Makefile.in, etc.) missing in the source repository.
+#
+# If you compile from a distribution tarball, you can skip this. Otherwise,
+# make sure that you have Autoconf, Automake, Libtool, and pkg-config
+# installed on your system, and that the corresponding *.m4 files are visible
+# to the aclocal. The latter can be achieved by using packages shipped by
+# your OS, or by installing custom versions of all four packages to the same
+# prefix. Otherwise, you may need to invoke autoreconf with the appropriate
+# -I options to locate the required *.m4 files.
+
+autoreconf -i
-set -x
-aclocal$am_version
-autoconf
-autoheader
-libtoolize --automake --force
-automake$am_version -ac
-set +x
echo
echo "Ready to run './configure'."
-echo
diff --git a/backgraph.c b/backgraph.c
index cd2fdcd2..d6f3223d 100644
--- a/backgraph.c
+++ b/backgraph.c
@@ -172,7 +172,7 @@ GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED)
/* Execute s once for each predecessor q of p in the points-to graph. */
/* s should be a bracketed statement. We declare q. */
#define FOR_EACH_PRED(q, p, s) \
- { \
+ do { \
ptr_t q = GET_OH_BG_PTR(p); \
if (!((word)q & FLAG_MANY)) { \
if (q && !((word)q & 1)) s \
@@ -191,7 +191,7 @@ GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED)
q = be_ -> edges[local_]; s \
} \
} \
- }
+ } while (0)
/* Ensure that p has a back_edges structure associated with it. */
static void ensure_struct(ptr_t p)
@@ -209,7 +209,7 @@ static void ensure_struct(ptr_t p)
}
be -> height = HEIGHT_UNKNOWN;
be -> height_gc_no = (unsigned short)(GC_gc_no - 1);
- GC_ASSERT(be >= back_edge_space);
+ GC_ASSERT((word)be >= (word)back_edge_space);
SET_OH_BG_PTR(p, (word)be | FLAG_MANY);
}
}
@@ -327,7 +327,7 @@ static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr)
if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) {
gc_descr = n_bytes;
}
- while (currentp < (word *)(p + gc_descr)) {
+ while ((word)currentp < (word)(p + gc_descr)) {
word current = *currentp++;
FIXUP_POINTER(current);
if (current >= (word)GC_least_plausible_heap_addr &&
@@ -376,8 +376,7 @@ static word backwards_height(ptr_t p)
FOR_EACH_PRED(q, p, {
word this_height;
if (GC_is_marked(q) && !(FLAG_MANY & (word)GET_OH_BG_PTR(p))) {
- if (GC_print_stats)
- GC_log_printf("Found bogus pointer from %p to %p\n", q, p);
+ GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", q, p);
/* Reachable object "points to" unreachable one. */
/* Could be caused by our lax treatment of GC descriptors. */
this_height = 1;
@@ -458,18 +457,17 @@ GC_INNER void GC_traverse_back_graph(void)
void GC_print_back_graph_stats(void)
{
- GC_printf("Maximum backwards height of reachable objects at GC %lu is %ld\n",
+ GC_printf("Maximum backwards height of reachable objects at GC %lu is %lu\n",
(unsigned long) GC_gc_no, (unsigned long)GC_max_height);
if (GC_max_height > GC_max_max_height) {
GC_max_max_height = GC_max_height;
- GC_printf("The following unreachable object is last in a longest chain "
- "of unreachable objects:\n");
+ GC_err_printf(
+ "The following unreachable object is last in a longest chain "
+ "of unreachable objects:\n");
GC_print_heap_obj(GC_deepest_obj);
}
- if (GC_print_stats) {
- GC_log_printf("Needed max total of %d back-edge structs\n",
- GC_n_back_edge_structs);
- }
+ GC_COND_LOG_PRINTF("Needed max total of %d back-edge structs\n",
+ GC_n_back_edge_structs);
GC_apply_to_each_object(reset_back_edge);
GC_deepest_obj = 0;
}
diff --git a/blacklst.c b/blacklst.c
index 9fd00167..dcac51a7 100644
--- a/blacklst.c
+++ b/blacklst.c
@@ -21,7 +21,7 @@
* See the definition of page_hash_table in gc_private.h.
* False hits from the stack(s) are much more dangerous than false hits
* from elsewhere, since the former can pin a large object that spans the
- * block, eventhough it does not start on the dangerous block.
+ * block, even though it does not start on the dangerous block.
*/
/*
@@ -57,31 +57,37 @@ STATIC void GC_clear_bl(word *);
GC_INNER void GC_default_print_heap_obj_proc(ptr_t p)
{
ptr_t base = GC_base(p);
- GC_err_printf("start: %p, appr. length: %ld", base,
- (unsigned long)GC_size(base));
+ int kind = HDR(base)->hb_obj_kind;
+
+ GC_err_printf("object at %p of appr. %lu bytes (%s)\n",
+ base, (unsigned long)GC_size(base),
+ kind == PTRFREE ? "atomic" :
+ IS_UNCOLLECTABLE(kind) ? "uncollectable" : "composite");
}
GC_INNER void (*GC_print_heap_obj)(ptr_t p) = GC_default_print_heap_obj_proc;
#ifdef PRINT_BLACK_LIST
-STATIC void GC_print_source_ptr(ptr_t p)
-{
- ptr_t base = GC_base(p);
+ STATIC void GC_print_blacklisted_ptr(word p, ptr_t source,
+ const char *kind_str)
+ {
+ ptr_t base = GC_base(source);
+
if (0 == base) {
- if (0 == p) {
- GC_err_printf("in register");
- } else {
- GC_err_printf("in root set");
- }
+ GC_err_printf("Black listing (%s) %p referenced from %p in %s\n",
+ kind_str, (ptr_t)p, source,
+ NULL != source ? "root set" : "register");
} else {
- GC_err_printf("in object at ");
/* FIXME: We can't call the debug version of GC_print_heap_obj */
/* (with PRINT_CALL_CHAIN) here because the lock is held and */
/* the world is stopped. */
- GC_default_print_heap_obj_proc(base);
+ GC_err_printf("Black listing (%s) %p referenced from %p in"
+ " object at %p of appr. %lu bytes\n",
+ kind_str, (ptr_t)p, source,
+ base, (unsigned long)GC_size(base));
}
-}
-#endif
+ }
+#endif /* PRINT_BLACK_LIST */
GC_INNER void GC_bl_init_no_interiors(void)
{
@@ -141,9 +147,9 @@ GC_INNER void GC_promote_black_lists(void)
GC_incomplete_normal_bl = very_old_normal_bl;
GC_incomplete_stack_bl = very_old_stack_bl;
GC_total_stack_black_listed = total_stack_black_listed();
- if (GC_print_stats == VERBOSE)
- GC_log_printf("%ld bytes in heap blacklisted for interior pointers\n",
- (unsigned long)GC_total_stack_black_listed);
+ GC_VERBOSE_LOG_PRINTF(
+ "%lu bytes in heap blacklisted for interior pointers\n",
+ (unsigned long)GC_total_stack_black_listed);
if (GC_total_stack_black_listed != 0) {
GC_black_list_spacing =
HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed);
@@ -183,10 +189,7 @@ GC_INNER void GC_unpromote_black_lists(void)
if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) {
# ifdef PRINT_BLACK_LIST
if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) {
- GC_err_printf("Black listing (normal) %p referenced from %p ",
- (ptr_t)p, source);
- GC_print_source_ptr(source);
- GC_err_puts("\n");
+ GC_print_blacklisted_ptr(p, source, "normal");
}
# endif
set_pht_entry_from_index(GC_incomplete_normal_bl, index);
@@ -207,10 +210,7 @@ GC_INNER void GC_unpromote_black_lists(void)
if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) {
# ifdef PRINT_BLACK_LIST
if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) {
- GC_err_printf("Black listing (stack) %p referenced from %p ",
- (ptr_t)p, source);
- GC_print_source_ptr(source);
- GC_err_puts("\n");
+ GC_print_blacklisted_ptr(p, source, "stack");
}
# endif
set_pht_entry_from_index(GC_incomplete_stack_bl, index);
@@ -225,7 +225,7 @@ GC_INNER void GC_unpromote_black_lists(void)
* If (h,len) is not black listed, return 0.
* Knows about the structure of the black list hash tables.
*/
-GC_INNER struct hblk * GC_is_black_listed(struct hblk *h, word len)
+struct hblk * GC_is_black_listed(struct hblk *h, word len)
{
word index = PHT_HASH((word)h);
word i;
@@ -265,7 +265,7 @@ STATIC word GC_number_stack_black_listed(struct hblk *start,
register struct hblk * h;
word result = 0;
- for (h = start; h < endp1; h++) {
+ for (h = start; (word)h < (word)endp1; h++) {
word index = PHT_HASH((word)h);
if (get_pht_entry_from_index(GC_old_stack_bl, index)) result++;
diff --git a/build/s60v3/bld.inf b/build/s60v3/bld.inf
new file mode 100644
index 00000000..491fcf43
--- /dev/null
+++ b/build/s60v3/bld.inf
@@ -0,0 +1,11 @@
+/*
+ Name : bld.inf
+ Description : This file provides the information required for building the
+ whole of a libgc.
+*/
+
+PRJ_PLATFORMS
+default armv5
+
+PRJ_MMPFILES
+libgc.mmp
diff --git a/build/s60v3/libgc.mmp b/build/s60v3/libgc.mmp
new file mode 100644
index 00000000..8c0dcab5
--- /dev/null
+++ b/build/s60v3/libgc.mmp
@@ -0,0 +1,76 @@
+TARGET libgc.dll
+
+TARGETTYPE dll
+UID 0x1000008d 0x200107C2 // check uid
+
+EXPORTUNFROZEN
+EPOCALLOWDLLDATA
+//ALWAYS_BUILD_AS_ARM
+//nocompresstarget
+//srcdbg
+//baseaddress 00500000
+//LINKEROPTION CW -map libgc.map
+//LINKEROPTION CW -filealign 0x10000
+
+CAPABILITY PowerMgmt ReadDeviceData ReadUserData WriteDeviceData WriteUserData SwEvent LocalServices NetworkServices UserEnvironment
+
+
+MACRO ALL_INTERIOR_POINTERS
+MACRO NO_EXECUTE_PERMISSION
+MACRO USE_MMAP
+MACRO GC_DONT_REGISTER_MAIN_STATIC_DATA
+MACRO GC_DLL
+MACRO SYMBIAN
+//MACRO ENABLE_DISCLAIM
+//MACRO GC_GCJ_SUPPORT
+
+USERINCLUDE ..\..\include
+USERINCLUDE ..\..\include\private
+
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\stdapis
+
+SOURCEPATH ..\..\
+
+SOURCE allchblk.c
+SOURCE alloc.c
+SOURCE blacklst.c
+SOURCE dbg_mlc.c
+SOURCE dyn_load.c
+SOURCE finalize.c
+SOURCE fnlz_mlc.c
+//SOURCE gc_cpp.cpp
+SOURCE gcj_mlc.c
+SOURCE headers.c
+SOURCE mach_dep.c
+SOURCE malloc.c
+SOURCE mallocx.c
+SOURCE mark.c
+SOURCE mark_rts.c
+SOURCE misc.c
+SOURCE new_hblk.c
+SOURCE obj_map.c
+SOURCE os_dep.c
+SOURCE extra/symbian.cpp
+SOURCE ptr_chck.c
+SOURCE reclaim.c
+SOURCE stubborn.c
+SOURCE typd_mlc.c
+
+/*
+#ifdef ENABLE_ABIV2_MODE
+ DEBUGGABLE_UDEBONLY
+#endif
+*/
+
+// Using main() as entry point
+STATICLIBRARY libcrt0.lib
+
+// libc and euser are always needed when using main() entry point
+LIBRARY libc.lib
+
+
+LIBRARY euser.lib
+LIBRARY efsrv.lib
+LIBRARY avkon.lib
+LIBRARY eikcore.lib
diff --git a/checksums.c b/checksums.c
index 414ee36d..f5ad843a 100644
--- a/checksums.c
+++ b/checksums.c
@@ -68,7 +68,7 @@ STATIC word GC_checksum(struct hblk *h)
word *lim = (word *)(h+1);
word result = 0;
- while (p < lim) {
+ while ((word)p < (word)lim) {
result += *p++;
}
return(result | 0x80000000 /* doesn't look like pointer */);
@@ -162,12 +162,11 @@ STATIC void GC_check_blocks(void)
GC_bytes_in_used_blocks = 0;
GC_apply_to_all_blocks(GC_add_block, (word)0);
- if (GC_print_stats)
- GC_log_printf("GC_bytes_in_used_blocks = %lu,"
- " bytes_in_free_blocks = %lu, heapsize = %lu\n",
- (unsigned long)GC_bytes_in_used_blocks,
- (unsigned long)bytes_in_free_blocks,
- (unsigned long)GC_heapsize);
+ GC_COND_LOG_PRINTF("GC_bytes_in_used_blocks = %lu,"
+ " bytes_in_free_blocks = %lu, heapsize = %lu\n",
+ (unsigned long)GC_bytes_in_used_blocks,
+ (unsigned long)bytes_in_free_blocks,
+ (unsigned long)GC_heapsize);
if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) {
GC_err_printf("LOST SOME BLOCKS!!\n");
}
@@ -193,17 +192,15 @@ void GC_check_dirty(void)
for (i = 0; i < GC_n_heap_sects; i++) {
start = GC_heap_sects[i].hs_start;
for (h = (struct hblk *)start;
- h < (struct hblk *)(start + GC_heap_sects[i].hs_bytes);
- h++) {
+ (word)h < (word)(start + GC_heap_sects[i].hs_bytes); h++) {
GC_update_check_page(h, index);
index++;
if (index >= NSUMS) goto out;
}
}
out:
- if (GC_print_stats)
- GC_log_printf("Checked %lu clean and %lu dirty pages\n",
- (unsigned long)GC_n_clean, (unsigned long)GC_n_dirty);
+ GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n",
+ (unsigned long)GC_n_clean, (unsigned long)GC_n_dirty);
if (GC_n_dirty_errors > 0) {
GC_err_printf("Found %d dirty bit errors (%d were faulted)\n",
GC_n_dirty_errors, GC_n_faulted_dirty_errors);
diff --git a/configure.ac b/configure.ac
index bdaf215e..031b129f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,16 +12,15 @@
dnl Process this file with autoconf to produce configure.
# Initialization
-AC_INIT(gc,7.3alpha1,gc@linux.hpl.hp.com)
+AC_INIT(gc,7.3alpha3,gc@linux.hpl.hp.com)
## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)?
AC_CONFIG_SRCDIR(gcj_mlc.c)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_TARGET
-AC_PREREQ(2.64)
-AC_REVISION($Revision: 1.69 $)
+AC_PREREQ(2.61)
GC_SET_VERSION
AM_INIT_AUTOMAKE([foreign dist-bzip2 nostdinc])
-AM_CONFIG_HEADER([include/private/config.h])
+AC_CONFIG_HEADERS([include/config.h])
AM_MAINTAINER_MODE
AC_SUBST(PACKAGE)
@@ -29,10 +28,11 @@ AC_SUBST(GC_VERSION)
AM_PROG_CC_C_O
AC_PROG_CXX
-
AM_PROG_AS
-
AC_PROG_INSTALL
+LT_INIT
+# Note: If Autoconf reports that LIBTOOL (or AC_ENABLE_SHARED, or
+# AC_PROG_LIBTOOL) is undefined, Libtool installation should be checked.
# Special CFLAGS to use when building
gc_cflags=""
@@ -113,7 +113,9 @@ AC_ARG_ENABLE(parallel-mark,
[parallelize marking and free list construction])],
[case "$THREADS" in
no | none | single)
- AC_MSG_ERROR([Parallel mark requires --enable-threads=x spec])
+ if test "${enable_parallel_mark}" != no; then
+ AC_MSG_ERROR([Parallel mark requires --enable-threads=x spec])
+ fi
;;
esac ]
)
@@ -124,12 +126,12 @@ AC_ARG_ENABLE(cplusplus,
dnl Features which may be selected in the following thread-detection switch.
AH_TEMPLATE([PARALLEL_MARK], [Define to enable parallel marking.])
AH_TEMPLATE([THREAD_LOCAL_ALLOC],
- [Define to enable thread-local allocation optimisation.])
+ [Define to enable thread-local allocation optimization.])
AH_TEMPLATE([USE_COMPILER_TLS],
[Define to use of compiler-support for thread-local variables.])
dnl Thread selection macros.
-AH_TEMPLATE([GC_THREADS], [Define to support platform-specific \
+AH_TEMPLATE([GC_THREADS], [Define to support platform-specific
threads.])
AH_TEMPLATE([GC_AIX_THREADS], [Define to support IBM AIX threads.])
AH_TEMPLATE([GC_DARWIN_THREADS], [Define to support Darwin pthreads.])
@@ -211,17 +213,20 @@ case "$THREADS" in
*-*-openbsd*)
AC_DEFINE(GC_OPENBSD_THREADS)
THREADDLLIBS=-pthread
- INCLUDES="$INCLUDES -pthread"
- openbsd_threads=true
+ AM_CFLAGS="$AM_CFLAGS -pthread"
;;
*-*-freebsd*)
AC_MSG_WARN("FreeBSD does not yet fully support threads with Boehm GC.")
AC_DEFINE(GC_FREEBSD_THREADS)
- INCLUDES="$INCLUDES -pthread"
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ if test "${enable_parallel_mark}" = yes; then
+ AC_DEFINE(PARALLEL_MARK)
+ fi
+ AC_DEFINE(THREAD_LOCAL_ALLOC)
;;
*-*-kfreebsd*-gnu)
AC_DEFINE(GC_FREEBSD_THREADS)
- INCLUDES="$INCLUDES -pthread"
+ AM_CFLAGS="$AM_CFLAGS -pthread"
THREADDLLIBS=-pthread
AC_DEFINE(_REENTRANT)
if test "${enable_parallel_mark}" = yes; then
@@ -276,6 +281,7 @@ case "$THREADS" in
fi
AC_DEFINE(THREAD_LOCAL_ALLOC)
THREADDLLIBS="-lpthread"
+ win32_threads=true
;;
*-*-darwin*)
AC_DEFINE(GC_DARWIN_THREADS)
@@ -296,7 +302,7 @@ case "$THREADS" in
# May want to enable it in other cases, too.
# Measurements have not yet been done.
fi
- INCLUDES="$INCLUDES -pthread"
+ AM_CFLAGS="$AM_CFLAGS -pthread"
THREADDLLIBS="-lpthread -lrt"
;;
*)
@@ -346,7 +352,7 @@ case "$THREADS" in
AC_DEFINE([DGUX_THREADS], 1,
[Define to enable support for DB/UX threads.])
# Enable _POSIX4A_DRAFT10_SOURCE with flag -pthread
- INCLUDES="-pthread $INCLUDES"
+ AM_CFLAGS="-pthread $AM_CFLAGS"
;;
aix)
THREADS=posix
@@ -371,7 +377,6 @@ AM_CONDITIONAL(THREADS, test x$THREADS != xnone)
AM_CONDITIONAL(PTHREADS, test x$THREADS = xposix)
AM_CONDITIONAL(DARWIN_THREADS, test x$darwin_threads = xtrue)
AM_CONDITIONAL(WIN32_THREADS, test x$win32_threads = xtrue)
-AM_CONDITIONAL(OPENBSD_THREADS, test x$openbsd_threads = xtrue)
case "$host" in
powerpc-*-darwin*)
@@ -385,7 +390,7 @@ case "$host" in
;;
esac
-if test "$GCC" == yes; then
+if test "$GCC" = yes; then
# Output all warnings.
AC_MSG_CHECKING(for gcc -Wextra)
old_CFLAGS="$CFLAGS"
@@ -410,7 +415,7 @@ if test $compiler_xlc = yes -a "$powerpc_darwin" = true; then
AC_DEFINE([DARWIN_DONT_PARSE_STACK], 1, [See doc/README.macros.])
fi
-if test "$GCC" == yes; then
+if test "$GCC" = yes; then
# Disable aliasing optimization unless forced to.
AC_MSG_CHECKING([whether gcc supports -fno-strict-aliasing])
ac_cv_fno_strict_aliasing=no
@@ -481,23 +486,41 @@ TARGET_ECOS="$with_ecos"
addobjs=
addlibs=
-CXXINCLUDES=
+CXXLIBS=
+
case "$TARGET_ECOS" in
no)
;;
*)
AC_DEFINE([ECOS], 1, [Define to enable eCos target support.])
- CXXINCLUDES="-I${TARGET_ECOS}/include"
+ AM_CPPFLAGS="-I${TARGET_ECOS}/include $AM_CPPFLAGS"
addobjs="$addobjs ecos.lo"
;;
esac
AM_CONDITIONAL(CPLUSPLUS, test "${enable_cplusplus}" = yes)
-AC_SUBST(CXX)
+if test "$GCC" = yes; then
+ if test "${enable_cplusplus}" = yes; then
+ case "$host" in
+ *-*-cygwin* | *-*-mingw*)
+ AC_MSG_CHECKING([whether libsupc++ required])
+ SUPC="`$CXX -print-file-name=libsupc++.a 2>/dev/null`"
+ if test -n "$SUPC" -a "$SUPC" != "libsupc++.a"; then
+ AC_MSG_RESULT(yes)
+ CXXLIBS="-lsupc++"
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+ esac
+ fi
+fi
-AC_SUBST(INCLUDES)
-AC_SUBST(CXXINCLUDES)
+AC_SUBST(CXX)
+AC_SUBST(AM_CFLAGS)
+AC_SUBST(AM_CPPFLAGS)
+AC_SUBST(CXXLIBS)
# Configuration of shared libraries
#
@@ -515,8 +538,8 @@ esac
AC_MSG_RESULT($enable_shared)
# Compile with GC_DLL defined unless building static libraries.
-if test "${enable_shared}" == yes; then
- if test "${enable_static}" == no; then
+if test "${enable_shared}" = yes; then
+ if test "${enable_static}" = no; then
AC_DEFINE(GC_DLL)
if test "$GCC" = yes; then
# Pass -fvisibility=hidden option if supported
@@ -540,7 +563,8 @@ machdep=
case "$host" in
alpha-*-openbsd*)
if test x"${ac_cv_lib_dl_dlopen}" != xyes ; then
- AC_MSG_WARN(OpenBSD/Alpha without dlopen(). Shared library support is disabled)
+ AC_MSG_WARN(
+ "OpenBSD/Alpha without dlopen(). Shared library support is disabled.")
fi
;;
i?86-*-solaris2.[[89]])
@@ -556,7 +580,7 @@ case "$host" in
sparc-*-netbsd*)
machdep="src/sparc_netbsd_mach_dep.lo"
;;
- sparc*-*-linux* | sparc*-*-openbsd* | sparc64-*-netbsd*)
+ sparc*-*-linux* | sparc*-*-openbsd* | sparc64-*-freebsd* | sparc64-*-netbsd*)
machdep="src/sparc_mach_dep.lo"
;;
sparc-sun-solaris2.3)
@@ -623,9 +647,11 @@ case "$host" in
esac
dnl Include defines that have become de facto standard.
-dnl ALL_INTERIOR_POINTERS and NO_EXECUTE_PERMISSION can be overridden in the startup code.
+dnl ALL_INTERIOR_POINTERS and NO_EXECUTE_PERMISSION can be overridden
+dnl in the startup code.
AC_DEFINE([NO_EXECUTE_PERMISSION], [1],
- [Define to make the collector not allocate executable memory by default.])
+ [Define to make the collector not allocate executable memory
+ by default.])
AC_DEFINE([ALL_INTERIOR_POINTERS], [1],
[Define to recognise all pointers to the interior of objects.])
@@ -638,8 +664,6 @@ dnl enable_gcj_support=no
AC_ARG_ENABLE(gcj-support,
[AC_HELP_STRING([--disable-gcj-support],
[Disable support for gcj.])])
-AM_CONDITIONAL(ENABLE_GCJ_SUPPORT,
- [test x"$enable_gcj_support" != xno])
if test x"$enable_gcj_support" != xno; then
AC_DEFINE(GC_GCJ_SUPPORT, 1, [Define to include support for gcj.])
fi
@@ -658,12 +682,16 @@ AC_ARG_ENABLE(gc-debug,
[AC_HELP_STRING([--enable-gc-debug],
[include full support for pointer backtracing etc.])],
[ if test "$enable_gc_debug" = "yes"; then
- AC_MSG_WARN("Should define GC_DEBUG and use debug alloc. in clients.")
+ AC_MSG_WARN("Should define GC_DEBUG and use debug alloc in clients.")
AC_DEFINE([KEEP_BACK_PTRS], 1,
[Define to save back-pointers in debugging headers.])
keep_back_ptrs=true
AC_DEFINE([DBG_HDRS_ALL], 1,
[Define to force debug headers on all objects.])
+ AH_TEMPLATE([SHORT_DBG_HDRS],
+ [Shorten the headers to minimize object size at the expense
+ of checking for writes past the end (see doc/README.macros).])
+
case $host in
ia64-*-linux* )
AC_DEFINE(MAKE_BACK_GRAPH)
@@ -684,8 +712,23 @@ AC_ARG_ENABLE(gc-debug,
;;
esac ]
fi)
+AM_CONDITIONAL([MAKE_BACK_GRAPH], [test x"$enable_gc_debug" = xyes])
AM_CONDITIONAL([KEEP_BACK_PTRS], [test x"$keep_back_ptrs" = xtrue])
+# Check for dladdr (used for debugging).
+AC_MSG_CHECKING(for dladdr)
+have_dladdr=no
+AC_TRY_COMPILE([
+#define _GNU_SOURCE 1
+#include <dlfcn.h>], [{
+ Dl_info info;
+ (void)dladdr("", &info);
+}], [ have_dladdr=yes ])
+AC_MSG_RESULT($have_dladdr)
+if test x"$have_dladdr" = xyes; then
+ AC_DEFINE([HAVE_DLADDR], 1, [Define to use 'dladdr' function.])
+fi
+
# Check for AViiON Machines running DGUX
ac_is_dgux=no
AC_CHECK_HEADER(sys/dg_sys_info.h,
@@ -755,6 +798,19 @@ if test "${enable_large_config}" = yes; then
AC_DEFINE(LARGE_CONFIG, 1, [Define to optimize for large heaps or root sets.])
fi
+AC_ARG_ENABLE(handle-fork,
+ [AC_HELP_STRING([--enable-handle-fork],
+ [Attempt to ensure a usable collector after fork() in multi-threaded
+ programs.])])
+
+if test "${enable_handle_fork}" = yes; then
+ AC_DEFINE(HANDLE_FORK, 1,
+ [Define to install pthread_atfork() handlers by default.])
+elif test "${enable_handle_fork}" = no; then
+ AC_DEFINE(NO_HANDLE_FORK, 1,
+ [Prohibit installation of pthread_atfork() handlers.])
+fi
+
dnl This is something of a hack. When cross-compiling we turn off
dnl some functionality. We also enable the "small" configuration.
dnl These is only correct when targetting an embedded system. FIXME.
@@ -764,7 +820,7 @@ if test -n "${with_cross_host}"; then
[Define to tune the collector for small heap sizes.])
fi
-if test "$enable_gc_debug" == "no"; then
+if test "$enable_gc_debug" = "no"; then
AC_DEFINE([NO_DEBUGGING], 1,
[Disable debugging, like GC_dump and its callees.])
fi
@@ -785,6 +841,14 @@ AC_ARG_ENABLE(munmap,
if test "${enable_munmap}" != ""; then
AC_DEFINE([USE_MMAP], 1,
[Define to use mmap instead of sbrk to expand the heap.])
+ case "$host" in
+ *-*-cygwin*)
+ # Workaround for Cygwin: use VirtualAlloc since mmap(PROT_NONE) fails
+ AC_DEFINE([USE_WINALLOC], 1,
+ [Define to use Win32 VirtualAlloc (instead of sbrk or
+ mmap) to expand the heap.])
+ ;;
+ esac
AC_DEFINE([USE_MUNMAP], 1,
[Define to return memory to OS with munmap calls
(see doc/README.macros).])
@@ -820,16 +884,23 @@ AC_ARG_WITH([libatomic-ops],
# Check for an external libatomic_ops if the answer was yes or check. If not
# found, fail on yes, and convert check to no.
+# Note: "syntax error near unexpected token ATOMIC_OPS" reported by configure
+# means Autotools pkg.m4 file was not found during aclocal.m4 generation.
+missing_libatomic_ops=false
AS_IF([test x"$with_libatomic_ops" != xno],
[ PKG_CHECK_MODULES([ATOMIC_OPS], [atomic_ops], [],
+ [ missing_libatomic_ops=true ]) ])
+AS_IF([test x$missing_libatomic_ops = xtrue ],
[ AS_IF([test x"$with_libatomic_ops" != xcheck],
- [AC_MSG_ERROR([A external libatomic_ops was not found.])])
- with_libatomic_ops=no ]) ])
+ [ AC_MSG_ERROR([An external libatomic_ops was not found]) ])
+ with_libatomic_ops=no ])
# If we have neither an external or an internal version, offer a useful hint
# and exit.
AS_IF([test x"$with_libatomic_ops" = xno -a ! -e "$srcdir/libatomic_ops"],
- [ AC_MSG_ERROR([libatomic_ops is required. You can either install it on your system, or fetch and unpack a recent version into the source directory and link or rename it to libatomic_ops.]) ])
+ [ AC_MSG_ERROR([libatomic_ops is required. You can either install it on
+ your system, or fetch and unpack a recent version into the
+ source directory and link or rename it to libatomic_ops.]) ])
# Finally, emit the definitions for bundled or external AO.
AC_MSG_CHECKING([which libatomic_ops to use])
diff --git a/cord/cordbscs.c b/cord/cordbscs.c
index 0314b7ae..a05e6e04 100644
--- a/cord/cordbscs.c
+++ b/cord/cordbscs.c
@@ -12,7 +12,7 @@
*/
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef CORD_BUILD
# define CORD_BUILD
@@ -193,6 +193,7 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny)
result_len = right_len + leny; /* length of new_right */
if (result_len <= SHORT_LIMIT) {
new_right = GC_MALLOC_ATOMIC(result_len + 1);
+ if (new_right == 0) OUT_OF_MEMORY;
memcpy(new_right, right, right_len);
memcpy(new_right + right_len, y, leny);
new_right[result_len] = '\0';
@@ -289,10 +290,10 @@ CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len)
if (c == '\0') goto gen_case;
buf[i] = c;
}
- buf[i] = '\0';
+
result = GC_MALLOC_ATOMIC(len+1);
if (result == 0) OUT_OF_MEMORY;
- strcpy(result, buf);
+ memcpy(result, buf, len);
result[len] = '\0';
return((CORD) result);
}
@@ -353,6 +354,7 @@ CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f)
sa->sa_cord = (CordRep *)x;
sa->sa_index = i;
result = CORD_from_fn(f, (void *)sa, n);
+ if (result == CORD_EMPTY) return CORD_EMPTY; /* n == 0 */
((CordRep *)result) -> function.header = SUBSTR_HDR;
return (result);
}
@@ -439,10 +441,10 @@ CORD CORD_substr_checked(CORD x, size_t i, size_t n)
}
*p++ = c;
}
- *p = '\0';
result = GC_MALLOC_ATOMIC(n+1);
if (result == 0) OUT_OF_MEMORY;
- strcpy(result, buf);
+ memcpy(result, buf, n);
+ result[n] = '\0';
return(result);
}
}
@@ -568,7 +570,7 @@ int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data)
* The following functions are concerned with balancing cords.
* Strategy:
* Scan the cord from left to right, keeping the cord scanned so far
- * as a forest of balanced trees of exponentialy decreasing length.
+ * as a forest of balanced trees of exponentially decreasing length.
* When a new subtree needs to be added to the forest, we concatenate all
* shorter ones to the new tree in the appropriate order, and then insert
* the result into the forest.
diff --git a/cord/cordprnt.c b/cord/cordprnt.c
index 864c50c7..94c49be8 100644
--- a/cord/cordprnt.c
+++ b/cord/cordprnt.c
@@ -22,7 +22,7 @@
/* sprintf implementation whenever possible. */
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef CORD_BUILD
# define CORD_BUILD
@@ -313,9 +313,17 @@ int CORD_vsprintf(CORD * out, CORD format, va_list args)
(void) va_arg(args, double);
break;
default:
+# if defined(__va_copy) \
+ || (defined(__GNUC__) && !defined(__DJGPP__))
+ va_end(vsprintf_args);
+# endif
return(-1);
}
res = vsprintf(buf, conv_spec, vsprintf_args);
+# if defined(__va_copy) \
+ || (defined(__GNUC__) && !defined(__DJGPP__))
+ va_end(vsprintf_args);
+# endif
len = (size_t)res;
if ((char *)(GC_word)res == buf) {
/* old style vsprintf */
diff --git a/cord/cordxtra.c b/cord/cordxtra.c
index 19acafea..610ea893 100644
--- a/cord/cordxtra.c
+++ b/cord/cordxtra.c
@@ -18,7 +18,7 @@
*/
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef CORD_BUILD
# define CORD_BUILD
@@ -63,7 +63,7 @@ typedef void (* oom_fn)(void);
ABORT("Out of memory\n"); }
# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); }
-#if __GNUC__ >= 4
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define CORD_ATTR_UNUSED __attribute__((__unused__))
#else
# define CORD_ATTR_UNUSED /* empty */
@@ -135,8 +135,8 @@ int CORD_batched_fill_proc(const char * s, void * client_data)
return(0);
}
-/* Fill buf with len characters starting at i. */
-/* Assumes len characters are available. */
+/* Fill buf with len characters starting at i. */
+/* Assumes len characters are available. */
void CORD_fill_buf(CORD x, size_t i, size_t len, char * buf)
{
CORD_fill_data fd;
@@ -362,12 +362,12 @@ size_t CORD_rchr(CORD x, size_t i, int c)
}
}
-/* Find the first occurrence of s in x at position start or later. */
+/* Find the first occurrence of s in x at position start or later. */
/* This uses an asymptotically poor algorithm, which should typically */
/* perform acceptably. We compare the first few characters directly, */
-/* and call CORD_ncmp whenever there is a partial match. */
+/* and call CORD_ncmp whenever there is a partial match. */
/* This has the advantage that we allocate very little, or not at all. */
-/* It's very fast if there are few close misses. */
+/* It's very fast if there are few close misses. */
size_t CORD_str(CORD x, size_t start, CORD s)
{
CORD_pos xpos;
@@ -375,10 +375,10 @@ size_t CORD_str(CORD x, size_t start, CORD s)
size_t slen;
register size_t start_len;
const char * s_start;
- unsigned long s_buf = 0; /* The first few characters of s */
- unsigned long x_buf = 0; /* Start of candidate substring. */
+ unsigned long s_buf = 0; /* The first few characters of s */
+ unsigned long x_buf = 0; /* Start of candidate substring. */
/* Initialized only to make compilers */
- /* happy. */
+ /* happy. */
unsigned long mask = 0;
register size_t i;
register size_t match_pos;
@@ -460,9 +460,9 @@ CORD CORD_from_file_eager(FILE * f)
for(;;) {
c = getc(f);
if (c == 0) {
- /* Append the right number of NULs */
- /* Note that any string of NULs is rpresented in 4 words, */
- /* independent of its length. */
+ /* Append the right number of NULs */
+ /* Note that any string of NULs is represented in 4 words, */
+ /* independent of its length. */
register size_t count = 1;
CORD_ec_flush_buf(ecord);
@@ -476,18 +476,18 @@ CORD CORD_from_file_eager(FILE * f)
return(CORD_balance(CORD_ec_to_cord(ecord)));
}
-/* The state maintained for a lazily read file consists primarily */
-/* of a large direct-mapped cache of previously read values. */
-/* We could rely more on stdio buffering. That would have 2 */
-/* disadvantages: */
-/* 1) Empirically, not all fseek implementations preserve the */
-/* buffer whenever they could. */
-/* 2) It would fail if 2 different sections of a long cord */
-/* were being read alternately. */
-/* We do use the stdio buffer for read ahead. */
-/* To guarantee thread safety in the presence of atomic pointer */
-/* writes, cache lines are always replaced, and never modified in */
-/* place. */
+/* The state maintained for a lazily read file consists primarily */
+/* of a large direct-mapped cache of previously read values. */
+/* We could rely more on stdio buffering. That would have 2 */
+/* disadvantages: */
+/* 1) Empirically, not all fseek implementations preserve the */
+/* buffer whenever they could. */
+/* 2) It would fail if 2 different sections of a long cord */
+/* were being read alternately. */
+/* We do use the stdio buffer for read ahead. */
+/* To guarantee thread safety in the presence of atomic pointer */
+/* writes, cache lines are always replaced, and never modified in */
+/* place. */
# define LOG_CACHE_SZ 14
# define CACHE_SZ (1 << LOG_CACHE_SZ)
@@ -519,8 +519,7 @@ typedef struct {
} refill_data;
/* Executed with allocation lock. */
-static char refill_cache(client_data)
-refill_data * client_data;
+static char refill_cache(refill_data * client_data)
{
register lf_state * state = client_data -> state;
register size_t file_pos = client_data -> file_pos;
@@ -583,7 +582,7 @@ CORD CORD_from_file_lazy_inner(FILE * f, size_t len)
/* This greatly increases the probability */
/* of avoiding deadlock if buffer allocation */
/* is redirected to GC_malloc and the */
- /* world is multithreaded. */
+ /* world is multi-threaded. */
char buf[1];
if (fread(buf, 1, 1, f) > 1) {
diff --git a/cord/tests/cordtest.c b/cord/tests/cordtest.c
index 24f56f1a..cf5c6966 100644
--- a/cord/tests/cordtest.c
+++ b/cord/tests/cordtest.c
@@ -187,15 +187,20 @@ void test_extras(void)
if (CORD_str(x,0,"9abcdefghijx") != CORD_NOT_FOUND)
ABORT("CORD_str failed 3");
if (CORD_str(x,0,"9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4");
+ /* Note: f1a, f1b, f2 handles are closed lazily by CORD library. */
+ /* TODO: Propose and use CORD_fclose. */
+ *(CORD volatile *)&w = CORD_EMPTY;
+ *(CORD volatile *)&z = CORD_EMPTY;
+ GC_gcollect();
+ GC_invoke_finalizers();
+ /* Of course, this does not guarantee the files are closed. */
if (remove(FNAME1) != 0) {
/* On some systems, e.g. OS2, this may fail if f1 is still open. */
- if ((fclose(f1a) == EOF) & (fclose(f1b) == EOF))
- ABORT("fclose(f1) failed");
- if (remove(FNAME1) != 0) ABORT("remove 1 failed");
+ /* But we cannot call fclose as it might lead to double close. */
+ fprintf(stderr, "WARNING: remove(FNAME1) failed\n");
}
if (remove(FNAME2) != 0) {
- if (fclose(f2) == EOF) ABORT("fclose(f2) failed");
- if (remove(FNAME2) != 0) ABORT("remove 2 failed");
+ fprintf(stderr, "WARNING: remove(FNAME2) failed\n");
}
}
@@ -203,8 +208,8 @@ void test_printf(void)
{
CORD result;
char result2[200];
- long l;
- short s;
+ long l = -1;
+ short s = (short)-1;
CORD x;
if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7)
@@ -221,7 +226,9 @@ void test_printf(void)
x = CORD_cat(x,x);
if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124)
ABORT("CORD_sprintf failed 3");
- (void) sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x));
+ (void)snprintf(result2, sizeof(result2), "->%-120.78s!\n",
+ CORD_to_char_star(x));
+ result2[sizeof(result2) - 1] = '\0';
if (CORD_cmp(result, result2) != 0)ABORT("CORD_sprintf goofed 5");
}
@@ -234,6 +241,6 @@ int main(void)
test_basics();
test_extras();
test_printf();
- CORD_fprintf(stderr, "SUCCEEDED\n");
+ CORD_fprintf(stdout, "SUCCEEDED\n");
return(0);
}
diff --git a/cord/tests/de.c b/cord/tests/de.c
index f08167bc..068848c4 100644
--- a/cord/tests/de.c
+++ b/cord/tests/de.c
@@ -242,7 +242,7 @@ CORD retrieve_line(CORD s, size_t pos, unsigned column)
{
CORD candidate = CORD_substr(s, pos, column + COLS);
/* avoids scanning very long lines */
- int eol = CORD_chr(candidate, 0, '\n');
+ size_t eol = CORD_chr(candidate, 0, '\n');
int len;
if (eol == CORD_NOT_FOUND) eol = CORD_len(candidate);
diff --git a/cord/tests/de_win.c b/cord/tests/de_win.c
index a49f8179..1179b8f7 100644
--- a/cord/tests/de_win.c
+++ b/cord/tests/de_win.c
@@ -69,7 +69,8 @@ int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
if (RegisterClass (&wndclass) == 0) {
char buf[50];
- sprintf(buf, "RegisterClass: error code: 0x%X", GetLastError());
+ sprintf(buf, "RegisterClass: error code: 0x%X",
+ (unsigned)GetLastError());
de_error(buf);
return(0);
}
@@ -104,7 +105,8 @@ int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
if (hwnd == NULL) {
char buf[50];
- sprintf(buf, "CreateWindow: error code: 0x%X", GetLastError());
+ sprintf(buf, "CreateWindow: error code: 0x%X",
+ (unsigned)GetLastError());
de_error(buf);
return(0);
}
@@ -179,6 +181,7 @@ void update_cursor(void);
INT_PTR CALLBACK AboutBoxCallback( HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam )
{
+ (void)lParam;
switch( message )
{
case WM_INITDIALOG:
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
index 2255905d..14aa1d36 100644
--- a/darwin_stop_world.c
+++ b/darwin_stop_world.c
@@ -53,20 +53,23 @@ GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start)
{
StackFrame *frame;
- if (stack_start == 0) {
# ifdef POWERPC
-# if CPP_WORDSZ == 32
- __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame));
-# else
- __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame));
-# endif
-# endif
- } else {
+ if (stack_start == 0) {
+# if CPP_WORDSZ == 32
+ __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame));
+# else
+ __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame));
+# endif
+ } else
+# else
+ GC_ASSERT(stack_start != 0); /* not implemented */
+# endif /* !POWERPC */
+ /* else */ {
frame = (StackFrame *)stack_start;
}
-# ifdef DEBUG_THREADS
- /* GC_printf("FindTopOfStack start at sp = %p\n", frame); */
+# ifdef DEBUG_THREADS_EXTRA
+ GC_log_printf("FindTopOfStack start at sp = %p\n", frame);
# endif
while (frame->savedSP != 0) {
/* if there are no more stack frames, stop */
@@ -76,11 +79,11 @@ GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start)
/* we do these next two checks after going to the next frame
because the LR for the first stack frame in the loop
is not set up on purpose, so we shouldn't check it. */
- if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3)
+ if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3U)
break; /* if the next LR is bogus, stop */
}
-# ifdef DEBUG_THREADS
- /* GC_printf("FindTopOfStack finish at sp = %p\n", frame); */
+# ifdef DEBUG_THREADS_EXTRA
+ GC_log_printf("FindTopOfStack finish at sp = %p\n", frame);
# endif
return (ptr_t)frame;
}
@@ -98,7 +101,7 @@ GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start)
#endif /* !GC_NO_THREADS_DISCOVERY */
/* Use implicit threads registration (all task threads excluding the GC */
-/* special ones are stoped and scanned). Should be called before */
+/* special ones are stopped and scanned). Should be called before */
/* GC_INIT() (or, at least, before going multi-threaded). Deprecated. */
GC_API void GC_CALL GC_use_threads_discovery(void)
{
@@ -287,7 +290,7 @@ GC_INNER void GC_push_all_stacks(void)
for (i = 0; i < (int)listcount; i++) {
thread_act_t thread = act_list[i];
lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread);
- GC_ASSERT(lo <= hi);
+ GC_ASSERT((word)lo <= (word)hi);
total_size += hi - lo;
GC_push_all_stack(lo, hi);
nthreads++;
@@ -308,7 +311,7 @@ GC_INNER void GC_push_all_stacks(void)
thread_act_t thread = (thread_act_t)p->stop_info.mach_thread;
lo = GC_stack_range_for(&hi, thread, p, (GC_bool)p->thread_blocked,
my_thread);
- GC_ASSERT(lo <= hi);
+ GC_ASSERT((word)lo <= (word)hi);
total_size += hi - lo;
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
nthreads++;
@@ -319,8 +322,7 @@ GC_INNER void GC_push_all_stacks(void)
}
mach_port_deallocate(my_task, my_thread);
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Pushed %d thread stacks\n", nthreads);
+ GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", nthreads);
if (!found_me && !GC_in_thread_creation)
ABORT("Collecting from unknown thread");
GC_total_stacksize = total_size;
diff --git a/dbg_mlc.c b/dbg_mlc.c
index f061236d..a0423e97 100644
--- a/dbg_mlc.c
+++ b/dbg_mlc.c
@@ -28,7 +28,7 @@
/* of the heap. */
/* This excludes the check as to whether the back pointer is */
/* odd, which is added by the GC_HAS_DEBUG_INFO macro. */
- /* Note that if DBG_HDRS_ALL is set, uncollectable objects */
+ /* Note that if DBG_HDRS_ALL is set, uncollectible objects */
/* on free lists may not have debug information set. Thus it's */
/* not always safe to return TRUE (1), even if the client does */
/* its part. Return -1 if the object with debug info has been */
@@ -115,10 +115,10 @@
ptr_t target = *(ptr_t *)bp;
ptr_t alternate_target = *(ptr_t *)alternate_ptr;
- if (alternate_target >= GC_least_plausible_heap_addr
- && alternate_target <= GC_greatest_plausible_heap_addr
- && (target < GC_least_plausible_heap_addr
- || target > GC_greatest_plausible_heap_addr)) {
+ if ((word)alternate_target >= (word)GC_least_plausible_heap_addr
+ && (word)alternate_target <= (word)GC_greatest_plausible_heap_addr
+ && ((word)target < (word)GC_least_plausible_heap_addr
+ || (word)target > (word)GC_greatest_plausible_heap_addr)) {
bp = alternate_ptr;
}
}
@@ -189,7 +189,7 @@
void *base;
GC_print_heap_obj(GC_base(current));
- GC_err_printf("\n");
+
for (i = 0; ; ++i) {
source = GC_get_back_ptr_info(current, &base, &offset);
if (GC_UNREFERENCED == source) {
@@ -212,10 +212,9 @@
GC_err_printf("list of finalizable objects\n\n");
goto out;
case GC_REFD_FROM_HEAP:
- GC_err_printf("offset %ld in object:\n", (unsigned long)offset);
+ GC_err_printf("offset %ld in object:\n", (long)offset);
/* Take GC_base(base) to get real base, i.e. header. */
GC_print_heap_obj(GC_base(base));
- GC_err_printf("\n");
break;
default:
GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n");
@@ -226,8 +225,8 @@
out:;
}
- /* Force a garbage collection and generate a backtrace from a */
- /* random heap address. */
+ /* Force a garbage collection and generate/print a backtrace */
+ /* from a random heap address. */
GC_INNER void GC_generate_random_backtrace_no_gc(void)
{
void * current;
@@ -249,12 +248,12 @@
#endif /* KEEP_BACK_PTRS */
# define CROSSES_HBLK(p, sz) \
- (((word)(p + sizeof(oh) + sz - 1) ^ (word)p) >= HBLKSIZE)
+ (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE)
/* Store debugging info into p. Return displaced pointer. */
/* This version assumes we do hold the allocation lock. */
-STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz, const char *string,
- int linenum)
+STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz GC_ATTR_UNUSED,
+ const char *string, int linenum)
{
word * result = (word *)((oh *)p + 1);
@@ -322,69 +321,83 @@ GC_API void GC_CALL GC_register_describe_type_fn(int kind,
GC_describe_type_fns[kind] = fn;
}
-/* Print a type description for the object whose client-visible address */
-/* is p. */
-STATIC void GC_print_type(ptr_t p)
+#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
+
+#ifndef SHORT_DBG_HDRS
+# define IF_NOT_SHORTDBG_HDRS(x) x
+# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x
+#else
+# define IF_NOT_SHORTDBG_HDRS(x) /* empty */
+# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */
+#endif
+
+/* Print a human-readable description of the object to stderr. */
+/* p points to somewhere inside an object with the debugging info. */
+STATIC void GC_print_obj(ptr_t p)
{
- hdr * hhdr = GC_find_header(p);
+ oh * ohdr = (oh *)GC_base(p);
+ ptr_t q;
+ hdr * hhdr;
+ int kind;
+ char *kind_str;
char buffer[GC_TYPE_DESCR_LEN + 1];
- int kind = hhdr -> hb_obj_kind;
- if (0 != GC_describe_type_fns[kind] && GC_is_marked(GC_base(p))) {
+ GC_ASSERT(I_DONT_HOLD_LOCK());
+# ifdef LINT2
+ if (!ohdr) ABORT("Invalid GC_print_obj argument");
+# endif
+
+ q = (ptr_t)(ohdr + 1);
+ /* Print a type description for the object whose client-visible */
+ /* address is q. */
+ hhdr = GC_find_header(q);
+ kind = hhdr -> hb_obj_kind;
+ if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) {
/* This should preclude free list objects except with */
/* thread-local allocation. */
buffer[GC_TYPE_DESCR_LEN] = 0;
- (GC_describe_type_fns[kind])(p, buffer);
+ (GC_describe_type_fns[kind])(q, buffer);
GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0);
- GC_err_puts(buffer);
+ kind_str = buffer;
} else {
switch(kind) {
case PTRFREE:
- GC_err_puts("PTRFREE");
+ kind_str = "PTRFREE";
break;
case NORMAL:
- GC_err_puts("NORMAL");
+ kind_str = "NORMAL";
break;
case UNCOLLECTABLE:
- GC_err_puts("UNCOLLECTABLE");
+ kind_str = "UNCOLLECTABLE";
break;
# ifdef ATOMIC_UNCOLLECTABLE
case AUNCOLLECTABLE:
- GC_err_puts("ATOMIC UNCOLLECTABLE");
+ kind_str = "ATOMIC_UNCOLLECTABLE";
break;
# endif
case STUBBORN:
- GC_err_puts("STUBBORN");
+ kind_str = "STUBBORN";
break;
default:
- GC_err_printf("kind=%d descr=0x%lx", kind,
- (unsigned long)(hhdr -> hb_descr));
+ kind_str = NULL;
+ /* The alternative is to use snprintf(buffer) but it is */
+ /* not quite portable (see vsnprintf in misc.c). */
}
}
-}
-
-#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int)
-
-/* Print a human-readable description of the object to stderr. p points */
-/* to somewhere inside an object with the debugging info. */
-STATIC void GC_print_obj(ptr_t p)
-{
- oh * ohdr = (oh *)GC_base(p);
- GC_ASSERT(I_DONT_HOLD_LOCK());
-# ifdef LINT2
- if (!ohdr) ABORT("Invalid GC_print_obj argument");
-# endif
- GC_err_printf("%p (", ((ptr_t)ohdr + sizeof(oh)));
- GC_err_puts(ohdr -> oh_string);
-# ifdef SHORT_DBG_HDRS
- GC_err_printf(":%d, ", GET_OH_LINENUM(ohdr));
-# else
- GC_err_printf(":%d, sz=%lu, ",
- GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz));
-# endif
- GC_print_type((ptr_t)(ohdr + 1));
- GC_err_puts(")\n");
+ if (NULL != kind_str) {
+ GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") " %s)\n",
+ (ptr_t)ohdr + sizeof(oh),
+ ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
+ COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
+ kind_str);
+ } else {
+ GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")
+ " kind=%d descr=0x%lx)\n", (ptr_t)ohdr + sizeof(oh),
+ ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */
+ COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz),
+ kind, (unsigned long)hhdr->hb_descr);
+ }
PRINT_CALL_CHAIN(ohdr);
}
@@ -411,7 +424,7 @@ STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
# ifdef LINT2
if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument");
# endif
- if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz))
+ if ((word)clobbered_addr <= (word)(&ohdr->oh_sz)
|| ohdr -> oh_string == 0) {
GC_err_printf(
"%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n",
@@ -436,8 +449,9 @@ STATIC void GC_debug_print_heap_obj_proc(ptr_t p)
STATIC void GC_do_nothing(void) {}
#endif
-GC_INNER void GC_start_debugging(void)
+STATIC void GC_start_debugging_inner(void)
{
+ GC_ASSERT(I_HOLD_LOCK());
# ifndef SHORT_DBG_HDRS
GC_check_heap = GC_check_heap_proc;
GC_print_all_smashed = GC_print_all_smashed_proc;
@@ -447,30 +461,67 @@ GC_INNER void GC_start_debugging(void)
# endif
GC_print_heap_obj = GC_debug_print_heap_obj_proc;
GC_debugging_started = TRUE;
- GC_register_displacement((word)sizeof(oh));
+ GC_register_displacement_inner((word)sizeof(oh));
+}
+
+GC_INNER void GC_start_debugging(void)
+{
+ DCL_LOCK_STATE;
+
+ LOCK();
+ GC_start_debugging_inner();
+ UNLOCK();
}
size_t GC_debug_header_size = sizeof(oh);
GC_API void GC_CALL GC_debug_register_displacement(size_t offset)
{
- GC_register_displacement(offset);
- GC_register_displacement((word)sizeof(oh) + offset);
+ DCL_LOCK_STATE;
+
+ LOCK();
+ GC_register_displacement_inner(offset);
+ GC_register_displacement_inner((word)sizeof(oh) + offset);
+ UNLOCK();
}
+#ifdef GC_ADD_CALLER
+# if defined(HAVE_DLADDR) && defined(GC_RETURN_ADDR_PARENT)
+# include <dlfcn.h>
+
+ STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp)
+ {
+ Dl_info caller;
+
+ if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) {
+ *symp = caller.dli_sname;
+ *offp = (int)((char *)ad - (char *)caller.dli_saddr);
+ }
+ if (NULL == *symp) {
+ *symp = "unknown";
+ }
+ }
+# else
+# define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown")
+# endif
+#endif /* GC_ADD_CALLER */
+
GC_API void * GC_CALL GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS)
{
void * result;
+
/* Note that according to malloc() specification, if size is 0 then */
/* malloc() returns either NULL, or a unique pointer value that can */
/* later be successfully passed to free(). We always do the latter. */
result = GC_malloc(lb + DEBUG_BYTES);
-
+# ifdef GC_ADD_CALLER
+ if (s == NULL) {
+ GC_caller_func_offset(ra, &s, &i);
+ }
+# endif
if (result == 0) {
- GC_err_printf("GC_debug_malloc(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%ld)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc(%lu) returning NULL (%s:%d)\n",
+ (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -486,10 +537,8 @@ GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(size_t lb,
void * result = GC_malloc_ignore_off_page(lb + DEBUG_BYTES);
if (result == 0) {
- GC_err_printf("GC_debug_malloc_ignore_off_page(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc_ignore_off_page(%lu)"
+ " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -506,9 +555,7 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
if (result == 0) {
GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)"
- " returning NULL (", (unsigned long)lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -533,6 +580,9 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
(unsigned long) lb);
return(0);
}
+ if (!GC_debugging_started) {
+ GC_start_debugging_inner();
+ }
ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
}
@@ -548,6 +598,9 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
(unsigned long) lb);
return(0);
}
+ if (!GC_debugging_started) {
+ GC_start_debugging_inner();
+ }
ADD_CALL_CHAIN(result, GC_RETURN_ADDR);
return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0));
}
@@ -559,10 +612,8 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
void * result = GC_malloc_stubborn(lb + DEBUG_BYTES);
if (result == 0) {
- GC_err_printf("GC_debug_malloc(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc_stubborn(%lu)"
+ " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -574,34 +625,31 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb,
GC_API void GC_CALL GC_debug_change_stubborn(const void *p)
{
- const void * q = GC_base((void *)p);
+ const void * q = GC_base_C(p);
hdr * hhdr;
if (q == 0) {
- GC_err_printf("Bad argument: %p to GC_debug_change_stubborn\n", p);
- ABORT("GC_debug_change_stubborn: bad arg");
+ ABORT_ARG1("GC_debug_change_stubborn: bad arg", ": %p", p);
}
hhdr = HDR(q);
if (hhdr -> hb_obj_kind != STUBBORN) {
- GC_err_printf("GC_debug_change_stubborn arg not stubborn: %p\n", p);
- ABORT("GC_debug_change_stubborn: arg not stubborn");
+ ABORT_ARG1("GC_debug_change_stubborn: arg not stubborn", ": %p", p);
}
GC_change_stubborn(q);
}
GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p)
{
- const void * q = GC_base((void *)p);
+ const void * q = GC_base_C(p);
hdr * hhdr;
if (q == 0) {
- GC_err_printf("Bad argument: %p to GC_debug_end_stubborn_change\n", p);
- ABORT("GC_debug_end_stubborn_change: bad arg");
+ ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p);
}
hhdr = HDR(q);
if (hhdr -> hb_obj_kind != STUBBORN) {
- GC_err_printf("debug_end_stubborn_change arg not stubborn: %p\n", p);
- ABORT("GC_debug_end_stubborn_change: arg not stubborn");
+ ABORT_ARG1("GC_debug_end_stubborn_change: arg not stubborn",
+ ": %p", p);
}
GC_end_stubborn_change(q);
}
@@ -625,10 +673,8 @@ GC_API void * GC_CALL GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS)
void * result = GC_malloc_atomic(lb + DEBUG_BYTES);
if (result == 0) {
- GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc_atomic(%lu) returning NULL (%s:%d)\n",
+ (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -656,12 +702,7 @@ GC_API char * GC_CALL GC_debug_strdup(const char *str, GC_EXTRA_PARAMS)
# endif
return NULL;
}
-# ifndef MSWINCE
- strcpy(copy, str);
-# else
- /* strcpy() is deprecated in WinCE */
- memcpy(copy, str, lb);
-# endif
+ BCOPY(str, copy, lb);
return copy;
}
@@ -708,10 +749,8 @@ GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
void * result = GC_malloc_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
if (result == 0) {
- GC_err_printf("GC_debug_malloc_uncollectable(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc_uncollectable(%lu)"
+ " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -729,11 +768,8 @@ GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb,
GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES);
if (result == 0) {
- GC_err_printf(
- "GC_debug_malloc_atomic_uncollectable(%lu) returning NULL (",
- (unsigned long) lb);
- GC_err_puts(s);
- GC_err_printf(":%lu)\n", (unsigned long)i);
+ GC_err_printf("GC_debug_malloc_atomic_uncollectable(%lu)"
+ " returning NULL (%s:%d)\n", (unsigned long)lb, s, i);
return(0);
}
if (!GC_debugging_started) {
@@ -759,8 +795,7 @@ GC_API void GC_CALL GC_debug_free(void * p)
base = GC_base(p);
if (base == 0) {
- GC_err_printf("Attempt to free invalid pointer %p\n", p);
- ABORT("Invalid pointer passed to free()");
+ ABORT_ARG1("Invalid pointer passed to free()", ": %p", p);
}
if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
GC_err_printf(
@@ -832,13 +867,18 @@ GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
void * base;
void * result;
hdr * hhdr;
- if (p == 0)
- return(GC_debug_malloc(lb, OPT_RA s, i));
+ if (p == 0) {
+ return GC_debug_malloc(lb, OPT_RA s, i);
+ }
+# ifdef GC_ADD_CALLER
+ if (s == NULL) {
+ GC_caller_func_offset(ra, &s, &i);
+ }
+# endif
base = GC_base(p);
if (base == 0) {
- GC_err_printf("Attempt to reallocate invalid pointer %p\n", p);
- ABORT("Invalid pointer passed to realloc()");
+ ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p);
}
if ((ptr_t)p - (ptr_t)base != sizeof(oh)) {
GC_err_printf(
@@ -868,8 +908,7 @@ GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS)
# endif
default:
result = NULL; /* initialized to prevent warning. */
- GC_err_printf("GC_debug_realloc: encountered bad kind\n");
- ABORT_RET("Bad kind");
+ ABORT_RET("GC_debug_realloc: encountered bad kind");
}
if (result != NULL) {
@@ -915,14 +954,18 @@ STATIC void GC_print_all_smashed_proc(void)
GC_ASSERT(I_DONT_HOLD_LOCK());
if (GC_n_smashed == 0) return;
- GC_err_printf("GC_check_heap_block: found smashed heap objects:\n");
+ GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n",
+ GC_n_smashed);
for (i = 0; i < GC_n_smashed; ++i) {
- GC_print_smashed_obj("", (ptr_t)GC_base(GC_smashed[i]) + sizeof(oh),
- GC_smashed[i]);
+ ptr_t base = (ptr_t)GC_base(GC_smashed[i]);
+
+# ifdef LINT2
+ if (!base) ABORT("Invalid GC_smashed element");
+# endif
+ GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]);
GC_smashed[i] = 0;
}
GC_n_smashed = 0;
- GC_err_printf("\n");
}
/* Check all marked objects in the given block for validity */
@@ -941,7 +984,8 @@ STATIC void GC_check_heap_block(struct hblk *hbp, word dummy GC_ATTR_UNUSED)
plim = hbp->hb_body + HBLKSIZE - sz;
}
/* go through all words in block */
- for (bit_no = 0; p <= plim; bit_no += MARK_BIT_OFFSET(sz), p += sz) {
+ for (bit_no = 0; (word)p <= (word)plim;
+ bit_no += MARK_BIT_OFFSET(sz), p += sz) {
if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) {
ptr_t clobbered = GC_check_annotated_obj((oh *)p);
if (clobbered != 0)
@@ -987,6 +1031,8 @@ GC_INNER GC_bool GC_check_leaked(ptr_t base)
#endif /* !SHORT_DBG_HDRS */
+#ifndef GC_NO_FINALIZATION
+
struct closure {
GC_finalization_proc cl_fn;
void * cl_data;
@@ -1058,9 +1104,8 @@ GC_API void GC_CALL GC_debug_register_finalizer(void * obj,
return;
}
if ((ptr_t)obj - base != sizeof(oh)) {
- GC_err_printf(
- "GC_debug_register_finalizer called with non-base-pointer %p\n",
- obj);
+ GC_err_printf("GC_debug_register_finalizer called with"
+ " non-base-pointer %p\n", obj);
}
if (0 == fn) {
GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd);
@@ -1088,10 +1133,8 @@ GC_API void GC_CALL GC_debug_register_finalizer_no_order
return;
}
if ((ptr_t)obj - base != sizeof(oh)) {
- GC_err_printf(
- "GC_debug_register_finalizer_no_order called with "
- "non-base-pointer %p\n",
- obj);
+ GC_err_printf("GC_debug_register_finalizer_no_order called with"
+ " non-base-pointer %p\n", obj);
}
if (0 == fn) {
GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd);
@@ -1119,10 +1162,8 @@ GC_API void GC_CALL GC_debug_register_finalizer_unreachable
return;
}
if ((ptr_t)obj - base != sizeof(oh)) {
- GC_err_printf(
- "GC_debug_register_finalizer_unreachable called with "
- "non-base-pointer %p\n",
- obj);
+ GC_err_printf("GC_debug_register_finalizer_unreachable called with"
+ " non-base-pointer %p\n", obj);
}
if (0 == fn) {
GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd);
@@ -1150,9 +1191,8 @@ GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
return;
}
if ((ptr_t)obj - base != sizeof(oh)) {
- GC_err_printf(
- "GC_debug_register_finalizer_ignore_self called with "
- "non-base-pointer %p\n", obj);
+ GC_err_printf("GC_debug_register_finalizer_ignore_self called with"
+ " non-base-pointer %p\n", obj);
}
if (0 == fn) {
GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd);
@@ -1165,12 +1205,14 @@ GC_API void GC_CALL GC_debug_register_finalizer_ignore_self
store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd);
}
+#endif /* !GC_NO_FINALIZATION */
+
GC_API void * GC_CALL GC_debug_malloc_replacement(size_t lb)
{
- return GC_debug_malloc(lb, GC_DBG_RA "unknown", 0);
+ return GC_debug_malloc(lb, GC_DBG_EXTRAS);
}
GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb)
{
- return GC_debug_realloc(p, lb, GC_DBG_RA "unknown", 0);
+ return GC_debug_realloc(p, lb, GC_DBG_EXTRAS);
}
diff --git a/digimars.mak b/digimars.mak
index 383df5c4..7c75eb42 100644
--- a/digimars.mak
+++ b/digimars.mak
@@ -3,7 +3,7 @@
# Written by Walter Bright
-DEFINES=-DNDEBUG -DGC_BUILD -D_WINDOWS -DGC_DLL -DALL_INTERIOR_POINTERS -DWIN32_THREADS
+DEFINES=-DNDEBUG -D_WINDOWS -DGC_DLL -DALL_INTERIOR_POINTERS -DWIN32_THREADS
CFLAGS=-Iinclude $(DEFINES) -wx -g
LFLAGS=/ma/implib/co
CC=sc
@@ -62,7 +62,7 @@ gctest.exe : gc.lib tests\test.obj
sc -ogctest.exe tests\test.obj gc.lib
tests\test.obj : tests\test.c
- $(CC) -c -g -DNDEBUG -DGC_BUILD -D_WINDOWS -DGC_DLL \
+ $(CC) -c -g -DNDEBUG -D_WINDOWS -DGC_DLL \
-DALL_INTERIOR_POINTERS -DWIN32_THREADS \
-Iinclude tests\test.c -otests\test.obj
diff --git a/doc/README.Mac b/doc/README.Mac
index 572e689d..55906697 100644
--- a/doc/README.Mac
+++ b/doc/README.Mac
@@ -12,7 +12,7 @@ are distributed in the file Mac_projects.sit.hqx. The project file
:Mac_projects:gctest.prj builds the GC test suite.
Configuring the collector is still done by editing the file
-:Mac_files:MacOS_config.h.
+:extra:Mac_files:MacOS_config.h.
Lars Farm's suggestions on building the collector:
----------------------------------------------------------------------------
@@ -92,7 +92,7 @@ o C/C++ language
o PPC Processor
- Struct Alignment: PowerPC
- uncheck "Store Static Data in TOC" -- important!
- I don't think the others matter, I use full optimization and its ok
+ I don't think the others matter, I use full optimization and it is OK
o PPC Linker
- Factory Settings (SYM file with full paths, faster linking, dead-strip
static init, Main: __start)
@@ -120,7 +120,7 @@ o IR Optimizer
- enable: Optimize Space, Optimize Speed
I suppose the others would work too, but haven't tried...
o 68K Linker
- - Factory Settings (New Style MacsBug,SYM file with full paths,
+ - Factory Settings (New Style MacsBug, SYM file with full paths,
A6 Frames, fast link, Merge compiler glue into segment 1,
dead-strip static init)
@@ -261,7 +261,7 @@ same as for test.c
For convenience I used one test-project with several targets so that all
test apps are build at once. Two for each library to test: test.c and
-gc_app.cc. When I was satisfied that the libraries were ok. I put the
+gc_app.cc. When I was satisfied that the libraries were OK. I put the
libraries + gc.h + the c++ interface-file in a folder that I then put into
the MSL hierarchy so that I don't have to alter access-paths in projects
that use the GC.
diff --git a/doc/README.amiga b/doc/README.amiga
index ea2a2f68..bf18d3f6 100644
--- a/doc/README.amiga
+++ b/doc/README.amiga
@@ -1,8 +1,8 @@
Kjetil S. Matheussen's notes (28-11-2000)
-Compiles under SAS/C again. Should allso still compile under other
-amiga compilers without big changes. I haven't checked if it still
-works under gcc, because I don't have gcc for amiga. But I have
+Compiles under SAS/C again. Should also still compile under other
+Amiga compilers without big changes. I haven't checked if it still
+works under gcc, because I don't have gcc for Amiga. But I have
updated 'Makefile', and hope it compiles fine.
@@ -15,7 +15,7 @@ WHATS NEW:
The lower part of the new file AmigaOS.c does this in various ways, mainly by
wrapping GC_malloc, GC_malloc_atomic, GC_malloc_uncollectable,
GC_malloc_atomic_uncollectable, GC_malloc_stubborn, GC_malloc_ignore_off_page
- and GC_malloc_atomic_ignore_off_page. GC_realloc is allso wrapped, but
+ and GC_malloc_atomic_ignore_off_page. GC_realloc is also wrapped, but
doesn't do the same effort in preventing to return chip-mem.
Other allocating-functions (f.ex. GC_*_typed_) can probably be
used without any problems, but beware that the warn hook will not be called.
@@ -33,7 +33,7 @@ WHATS NEW:
If you absolutely must call abort() instead of exit(), try calling
the GC_amiga_free_all_mem function before abort().
- New amiga-spesific compilation flags:
+ New Amiga-specific compilation flags:
GC_AMIGA_FASTALLOC - By NOT defining this option, GC will work like before,
it will not try to force fast-mem out of the OS, and
@@ -45,7 +45,7 @@ WHATS NEW:
GC_AMIGA_GC - If gc returns NULL, do a GC_gcollect, and try again. This
usually is a success with the standard GC configuration.
- It is allso the most important flag to set to prevent
+ It is also the most important flag to set to prevent
GC from returning chip-mem. Beware that it slows down a lot
when a program is rapidly allocating/deallocating when
theres either very little fast-memory left or verly little
@@ -66,10 +66,10 @@ WHATS NEW:
GC_AMIGA_ONLYFAST.
If your program demands high response-time, you should
- not define GC_AMIGA_GC, and possible allso define GC_AMIGA_ONLYFAST.
+ not define GC_AMIGA_GC, and possible also define GC_AMIGA_ONLYFAST.
GC_AMIGA_RETRY does not seem to slow down much.
- Allso, when compiling up programs, and GC_AMIGA_FASTALLOC was not defined when
+ Also, when compiling up programs, and GC_AMIGA_FASTALLOC was not defined when
compilling gc, you can define GC_AMIGA_MAKINGLIB to avoid having these allocation-
functions wrapped. (see gc.h)
@@ -81,20 +81,20 @@ WHATS NEW:
GC_AMIGA_FASTALLOC by letting the function go thru the new
GC_amiga_allocwrapper_do function-pointer (see gc.h). Means that
sending function-pointers, such as GC_malloc, GC_malloc_atomic, etc.,
- for later to be called like f.ex this, (*GC_malloc_functionpointer)(size),
+ for later to be called like f.ex this, (*GC_malloc_function_pointer)(size),
will not wrap the function. This is normally not a big problem, unless
all allocation function is called like this, which will cause the
atexit un-allocating function never to be called. Then you either
have to manually add the atexit handler, or call the allocation-
functions function-pointer functions like this;
- (*GC_amiga_allocwrapper_do)(size,GC_malloc_functionpointer).
+ (*GC_amiga_allocwrapper_do)(size,GC_malloc_function_pointer).
There are probably better ways this problem could be handled, unfortunately,
I didn't find any without rewriting or replacing a lot of the GC-code, which
I really didn't want to. (Making new GC_malloc_* functions, and just
- define f.ex GC_malloc as GC_amiga_malloc should allso work).
+ define f.ex GC_malloc as GC_amiga_malloc should work too).
- New amiga-spesific function:
+ New Amiga-specific function:
void GC_amiga_set_toany(void (*func)(void));
@@ -103,10 +103,10 @@ WHATS NEW:
it will return chip-mem.
-2. A few small compiler-spesific additions to make it compile with SAS/C again.
+2. A few small compiler-specific additions to make it compile with SAS/C again.
3. Updated and rewritten the smakefile, so that it works again and that
- the "unnecesarry" 'SCOPTIONS' files could be removed. Allso included
+ the "unnecessary" 'SCOPTIONS' files could be removed. Also included
the cord-smakefile stuff in the main smakefile, so that the cord smakefile
could be removed too. By writing smake -f Smakefile.smk, both gc.lib and
cord.lib will be made.
@@ -119,14 +119,14 @@ Programs can not be started from workbench, at least not for SAS/C. (Martin
Tauchmanns note about that it now works with workbench is definitely wrong
when concerning SAS/C). I guess it works if you use the old "#if 0'ed"-code,
but I haven't tested it. I think the reason for MT to replace the
-"#if 0'ed"-code was only because it was a bit to SAS/C-spesific. But I
+"#if 0'ed"-code was only because it was a bit to SAS/C-specific. But I
don't know. An iconx-script solves this problem anyway.
BEWARE!
-To run gctest, set the stack to around 200000 bytes first.
--SAS/C-spesific: cord will crash if you compile gc.lib with
+-SAS/C-specific: cord will crash if you compile gc.lib with
either parm=reg or parm=both. (missing legal prototypes for
function-pointers someplace is the reason I guess.).
diff --git a/doc/README.cords b/doc/README.cords
index 5730a7f8..0339a9a9 100644
--- a/doc/README.cords
+++ b/doc/README.cords
@@ -23,13 +23,13 @@ Boehm, Atkinson, and Plass, "Ropes: An Alternative to Strings",
Software Practice and Experience 25, 12, December 1995, pp. 1315-1330.
A fundamentally similar "rope" data structure is also part of SGI's standard
-template library implementation, and its descendents, which include the
+template library implementation, and its descendants, which include the
GNU C++ library. That uses reference counting by default.
There is a short description of that data structure at
http://reality.sgi.com/boehm/ropeimpl.html . (The more official location
http://www.sgi.com/tech/stl/ropeimpl.html is missing a figure.)
-All of these are descendents of the "ropes" in Xerox Cedar.
+All of these are descendants of the "ropes" in Xerox Cedar.
cord/tests/de.c is a very dumb text editor that illustrates the use of cords.
It maintains a list of file versions. Each version is simply a
@@ -38,7 +38,7 @@ editing operations are efficient, even on very large files.
(Its 3 line "user manual" can be obtained by invoking it without
arguments. Note that ^R^N and ^R^P move the cursor by
almost a screen. It does not understand tabs, which will show
-up as highlighred "I"s. Use the UNIX "expand" program first.)
+up as highlighted "I"s. Use the UNIX "expand" program first.)
To build the editor, type "make cord/de" in the gc directory.
This package assumes an ANSI C compiler such as gcc. It will
diff --git a/doc/README.environment b/doc/README.environment
index 866e9055..79752f78 100644
--- a/doc/README.environment
+++ b/doc/README.environment
@@ -34,6 +34,10 @@ GC_DUMP_REGULARLY - Generate a GC debugging dump GC_dump() on startup
if you have a bug to report, but please include only the
last complete dump.
+GC_COLLECT_AT_MALLOC=<n> - Override the default value specified by
+ GC_COLLECT_AT_MALLOC macro. Has no effect unless
+ GC is built with GC_COLLECT_AT_MALLOC defined.
+
GC_BACKTRACES=<n> - Generate n random back-traces (for heap profiling) after
each GC. Collector must have been built with
KEEP_BACK_PTRS. This won't generate useful output unless
diff --git a/doc/README.hp b/doc/README.hp
index caa8bdd1..d9fe7c8b 100644
--- a/doc/README.hp
+++ b/doc/README.hp
@@ -1,6 +1,6 @@
Dynamic loading support requires that executables be linked with -ldld.
The alternative is to build the collector without defining DYNAMIC_LOADING
-in gcconfig.h and ensuring that all garbage collectable objects are
+in gcconfig.h and ensuring that all garbage collectible objects are
accessible without considering statically allocated variables in dynamic
libraries.
diff --git a/doc/README.linux b/doc/README.linux
index dc0fa543..f490365c 100644
--- a/doc/README.linux
+++ b/doc/README.linux
@@ -1,7 +1,7 @@
See README.alpha for Linux on DEC AXP info.
-This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K, IA64,
-SPARC, MIPS, Alpha and PowerPC are also integrated. They should behave
+This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K,
+IA64, SPARC, MIPS, Alpha and PowerPC are integrated too. They should behave
similarly, except that the PowerPC port lacks incremental GC support, and
it is unknown to what extent the Linux threads code is functional.
See below for M68K specific notes.
@@ -60,8 +60,8 @@ To use threads, you need to abide by the following requirements:
6) Thread local storage may not be viewed as part of the root set by the
collector. This probably depends on the linuxthreads version. For the
- time being, any collectable memory referenced by thread local storage should
- also be referenced from elsewhere, or be allocated as uncollectable.
+ time being, any collectible memory referenced by thread local storage
+ should also be referenced from elsewhere, or be allocated as uncollectible.
(This is really a bug that should be fixed somehow. The current GC
version probably gets things right if there are not too many tls locations
and if dlopen is not used.)
diff --git a/doc/README.macros b/doc/README.macros
index 09a05fca..a822f0f4 100644
--- a/doc/README.macros
+++ b/doc/README.macros
@@ -30,6 +30,9 @@ MACRO EXPLANATION
GC_DEBUG Tested by gc.h. Causes all-upper-case macros to
expand to calls to debug versions of collector routines.
+GC_NAMESPACE Tested by gc_cpp.h. Causes gc_cpp symbols to be defined
+ in "boehmgc" namespace.
+
GC_DEBUG_REPLACEMENT Tested by gc.h. Causes GC_MALLOC/REALLOC() to be
defined as GC_debug_malloc/realloc_replacement().
@@ -235,6 +238,10 @@ NO_DEBUGGING Removes GC_dump and the debugging routines it calls.
DEBUG_THREADS Turn on printing additional thread-support debugging
information.
+GC_COLLECT_AT_MALLOC=<n> Force garbage collection at every
+ GC_malloc_* call with the size greater than the specified value.
+ (Might be useful for application debugging or in find-leak mode.)
+
JAVA_FINALIZATION Makes it somewhat safer to finalize objects out of
order by specifying a nonstandard finalization mark procedure (see
finalize.c). Objects reachable from finalizable objects will be marked
@@ -248,6 +255,8 @@ FINALIZE_ON_DEMAND Causes finalizers to be run only in response
In 5.0 this became runtime adjustable, and this only determines the
initial value of GC_finalize_on_demand.
+GC_NO_FINALIZATION Exclude finalization support (for smaller code size)
+
ATOMIC_UNCOLLECTABLE Includes code for GC_malloc_atomic_uncollectable.
This is useful if either the vendor malloc implementation is poor,
or if REDIRECT_MALLOC is used.
@@ -277,6 +286,9 @@ USE_MUNMAP Causes memory to be returned to the OS under the right
Works under some Unix, Linux and Windows versions.
Requires USE_MMAP except for Windows.
+USE_WINALLOC (Cygwin only) Use Win32 VirtualAlloc (instead of sbrk or mmap)
+ to get new memory. Useful if memory unmapping (USE_MUNMAP) is enabled.
+
MUNMAP_THRESHOLD=<value> Set the desired memory blocks unmapping
threshold (the number of sequential garbage collections for which
a candidate block for unmapping should remain free).
@@ -286,9 +298,6 @@ GC_FORCE_UNMAP_ON_GCOLLECT Set "unmap as much as possible on explicit GC"
unless unmapping is turned on. Has no effect on implicitly-initiated
garbage collections.
-MMAP_STACKS (for Solaris threads) Use mmap from /dev/zero rather than
- GC_scratch_alloc() to get stack memory.
-
PRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever
the garbage collector detects a value that looks almost, but not quite,
like a pointer, print both the address containing the value, and the
@@ -433,9 +442,9 @@ STUBBORN_ALLOC Allows allocation of "hard to change" objects, and thus
makes incremental collection easier. Was enabled by default until 6.0.
Rarely used, to my knowledge.
-HANDLE_FORK Attempts to make GC_malloc() work in a child process fork()'ed
- from a multithreaded parent. Currently only supported by pthread_support.c.
- (Similar code should work on Solaris or Irix, but it hasn't been tried.)
+HANDLE_FORK (Unix and Cygwin only) Attempt by default to make GC_malloc()
+ work in a child process fork()'ed from a multi-threaded parent. Not fully
+ POSIX-compliant and could be disabled at runtime (before GC_INIT).
TEST_WITH_SYSTEM_MALLOC Causes gctest to allocate (and leak) large
chunks of memory with the standard system malloc. This will cause the root
@@ -523,6 +532,8 @@ GC_ONLY_LOG_TO_FILE Don't redirect GC stdout and stderr to the log file
specified by GC_LOG_FILE environment variable. Has effect only when the
variable is set (to anything other than "0").
+GC_ANDROID_LOG (Android only) Output error/debug information to Android log.
+
GC_DONT_EXPAND Don't expand the heap unless explicitly requested or forced to.
GC_USE_ENTIRE_HEAP Causes the non-incremental collector to use the
diff --git a/doc/README.solaris2 b/doc/README.solaris2
index d46fe262..2f3b511a 100644
--- a/doc/README.solaris2
+++ b/doc/README.solaris2
@@ -24,9 +24,15 @@ libgcc_s.so.1. Alternatively, you can configure with --disable-shared.
SOLARIS THREADS:
-The collector must be compiled with -DGC_THREADS to be thread safe.
+Threads support is enabled by configure "--enable-threads=posix" option.
+(In case of GCC compiler, multi-threading support is on by default.)
+This causes the collector to be compiled with -D GC_THREADS (or
+-D GC_SOLARIS_THREADS) ensuring thread safety.
This assumes use of the pthread_ interface. Old style Solaris threads
are no longer supported.
+Thread-local allocation is now on by default. Parallel marking is on by
+default starting from GC v7.3 but it could be enabled or disabled manually
+by the corresponding "--enable/disable-parallel-mark" options.
It is also essential that gc.h be included in files that call pthread_create,
pthread_join, pthread_detach, or dlopen. gc.h macro defines these to also do
diff --git a/doc/README.symbian b/doc/README.symbian
new file mode 100644
index 00000000..50658c31
--- /dev/null
+++ b/doc/README.symbian
@@ -0,0 +1,13 @@
+Instructions for Symbian:
+1. base version: libgc 7.1
+2. Build: use libgc.mmp
+3. Limitations
+3.1.No multi-threaded support
+
+3.2. Be careful with limitation that emulator introduces: Static roots are not
+dynamically accessible (there are Symbian APIs for this purpose but are just
+stubs, returning irrelevant values).
+Consequently, on emulator, you can only use dlls or exe, and retrieve static
+roots by calling global_init_static_root per dll (or exe).
+On target, only libs are supported, because static roots are retrieved by
+linker flags, by calling global_init_static_root in main exe.
diff --git a/doc/README.win32 b/doc/README.win32
index 3ffb3b6f..471126e2 100644
--- a/doc/README.win32
+++ b/doc/README.win32
@@ -78,9 +78,7 @@ To build the collector as a DLL, pass "--enable-shared --disable-static" to
configure (this will instruct make compile with -D GC_DLL).
Parallel marker could be enabled via "--enable-parallel-mark".
-
-Memory unmapping could be enabled via "--enable-munmap" (not well supported
-on Cygwin currently).
+Memory unmapping could be enabled via "--enable-munmap".
Borland Tools
-------------
@@ -136,7 +134,7 @@ To compile the collector and testing programs use the command:
All programs using gc should be compiled with 4-byte alignment.
For further explanations on this see comments about Borland.
-If the gc is compiled as dll, the macro ``GC_DLL'' should be defined before
+If the gc is compiled as dll, the macro "GC_DLL" should be defined before
including "gc.h" (for example, with -DGC_DLL compiler option). It's
important, otherwise resulting programs will not run.
diff --git a/doc/debugging.html b/doc/debugging.html
index cc51d7f3..158e4faa 100644
--- a/doc/debugging.html
+++ b/doc/debugging.html
@@ -38,9 +38,9 @@ platforms, this will cause the collector to loop in a handler when the
SIGSEGV is encountered (or when the collector aborts for some other reason),
and a debugger can then be attached to the looping
process. This sidesteps common operating system problems related
-to incomplete core files for multithreaded applications, etc.
+to incomplete core files for multi-threaded applications, etc.
<H2>Other Signals</h2>
-On most platforms, the multithreaded version of the collector needs one or
+On most platforms, the multi-threaded version of the collector needs one or
two other signals for internal use by the collector in stopping threads.
It is normally wise to tell the debugger to ignore these. On Linux,
the collector currently uses SIGPWR and SIGXCPU by default.
@@ -183,7 +183,7 @@ pseudo-random numbers, and the like. It is also likely to improve GC
performance, perhaps drastically so if the application is paging.
<LI> If you allocate large objects containing only
one or two pointers at the beginning, either try the typed allocation
-primitives is <TT>gc_typed.h</tt>, or separate out the pointerfree component.
+primitives is <TT>gc_typed.h</tt>, or separate out the pointer-free component.
<LI> Consider using <TT>GC_malloc_ignore_off_page()</tt>
to allocate large objects. (See <TT>gc.h</tt> and above for details.
Large means &gt; 100K in most environments.)
@@ -201,7 +201,7 @@ value in a heap object. This should, of course, be impossible. In practice,
it may happen for reasons like the following:
<OL>
<LI> The collector did not intercept the creation of threads correctly in
-a multithreaded application, <I>e.g.</i> because the client called
+a multi-threaded application, <I>e.g.</i> because the client called
<TT>pthread_create</tt> without including <TT>gc.h</tt>, which redefines it.
<LI> The last pointer to an object in the garbage collected heap was stored
somewhere were the collector couldn't see it, <I>e.g.</i> in an
diff --git a/doc/doc.am b/doc/doc.am
index 27c4691a..dcae4d73 100644
--- a/doc/doc.am
+++ b/doc/doc.am
@@ -14,7 +14,7 @@
#
dist_pkgdata_DATA = \
AUTHORS \
- README \
+ README.md \
doc/README.DGUX386 \
doc/README.Mac \
doc/README.OS2 \
@@ -33,6 +33,7 @@ dist_pkgdata_DATA = \
doc/README.rs6000 \
doc/README.sgi \
doc/README.solaris2 \
+ doc/README.symbian \
doc/README.uts \
doc/README.win32 \
doc/README.win64 \
diff --git a/doc/gcdescr.html b/doc/gcdescr.html
index 7879ef49..8a1dae06 100644
--- a/doc/gcdescr.html
+++ b/doc/gcdescr.html
@@ -87,7 +87,7 @@ Different kinds are handled somewhat differently by certain parts
of the garbage collector. Certain kinds are scanned for pointers,
others are not. Some may have per-object type descriptors that
determine pointer locations. Or a specific kind may correspond
-to one specific object layout. Two built-in kinds are uncollectable.
+to one specific object layout. Two built-in kinds are uncollectible.
One (<TT>STUBBORN</tt>) is immutable without special precautions.
In spite of that, it is very likely that most C clients of the
collector currently
@@ -250,8 +250,8 @@ a stop-the-world collection is:
objects. In this case <TT>GC_objects_are_marked</tt> will simultaneously
be false, so the mark state is advanced to
<LI> <TT>MS_PUSH_UNCOLLECTABLE</tt> indicating that it suffices to push
-uncollectable objects, roots, and then mark everything reachable from them.
-<TT>Scan_ptr</tt> is advanced through the heap until all uncollectable
+uncollectible objects, roots, and then mark everything reachable from them.
+<TT>Scan_ptr</tt> is advanced through the heap until all uncollectible
objects are pushed, and objects reachable from them are marked.
At that point, the next call to <TT>GC_mark_some</tt> calls
<TT>GC_push_roots</tt> to push the roots. It the advances the
@@ -498,8 +498,8 @@ to follow a ``pointer'' to just outside the garbage-collected heap, or
to a currently unallocated page inside the heap. Pages that have been
the targets of such near misses are likely to be the targets of
misidentified ``pointers'' in the future. To minimize the future
-damage caused by such misidentifications they will be allocated only to
-small pointerfree objects.
+damage caused by such misidentification, they will be allocated only to
+small pointer-free objects.
<P>
The collector understands two different kinds of black-listing. A
page may be black listed for interior pointer references
@@ -520,7 +520,7 @@ a block to a particular object kind and size. It occasionally
drops (i.e. allocates and forgets) blocks that are completely black-listed
in order to avoid excessively long large block free lists containing
only unusable blocks. This would otherwise become an issue
-if there is low demand for small pointerfree objects.
+if there is low demand for small pointer-free objects.
<H2>Thread support</h2>
We support several different threading models. Unfortunately Pthreads,
diff --git a/doc/gcinterface.html b/doc/gcinterface.html
index eaa038c4..31a41fcd 100644
--- a/doc/gcinterface.html
+++ b/doc/gcinterface.html
@@ -30,7 +30,7 @@ or <TT>gc.h</tt> in the distribution.
<P>
Clients should include <TT>gc.h</tt>.
<P>
-In the case of multithreaded code,
+In the case of multi-threaded code,
<TT>gc.h</tt> should be included after the threads header file, and
after defining the appropriate <TT>GC_</tt><I>XXXX</i><TT>_THREADS</tt> macro.
(For 6.2alpha4 and later, simply defining <TT>GC_THREADS</tt> should suffice.)
@@ -74,7 +74,7 @@ collector using the interface in
Identical to <TT>GC_MALLOC</tt>,
except that the resulting object is not automatically
deallocated. Unlike the system-provided malloc, the collector does
-scan the object for pointers to garbage-collectable memory, even if the
+scan the object for pointers to garbage-collectible memory, even if the
block itself does not appear to be reachable. (Objects allocated in this way
are effectively treated as roots by the collector.)
<DT> <B> void * GC_REALLOC(void *<I>old</i>, size_t <I>new_size</i>) </b>
@@ -84,12 +84,12 @@ old object into the new object. The old object is reused in place if
convenient. If the original object was allocated with
<TT>GC_MALLOC_ATOMIC</tt>,
the new object is subject to the same constraints. If it was allocated
-as an uncollectable object, then the new object is uncollectable, and
+as an uncollectible object, then the new object is uncollectible, and
the old object (if different) is deallocated.
<DT> <B> void GC_FREE(void *<I>dead</i>) </b>
<DD>
Explicitly deallocate an object. Typically not useful for small
-collectable objects.
+collectible objects.
<DT> <B> void * GC_MALLOC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
<DD>
<DT> <B> void * GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(size_t <I>nbytes</i>) </b>
@@ -188,11 +188,11 @@ are many "standard" ways to allocate memory in C++. The default ::new
operator, default malloc, and default STL allocators allocate memory
that is not garbage collected, and is not normally "traced" by the
collector. This means that any pointers in memory allocated by these
-default allocators will not be seen by the collector. Garbage-collectable
+default allocators will not be seen by the collector. Garbage-collectible
memory referenced only by pointers stored in such default-allocated
objects is likely to be reclaimed prematurely by the collector.
<P>
-It is the programmers responsibility to ensure that garbage-collectable
+It is the programmers responsibility to ensure that garbage-collectible
memory is referenced by pointers stored in one of
<UL>
<LI> Program variables
@@ -200,7 +200,7 @@ memory is referenced by pointers stored in one of
<LI> Uncollected but "traceable" objects
</ul>
"Traceable" objects are not necessarily reclaimed by the collector,
-but are scanned for pointers to collectable objects.
+but are scanned for pointers to collectible objects.
They are usually allocated by <TT>GC_MALLOC_UNCOLLECTABLE</tt>, as described
above, and through some interfaces described below.
<P>
@@ -209,8 +209,8 @@ exception objects. Thus objects thrown as exceptions should only
point to otherwise reachable memory. This is another bug whose
proper repair requires platform hooks.)
<P>
-The easiest way to ensure that collectable objects are properly referenced
-is to allocate only collectable objects. This requires that every
+The easiest way to ensure that collectible objects are properly referenced
+is to allocate only collectible objects. This requires that every
allocation go through one of the following interfaces, each one of
which replaces a standard C++ allocation mechanism. Note that
this requires that all STL containers be explicitly instantiated with
@@ -227,7 +227,7 @@ allocator implementation in <TT>gc_allocator.h</tt>. It defines
</ul>
which may be used either directly to allocate memory or to instantiate
container templates.
-The former allocates uncollectable but traced memory.
+The former allocates uncollectible but traced memory.
The latter allocates garbage-collected memory.
<P>
These should work with any fully standard-conforming C++ compiler.
@@ -244,8 +244,8 @@ This defines SGI-style allocators
<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 first two allocate uncollectible but traced
+memory, while the second two allocate collectible memory.
The <TT>single_client</tt> versions are not safe for concurrent access by
multiple threads, but are faster.
<P>
@@ -253,13 +253,13 @@ For an example, click <A HREF="http://hpl.hp.com/personal/Hans_Boehm/gc/gc_alloc
<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
+be allocated in garbage collectible memory by having those classes
inherit from class gc.
For details see <A type="text/plain" HREF="../include/gc_cpp.h">gc_cpp.h</a>.
<P>
Linking against libgccpp in addition to the gc library overrides
-::new (and friends) to allocate traceable memory but uncollectable
-memory, making it safe to refer to collectable objects from the resulting
+::new (and friends) to allocate traceable memory but uncollectible
+memory, making it safe to refer to collectible objects from the resulting
memory.
<DT> <B> C interface </b>
<DD>
diff --git a/doc/leak.html b/doc/leak.html
index 7751a131..d6e8a4c5 100644
--- a/doc/leak.html
+++ b/doc/leak.html
@@ -166,8 +166,7 @@ detection mode on a program a.out under Linux/X86 as follows:
<OL>
<LI> <I>Ensure that a.out is a single-threaded executable, or you are using
a very recent (7.0alpha7+) collector version on Linux.</i>
-On most platforms this does not
-work at all for multithreaded programs.
+On most platforms this does not work at all for the multi-threaded programs.
<LI> If possible, ensure that the <TT>addr2line</tt> program is installed in
<TT>/usr/bin</tt>. (It comes with most Linux distributions.)
<LI> If possible, compile your program, which we'll call <TT>a.out</tt>,
diff --git a/doc/overview.html b/doc/overview.html
index 03484575..6754a3c1 100644
--- a/doc/overview.html
+++ b/doc/overview.html
@@ -104,7 +104,7 @@ Tru64, Irix and a few other operating systems.
Some ports are more polished than others.
<p>
Irix pthreads, Linux threads, Win32 threads, Solaris threads
-(old style and pthreads),
+(pthreads only),
HP/UX 11 pthreads, Tru64 pthreads, and MacOS X threads are supported
in recent versions.
</p><h3>Separately distributed ports</h3>
@@ -161,7 +161,7 @@ for programs written for malloc/free
<a href="ftp://ftp.cs.colorado.edu/pub/techreports/zorn/CU-CS-665-93.ps.Z">Memory Allocation Costs in Large C and C++ Programs</a>.)
For programs allocating primarily very small objects, the collector
may be faster; for programs allocating primarily large objects it will
-be slower. If the collector is used in a multithreaded environment
+be slower. If the collector is used in a multi-threaded environment
and configured for thread-local allocation, it may in some cases
significantly outperform malloc/free allocation in time.
</p><p>
@@ -245,7 +245,7 @@ HP Labs Technical Report HPL 2000-165</a>. Discusses the parallel
collection algorithms, and presents some performance results.
</p><p>
Boehm, H., "Bounding Space Usage of Conservative Garbage Collectors",
-<i>Proceeedings of the 2002 ACM SIGPLAN-SIGACT Symposium on Principles of
+<i>Proceedings of the 2002 ACM SIGPLAN-SIGACT Symposium on Principles of
Programming Languages</i>, Jan. 2002, pp. 93-100.
<a href="http://portal.acm.org/citation.cfm?doid=503272.503282">
Official version.</a>
diff --git a/doc/porting.html b/doc/porting.html
index 5a06c228..f22f654e 100644
--- a/doc/porting.html
+++ b/doc/porting.html
@@ -101,7 +101,7 @@ architecture here is compiler- and OS-dependent.
<DD>
The beginning of the main data segment. The collector will trace all
memory between <TT>DATASTART</tt> and <TT>DATAEND</tt> for root pointers.
-On some platforms,this can be defined to a constant address,
+On some platforms, this can be defined to a constant address,
though experience has shown that to be risky. Ideally the linker will
define a symbol (e.g. <TT>_data</tt> whose address is the beginning
of the data segment. Sometimes the value can be computed using
@@ -212,7 +212,7 @@ from are copied to the stack. Typically this can be done portably,
but on some platforms it may require assembly code, or just
tweaking of conditional compilation tests.
<P>
-For GC7, if your platform supports <TT>getcontext()</tt>, then definining
+For GC7, if your platform supports <TT>getcontext()</tt>, then defining
the macro <TT>UNIX_LIKE</tt> for your OS in <TT>gcconfig.h</tt>
(if it isn't defined there already) is likely to solve the problem.
otherwise, if you are using gcc, <TT>_builtin_unwind_init()</tt>
@@ -272,7 +272,7 @@ perform adequately as the allocation lock, you will probably need to
ensure that a sufficient <TT>atomic_ops</tt> port
exists for the platform to provided an atomic test and set
operation. (Current GC7 versions require more<TT>atomic_ops</tt>
-asupport than necessary. This is a bug.) For earlier versions define
+support than necessary. This is a bug.) For earlier versions define
<TT>GC_test_and_set</tt> in <TT>gc_locks.h</tt>.
<LI>Making any needed adjustments to <TT>pthread_stop_world.c</tt> and
<TT>pthread_support.c</tt>. Ideally none should be needed. In fact,
diff --git a/doc/simple_example.html b/doc/simple_example.html
index 95e49a6b..98ae4244 100644
--- a/doc/simple_example.html
+++ b/doc/simple_example.html
@@ -133,7 +133,7 @@ This can never hurt, and is thus generally good practice.
<H3><FONT COLOR=green>Threads</font></h3>
<FONT COLOR=green>
-For a multithreaded program some more rules apply:
+For a multi-threaded program, some more rules apply:
</font>
<UL>
<LI>
diff --git a/dyn_load.c b/dyn_load.c
index 31ec0537..215967e9 100644
--- a/dyn_load.c
+++ b/dyn_load.c
@@ -74,23 +74,41 @@ STATIC GC_has_static_roots_func GC_has_static_roots = 0;
#endif
#if defined(NETBSD)
+# include <sys/param.h>
+# include <dlfcn.h>
# include <machine/elf_machdep.h>
# define ELFSIZE ARCH_ELFSIZE
#endif
+#if defined(OPENBSD)
+# include <sys/param.h>
+# if OpenBSD >= 200519
+# define HAVE_DL_ITERATE_PHDR
+# endif
+#endif /* OPENBSD */
+
#if defined(SCO_ELF) || defined(DGUX) || defined(HURD) \
|| (defined(__ELF__) && (defined(LINUX) || defined(FREEBSD) \
|| defined(NETBSD) || defined(OPENBSD)))
# include <stddef.h>
# if !defined(OPENBSD) && !defined(PLATFORM_ANDROID)
- /* FIXME: Why we exclude it for OpenBSD? */
+ /* OpenBSD does not have elf.h file; link.h below is sufficient. */
/* Exclude Android because linker.h below includes its own version. */
# include <elf.h>
# endif
# ifdef PLATFORM_ANDROID
- /* The header file is in bionics/linker. */
+ /* The header file is in "bionic/linker" folder of Android sources. */
/* If you don't need the "dynamic loading" feature, you may build */
/* the collector with -D IGNORE_DYNAMIC_LOADING. */
+# ifdef BIONIC_ELFDATA_REDEF_BUG
+ /* Workaround a problem in Android 4.1 (and 4.2) Bionic which has */
+ /* mismatching ELF_DATA definitions in sys/exec_elf.h and */
+ /* asm/elf.h included from linker.h file (similar to EM_ALPHA). */
+# include <asm/elf.h>
+# include <linux/elf-em.h>
+# undef ELF_DATA
+# undef EM_ALPHA
+# endif
# include <linker.h>
# else
# include <link.h>
@@ -149,7 +167,8 @@ GC_FirstDLOpenedLinkMap(void)
dynStructureAddr = &_DYNAMIC;
# endif
- if( dynStructureAddr == 0) {
+ if (dynStructureAddr == 0) {
+ /* _DYNAMIC symbol not resolved. */
return(0);
}
if( cachedResult == 0 ) {
@@ -246,18 +265,18 @@ static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
while (nsorted < n) {
while (nsorted < n &&
- base[nsorted-1].hs_start < base[nsorted].hs_start)
+ (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start)
++nsorted;
if (nsorted == n) break;
- GC_ASSERT(base[nsorted-1].hs_start > base[nsorted].hs_start);
+ GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start);
i = nsorted - 1;
- while (i >= 0 && base[i].hs_start > base[i+1].hs_start) {
+ while (i >= 0 && (word)base[i].hs_start > (word)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);
+ GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start);
++nsorted;
}
}
@@ -298,7 +317,8 @@ STATIC word GC_register_map_entries(char *maps)
/* This is a writable mapping. Add it to */
/* the root set unless it is already otherwise */
/* accounted for. */
- if (start <= GC_stackbottom && end >= GC_stackbottom) {
+ if ((word)start <= (word)GC_stackbottom
+ && (word)end >= (word)GC_stackbottom) {
/* Stack mapping; discard */
continue;
}
@@ -315,7 +335,7 @@ STATIC word GC_register_map_entries(char *maps)
/* 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. */
+ /* uncollectible, 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. */
@@ -328,32 +348,34 @@ STATIC word GC_register_map_entries(char *maps)
/* very suboptimal for performance reasons. */
# endif
/* We no longer exclude the main data segment. */
- if (end <= least_ha || start >= greatest_ha) {
+ if ((word)end <= (word)least_ha
+ || (word)start >= (word)greatest_ha) {
/* The easy case; just trace entire segment */
GC_add_roots_inner((char *)start, (char *)end, TRUE);
continue;
}
/* Add sections that don't belong to us. */
i = 0;
- while (GC_our_memory[i].hs_start + GC_our_memory[i].hs_bytes
- < start)
+ while ((word)(GC_our_memory[i].hs_start
+ + GC_our_memory[i].hs_bytes) < (word)start)
++i;
GC_ASSERT(i < GC_n_memory);
- if (GC_our_memory[i].hs_start <= start) {
+ if ((word)GC_our_memory[i].hs_start <= (word)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)
+ while (i < GC_n_memory
+ && (word)GC_our_memory[i].hs_start < (word)end
+ && (word)start < (word)end) {
+ if ((word)start < (word)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)
+ if ((word)start < (word)end)
GC_add_roots_inner((char *)start, (char *)end, TRUE);
}
}
@@ -377,16 +399,21 @@ GC_INNER GC_bool GC_register_main_static_data(void)
#else /* !USE_PROC_FOR_LIBRARIES */
/* The following is the preferred way to walk dynamic libraries */
-/* For glibc 2.2.4+. Unfortunately, it doesn't work for older */
+/* for glibc 2.2.4+. Unfortunately, it doesn't work for older */
/* versions. Thanks to Jakub Jelinek for most of the code. */
-#if (defined(LINUX) || defined (__GLIBC__)) /* Are others OK here, too? */ \
- && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
- || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
+ || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)) \
+ || defined(PLATFORM_ANDROID) /* Are others OK here, too? */
/* We have the header files for a glibc that includes dl_iterate_phdr. */
/* It may still not be available in the library on the target system. */
/* Thus we also treat it as a weak symbol. */
# define HAVE_DL_ITERATE_PHDR
+# ifdef PLATFORM_ANDROID
+ /* Android headers might have no such definition for some targets. */
+ int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *),
+ void *data);
+# endif
# pragma weak dl_iterate_phdr
#endif
@@ -440,7 +467,7 @@ STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info,
case PT_GNU_RELRO:
/* This entry is known to be constant and will eventually be remapped
read-only. However, the address range covered by this entry is
- typically a subset of a previously encountered `LOAD' segment, so
+ typically a subset of a previously encountered "LOAD" segment, so
we need to exclude it. */
{
int j;
@@ -448,11 +475,12 @@ STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info,
start = ((ptr_t)(p->p_vaddr)) + info->dlpi_addr;
end = start + p->p_memsz;
for (j = n_load_segs; --j >= 0; ) {
- if (start >= load_segs[j].start && start < load_segs[j].end) {
+ if ((word)start >= (word)load_segs[j].start
+ && (word)start < (word)load_segs[j].end) {
if (load_segs[j].start2 != 0) {
WARN("More than one GNU_RELRO segment per load seg\n",0);
} else {
- GC_ASSERT(end <= load_segs[j].end);
+ GC_ASSERT((word)end <= (word)load_segs[j].end);
/* Remove from the existing load segment */
load_segs[j].end2 = load_segs[j].end;
load_segs[j].end = start;
@@ -544,10 +572,10 @@ STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void)
int i;
for (i = 0; i < n_load_segs; ++i) {
- if (load_segs[i].end > load_segs[i].start) {
+ if ((word)load_segs[i].end > (word)load_segs[i].start) {
GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE);
}
- if (load_segs[i].end2 > load_segs[i].start2) {
+ if ((word)load_segs[i].end2 > (word)load_segs[i].start2) {
GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE);
}
}
@@ -634,10 +662,16 @@ GC_FirstDLOpenedLinkMap(void)
ElfW(Dyn) *dp;
static struct link_map *cachedResult = 0;
- if( _DYNAMIC == 0) {
+ if (0 == (ptr_t)_DYNAMIC) {
+ /* _DYNAMIC symbol not resolved. */
return(0);
}
if( cachedResult == 0 ) {
+# if defined(NETBSD) && defined(RTLD_DI_LINKMAP)
+ struct link_map *lm = NULL;
+ if (!dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lm))
+ cachedResult = lm;
+# else
int tag;
for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
if( tag == DT_DEBUG ) {
@@ -647,6 +681,7 @@ GC_FirstDLOpenedLinkMap(void)
break;
}
}
+# endif /* !NETBSD || !RTLD_DI_LINKMAP */
}
return cachedResult;
}
@@ -729,7 +764,8 @@ GC_INNER void GC_register_dynamic_libraries(void)
# endif /* SOLARISDL */
if (fd < 0) {
- sprintf(buf, "/proc/%ld", (long)getpid());
+ (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid());
+ buf[sizeof(buf) - 1] = '\0';
/* The above generates a lint complaint, since pid_t varies. */
/* It's unclear how to improve this. */
fd = open(buf, O_RDONLY);
@@ -738,24 +774,27 @@ GC_INNER void GC_register_dynamic_libraries(void)
}
}
if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
- GC_err_printf("fd = %d, errno = %d\n", fd, errno);
- ABORT("/proc PIOCNMAP ioctl failed");
+ ABORT_ARG2("/proc PIOCNMAP ioctl failed",
+ ": fd = %d, errno = %d", fd, errno);
}
if (needed_sz >= current_sz) {
current_sz = needed_sz * 2 + 1;
/* Expansion, plus room for 0 record */
addr_map = (prmap_t *)GC_scratch_alloc(
(word)current_sz * sizeof(prmap_t));
+ if (addr_map == NULL)
+ ABORT("Insufficient memory for address map");
}
if (ioctl(fd, PIOCMAP, addr_map) < 0) {
- GC_err_printf("fd = %d, errno = %d, needed_sz = %d, addr_map = %p\n",
- fd, errno, needed_sz, addr_map);
- ABORT("/proc PIOCMAP ioctl failed");
+ ABORT_ARG3("/proc PIOCMAP ioctl failed",
+ ": errcode= %d, needed_sz= %d, addr_map= %p",
+ errno, needed_sz, addr_map);
};
if (GC_n_heap_sects > 0) {
heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
+ GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
- if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr;
+ if ((word)heap_end < (word)GC_scratch_last_end_ptr)
+ heap_end = GC_scratch_last_end_ptr;
}
for (i = 0; i < needed_sz; i++) {
flags = addr_map[i].pr_mflags;
@@ -770,11 +809,8 @@ GC_INNER void GC_register_dynamic_libraries(void)
/* This makes no sense to me. - HB */
start = (ptr_t)(addr_map[i].pr_vaddr);
if (GC_roots_present(start)) goto irrelevant;
- if (start < heap_end && start >= heap_start)
+ if ((word)start < (word)heap_end && (word)start >= (word)heap_start)
goto irrelevant;
-# ifdef MMAP_STACKS
- if (GC_is_thread_stack(start)) goto irrelevant;
-# endif /* MMAP_STACKS */
limit = start + addr_map[i].pr_size;
/* The following seemed to be necessary for very old versions */
@@ -842,18 +878,21 @@ GC_INNER void GC_register_dynamic_libraries(void)
if (base == limit) return;
for(;;) {
GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi);
- if (next_stack_lo >= limit) break;
- if (next_stack_lo > curr_base)
+ if ((word)next_stack_lo >= (word)limit) break;
+ if ((word)next_stack_lo > (word)curr_base)
GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
curr_base = next_stack_hi;
}
- if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
+ if ((word)curr_base < (word)limit)
+ GC_add_roots_inner(curr_base, limit, TRUE);
# else
- char dummy;
char * stack_top
- = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
+ = (char *)((word)GC_approx_sp() &
+ ~(GC_sysinfo.dwAllocationGranularity - 1));
+
if (base == limit) return;
- if (limit > stack_top && base < GC_stackbottom) {
+ if ((word)limit > (word)stack_top
+ && (word)base < (word)GC_stackbottom) {
/* Part of the stack; ignore it. */
return;
}
@@ -910,7 +949,7 @@ GC_INNER void GC_register_dynamic_libraries(void)
if (GC_no_win32_dlls) return;
# endif
base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
- while (p < GC_sysinfo.lpMaximumApplicationAddress) {
+ while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) {
result = VirtualQuery(p, &buf, sizeof(buf));
# ifdef MSWINCE
if (result == 0) {
@@ -951,7 +990,7 @@ GC_INNER void GC_register_dynamic_libraries(void)
limit = new_limit;
}
}
- if (p > (LPVOID)new_limit /* overflow */) break;
+ if ((word)p > (word)new_limit /* overflow */) break;
p = (LPVOID)new_limit;
}
GC_cond_add_roots(base, limit);
@@ -997,18 +1036,12 @@ GC_INNER void GC_register_dynamic_libraries(void)
if (moduleid == LDR_NULL_MODULE)
break; /* No more modules */
- /* Check status AFTER checking moduleid because */
- /* of a bug in the non-shared ldr_next_module stub */
+ /* Check status AFTER checking moduleid because */
+ /* of a bug in the non-shared ldr_next_module stub. */
if (status != 0) {
- if (GC_print_stats) {
- GC_log_printf("dynamic_load: status = %d\n", status);
- if (errno < sys_nerr) {
- GC_log_printf("dynamic_load: %s\n", sys_errlist[errno]);
- } else {
- GC_log_printf("dynamic_load: err_code = %d\n", errno);
- }
- }
- ABORT("ldr_next_module failed");
+ ABORT_ARG3("ldr_next_module failed",
+ ": status= %d, errcode= %d (%s)", status, errno,
+ errno < sys_nerr ? sys_errlist[errno] : "");
}
/* Get the module information */
@@ -1083,7 +1116,7 @@ GC_INNER void GC_register_dynamic_libraries(void)
/* Get info about next shared library */
status = shl_get(index, &shl_desc);
- /* Check if this is the end of the list or if some error occured */
+ /* Check if this is the end of the list or if some error occurred */
if (status != 0) {
# ifdef GC_HPUX_THREADS
/* I've seen errno values of 0. The man page is not clear */
@@ -1093,14 +1126,9 @@ GC_INNER void GC_register_dynamic_libraries(void)
if (errno == EINVAL) {
break; /* Moved past end of shared library list --> finished */
} else {
- if (GC_print_stats) {
- if (errno < sys_nerr) {
- GC_log_printf("dynamic_load: %s\n", sys_errlist[errno]);
- } else {
- GC_log_printf("dynamic_load: err_code = %d\n", errno);
- }
- }
- ABORT("shl_get failed");
+ ABORT_ARG3("shl_get failed",
+ ": status= %d, errcode= %d (%s)", status, errno,
+ errno < sys_nerr ? sys_errlist[errno] : "");
}
# endif
}
@@ -1197,13 +1225,11 @@ STATIC const struct {
/* containing private vs. public symbols. It also constructs */
/* sections specifically for zero-sized objects, when the */
/* target supports section anchors. */
-STATIC const char * GC_dyld_add_sect_fmts[] =
-{
+STATIC const char * const GC_dyld_add_sect_fmts[] = {
"__bss%u",
"__pu_bss%u",
"__zo_bss%u",
- "__zo_pu_bss%u",
- NULL
+ "__zo_pu_bss%u"
};
/* Currently, mach-o will allow up to the max of 2^15 alignment */
@@ -1263,10 +1289,12 @@ STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr,
}
/* Sections constructed on demand. */
- for (j = 0; (fmt = GC_dyld_add_sect_fmts[j]) != NULL; j++) {
+ for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
+ fmt = GC_dyld_add_sect_fmts[j];
/* Add our manufactured aligned BSS sections. */
for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
- snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+ (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+ secnam[sizeof(secnam) - 1] = '\0';
sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
if (sec == NULL || sec->size == 0)
continue;
@@ -1314,9 +1342,11 @@ STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
}
/* Remove our on-demand sections. */
- for (j = 0; (fmt = GC_dyld_add_sect_fmts[j]) != NULL; j++) {
+ for (j = 0; j < sizeof(GC_dyld_add_sect_fmts) / sizeof(char *); j++) {
+ fmt = GC_dyld_add_sect_fmts[j];
for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) {
- snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+ (void)snprintf(secnam, sizeof(secnam), fmt, (unsigned)i);
+ secnam[sizeof(secnam) - 1] = '\0';
sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam);
if (sec == NULL || sec->size == 0)
continue;
diff --git a/extra/AmigaOS.c b/extra/AmigaOS.c
index 759b3dfb..8f99b746 100644
--- a/extra/AmigaOS.c
+++ b/extra/AmigaOS.c
@@ -2,7 +2,7 @@
/******************************************************************
- AmigaOS-spesific routines for GC.
+ AmigaOS-specific routines for GC.
This file is normally included from os_dep.c
******************************************************************/
@@ -47,13 +47,13 @@ ptr_t GC_get_main_stack_base()
/* Reference: Amiga Guru Book Pages: 42,567,574 */
if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS
&& proc->pr_CLI != NULL) {
- /* first ULONG is StackSize */
- /*longPtr = proc->pr_ReturnAddr;
- size = longPtr[0];*/
+ /* first ULONG is StackSize */
+ /*longPtr = proc->pr_ReturnAddr;
+ size = longPtr[0];*/
- return (char *)proc->pr_ReturnAddr + sizeof(ULONG);
+ return (char *)proc->pr_ReturnAddr + sizeof(ULONG);
} else {
- return (char *)proc->pr_Task.tc_SPUpper;
+ return (char *)proc->pr_Task.tc_SPUpper;
}
}
@@ -69,16 +69,16 @@ ptr_t GC_get_stack_base()
long size;
if ((task = FindTask(0)) == 0) {
- GC_err_puts("Cannot find own task structure\n");
- ABORT("task missing");
+ GC_err_puts("Cannot find own task structure\n");
+ ABORT("task missing");
}
proc = (struct Process *)task;
cli = BADDR(proc->pr_CLI);
if (_WBenchMsg != 0 || cli == 0) {
- size = (char *)task->tc_SPUpper - (char *)task->tc_SPLower;
+ size = (char *)task->tc_SPUpper - (char *)task->tc_SPLower;
} else {
- size = cli->cli_DefaultStack * 4;
+ size = cli->cli_DefaultStack * 4;
}
return (ptr_t)(__base + GC_max(size, __stack));
}
@@ -95,96 +95,96 @@ ptr_t GC_get_stack_base()
void GC_register_data_segments()
{
- struct Process *proc;
+ struct Process *proc;
struct CommandLineInterface *cli;
BPTR myseglist;
ULONG *data;
- int num;
+ int num;
# ifdef __GNUC__
ULONG dataSegSize;
GC_bool found_segment = FALSE;
- extern char __data_size[];
+ extern char __data_size[];
- dataSegSize=__data_size+8;
- /* Can`t find the Location of __data_size, because
+ dataSegSize=__data_size+8;
+ /* Can`t find the Location of __data_size, because
it`s possible that is it, inside the segment. */
# endif
- proc= (struct Process*)SysBase->ThisTask;
-
- /* Reference: Amiga Guru Book Pages: 538ff,565,573
- and XOper.asm */
- if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) {
- if (proc->pr_CLI == NULL) {
- myseglist = proc->pr_SegList;
- } else {
- /* ProcLoaded 'Loaded as a command: '*/
- cli = BADDR(proc->pr_CLI);
- myseglist = cli->cli_Module;
- }
- } else {
- ABORT("Not a Process.");
- }
-
- if (myseglist == NULL) {
- ABORT("Arrrgh.. can't find segments, aborting");
- }
-
- /* xoper hunks Shell Process */
-
- num=0;
+ proc= (struct Process*)SysBase->ThisTask;
+
+ /* Reference: Amiga Guru Book Pages: 538ff,565,573
+ and XOper.asm */
+ if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) {
+ if (proc->pr_CLI == NULL) {
+ myseglist = proc->pr_SegList;
+ } else {
+ /* ProcLoaded 'Loaded as a command: '*/
+ cli = BADDR(proc->pr_CLI);
+ myseglist = cli->cli_Module;
+ }
+ } else {
+ ABORT("Not a Process.");
+ }
+
+ if (myseglist == NULL) {
+ ABORT("Arrrgh.. can't find segments, aborting");
+ }
+
+ /* xoper hunks Shell Process */
+
+ num=0;
for (data = (ULONG *)BADDR(myseglist); data != NULL;
data = (ULONG *)BADDR(data[0])) {
- if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) ||
- ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) {
+ if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) ||
+ ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) {
# ifdef __GNUC__
- if (dataSegSize == data[-1]) {
- found_segment = TRUE;
- }
-# endif
- GC_add_roots_inner((char *)&data[1],
- ((char *)&data[1]) + data[-1], FALSE);
+ if (dataSegSize == data[-1]) {
+ found_segment = TRUE;
+ }
+# endif
+ GC_add_roots_inner((char *)&data[1],
+ ((char *)&data[1]) + data[-1], FALSE);
}
++num;
} /* for */
-# ifdef __GNUC__
- if (!found_segment) {
- ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library");
- }
-# endif
+# ifdef __GNUC__
+ if (!found_segment) {
+ ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library");
+ }
+# endif
}
#if 0 /* old version */
void GC_register_data_segments()
{
extern struct WBStartup *_WBenchMsg;
- struct Process *proc;
+ struct Process *proc;
struct CommandLineInterface *cli;
BPTR myseglist;
ULONG *data;
if ( _WBenchMsg != 0 ) {
- if ((myseglist = _WBenchMsg->sm_Segment) == 0) {
- GC_err_puts("No seglist from workbench\n");
- return;
- }
+ if ((myseglist = _WBenchMsg->sm_Segment) == 0) {
+ GC_err_puts("No seglist from workbench\n");
+ return;
+ }
} else {
- if ((proc = (struct Process *)FindTask(0)) == 0) {
- GC_err_puts("Cannot find process structure\n");
- return;
- }
- if ((cli = BADDR(proc->pr_CLI)) == 0) {
- GC_err_puts("No CLI\n");
- return;
- }
- if ((myseglist = cli->cli_Module) == 0) {
- GC_err_puts("No seglist from CLI\n");
- return;
- }
+ if ((proc = (struct Process *)FindTask(0)) == 0) {
+ GC_err_puts("Cannot find process structure\n");
+ return;
+ }
+ if ((cli = BADDR(proc->pr_CLI)) == 0) {
+ GC_err_puts("No CLI\n");
+ return;
+ }
+ if ((myseglist = cli->cli_Module) == 0) {
+ GC_err_puts("No seglist from CLI\n");
+ return;
+ }
}
for (data = (ULONG *)BADDR(myseglist); data != 0;
@@ -192,11 +192,11 @@ ptr_t GC_get_stack_base()
# ifdef AMIGA_SKIP_SEG
if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) ||
((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) {
-# else
- {
-# endif /* AMIGA_SKIP_SEG */
+# else
+ {
+# endif /* AMIGA_SKIP_SEG */
GC_add_roots_inner((char *)&data[1],
- ((char *)&data[1]) + data[-1], FALSE);
+ ((char *)&data[1]) + data[-1], FALSE);
}
}
}
@@ -212,11 +212,11 @@ ptr_t GC_get_stack_base()
#ifndef GC_AMIGA_FASTALLOC
void *GC_amiga_allocwrapper(size_t size,void *(*AllocFunction)(size_t size2)){
- return (*AllocFunction)(size);
+ return (*AllocFunction)(size);
}
void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2))
- =GC_amiga_allocwrapper;
+ =GC_amiga_allocwrapper;
#else
@@ -226,14 +226,14 @@ void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size
void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2));
void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2))
- =GC_amiga_allocwrapper_firsttime;
+ =GC_amiga_allocwrapper_firsttime;
/******************************************************************
- Amiga-spesific routines to obtain memory, and force GC to give
+ Amiga-specific routines to obtain memory, and force GC to give
back fast-mem whenever possible.
- These hacks makes gc-programs go many times faster when
- the amiga is low on memory, and are therefore strictly necesarry.
+ These hacks makes gc-programs go many times faster when
+ the Amiga is low on memory, and are therefore strictly necessary.
-Kjetil S. Matheussen, 2000.
******************************************************************/
@@ -243,8 +243,8 @@ void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size
/* List-header for all allocated memory. */
struct GC_Amiga_AllocedMemoryHeader{
- ULONG size;
- struct GC_Amiga_AllocedMemoryHeader *next;
+ ULONG size;
+ struct GC_Amiga_AllocedMemoryHeader *next;
};
struct GC_Amiga_AllocedMemoryHeader *GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(int)~(NULL);
@@ -285,42 +285,42 @@ int ncur151=0;
/* Free everything at program-end. */
void GC_amiga_free_all_mem(void){
- struct GC_Amiga_AllocedMemoryHeader *gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(GC_AMIGAMEM));
- struct GC_Amiga_AllocedMemoryHeader *temp;
+ struct GC_Amiga_AllocedMemoryHeader *gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(GC_AMIGAMEM));
+ struct GC_Amiga_AllocedMemoryHeader *temp;
#ifdef GC_AMIGA_PRINTSTATS
- printf("\n\n"
- "%d bytes of chip-mem, and %d bytes of fast-mem where allocated from the OS.\n",
- allochip,allocfast
- );
- printf(
- "%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n",
- chipa
- );
- printf("\n");
- printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects);
- printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries);
- printf("\n");
- printf("Succeded forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",succ,succ2);
- printf("Failed forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",nsucc,nsucc2);
- printf("\n");
- printf(
- "Number of retries before succeding a chip->fast force:\n"
- "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
- cur0,cur1,cur10,cur50,cur150,cur151
- );
- printf(
- "Number of retries before giving up a chip->fast force:\n"
- "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
- ncur0,ncur1,ncur10,ncur50,ncur150,ncur151
- );
+ printf("\n\n"
+ "%d bytes of chip-mem, and %d bytes of fast-mem where allocated from the OS.\n",
+ allochip,allocfast
+ );
+ printf(
+ "%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n",
+ chipa
+ );
+ printf("\n");
+ printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects);
+ printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries);
+ printf("\n");
+ printf("Succeded forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",succ,succ2);
+ printf("Failed forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",nsucc,nsucc2);
+ printf("\n");
+ printf(
+ "Number of retries before succeding a chip->fast force:\n"
+ "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
+ cur0,cur1,cur10,cur50,cur150,cur151
+ );
+ printf(
+ "Number of retries before giving up a chip->fast force:\n"
+ "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n",
+ ncur0,ncur1,ncur10,ncur50,ncur150,ncur151
+ );
#endif
- while(gc_am!=NULL){
- temp=gc_am->next;
- FreeMem(gc_am,gc_am->size);
- gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp));
- }
+ while(gc_am!=NULL){
+ temp=gc_am->next;
+ FreeMem(gc_am,gc_am->size);
+ gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp));
+ }
}
#ifndef GC_AMIGA_ONLYFAST
@@ -331,7 +331,7 @@ char *chipmax;
/*
- * Allways set to the last size of memory tried to be allocated.
+ * Always set to the last size of memory tried to be allocated.
* Needed to ensure allocation when the size is bigger than 100000.
*
*/
@@ -346,36 +346,36 @@ size_t latestsize;
*/
void *GC_amiga_get_mem(size_t size){
- struct GC_Amiga_AllocedMemoryHeader *gc_am;
+ struct GC_Amiga_AllocedMemoryHeader *gc_am;
#ifndef GC_AMIGA_ONLYFAST
- if(GC_amiga_dontalloc==TRUE){
-// printf("rejected, size: %d, latestsize: %d\n",size,latestsize);
- return NULL;
- }
+ if(GC_amiga_dontalloc==TRUE){
+// printf("rejected, size: %d, latestsize: %d\n",size,latestsize);
+ return NULL;
+ }
- // We really don't want to use chip-mem, but if we must, then as little as possible.
- if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR) && size>100000 && latestsize<50000) return NULL;
+ // We really don't want to use chip-mem, but if we must, then as little as possible.
+ if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR) && size>100000 && latestsize<50000) return NULL;
#endif
- gc_am=AllocMem((ULONG)(size + sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF);
- if(gc_am==NULL) return NULL;
+ gc_am=AllocMem((ULONG)(size + sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF);
+ if(gc_am==NULL) return NULL;
- gc_am->next=GC_AMIGAMEM;
- gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader);
- GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am));
+ gc_am->next=GC_AMIGAMEM;
+ gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader);
+ GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am));
-// printf("Allocated %d (%d) bytes at address: %x. Latest: %d\n",size,tot,gc_am,latestsize);
+// printf("Allocated %d (%d) bytes at address: %x. Latest: %d\n",size,tot,gc_am,latestsize);
#ifdef GC_AMIGA_PRINTSTATS
- if((char *)gc_am<chipmax){
- allochip+=size;
- }else{
- allocfast+=size;
- }
+ if((char *)gc_am<chipmax){
+ allochip+=size;
+ }else{
+ allocfast+=size;
+ }
#endif
- return gc_am+1;
+ return gc_am+1;
}
@@ -391,125 +391,125 @@ void *GC_amiga_get_mem(size_t size){
*/
#ifdef GC_AMIGA_RETRY
void *GC_amiga_rec_alloc(size_t size,void *(*AllocFunction)(size_t size2),const int rec){
- void *ret;
+ void *ret;
- ret=(*AllocFunction)(size);
+ ret=(*AllocFunction)(size);
#ifdef GC_AMIGA_PRINTSTATS
- if((char *)ret>chipmax || ret==NULL){
- if(ret==NULL){
- nsucc++;
- nsucc2+=size;
- if(rec==0) ncur0++;
- if(rec==1) ncur1++;
- if(rec>1 && rec<10) ncur10++;
- if(rec>=10 && rec<50) ncur50++;
- if(rec>=50 && rec<150) ncur150++;
- if(rec>=150) ncur151++;
- }else{
- succ++;
- succ2+=size;
- if(rec==0) cur0++;
- if(rec==1) cur1++;
- if(rec>1 && rec<10) cur10++;
- if(rec>=10 && rec<50) cur50++;
- if(rec>=50 && rec<150) cur150++;
- if(rec>=150) cur151++;
- }
- }
+ if((char *)ret>chipmax || ret==NULL){
+ if(ret==NULL){
+ nsucc++;
+ nsucc2+=size;
+ if(rec==0) ncur0++;
+ if(rec==1) ncur1++;
+ if(rec>1 && rec<10) ncur10++;
+ if(rec>=10 && rec<50) ncur50++;
+ if(rec>=50 && rec<150) ncur150++;
+ if(rec>=150) ncur151++;
+ }else{
+ succ++;
+ succ2+=size;
+ if(rec==0) cur0++;
+ if(rec==1) cur1++;
+ if(rec>1 && rec<10) cur10++;
+ if(rec>=10 && rec<50) cur50++;
+ if(rec>=50 && rec<150) cur150++;
+ if(rec>=150) cur151++;
+ }
+ }
#endif
- if (((char *)ret)<=chipmax && ret!=NULL && (rec<(size>500000?9:size/5000))){
- ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1);
-// GC_free(ret2);
- }
+ if (((char *)ret)<=chipmax && ret!=NULL && (rec<(size>500000?9:size/5000))){
+ ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1);
+// GC_free(ret2);
+ }
- return ret;
+ return ret;
}
#endif
-/* The allocating-functions defined inside the amiga-blocks in gc.h is called
+/* The allocating-functions defined inside the Amiga-blocks in gc.h is called
* via these functions.
*/
void *GC_amiga_allocwrapper_any(size_t size,void *(*AllocFunction)(size_t size2)){
- void *ret,*ret2;
+ void *ret,*ret2;
- GC_amiga_dontalloc=TRUE; // Pretty tough thing to do, but its indeed necesarry.
- latestsize=size;
+ GC_amiga_dontalloc=TRUE; // Pretty tough thing to do, but its indeed necessary.
+ latestsize=size;
- ret=(*AllocFunction)(size);
+ ret=(*AllocFunction)(size);
- if(((char *)ret) <= chipmax){
- if(ret==NULL){
- //Give GC access to allocate memory.
+ if(((char *)ret) <= chipmax){
+ if(ret==NULL){
+ //Give GC access to allocate memory.
#ifdef GC_AMIGA_GC
- if(!GC_dont_gc){
- GC_gcollect();
+ if(!GC_dont_gc){
+ GC_gcollect();
#ifdef GC_AMIGA_PRINTSTATS
- numcollects++;
+ numcollects++;
#endif
- ret=(*AllocFunction)(size);
- }
+ ret=(*AllocFunction)(size);
+ }
#endif
- if(ret==NULL){
- GC_amiga_dontalloc=FALSE;
- ret=(*AllocFunction)(size);
- if(ret==NULL){
- WARN("Out of Memory! Returning NIL!\n", 0);
- }
- }
+ if(ret==NULL){
+ GC_amiga_dontalloc=FALSE;
+ ret=(*AllocFunction)(size);
+ if(ret==NULL){
+ WARN("Out of Memory! Returning NIL!\n", 0);
+ }
+ }
#ifdef GC_AMIGA_PRINTSTATS
- else{
- nullretries++;
- }
- if(ret!=NULL && (char *)ret<=chipmax) chipa+=size;
+ else{
+ nullretries++;
+ }
+ if(ret!=NULL && (char *)ret<=chipmax) chipa+=size;
#endif
- }
+ }
#ifdef GC_AMIGA_RETRY
- else{
- /* We got chip-mem. Better try again and again and again etc., we might get fast-mem sooner or later... */
- /* Using gctest to check the effectiviness of doing this, does seldom give a very good result. */
- /* However, real programs doesn't normally rapidly allocate and deallocate. */
-// printf("trying to force... %d bytes... ",size);
- if(
- AllocFunction!=GC_malloc_uncollectable
+ else{
+ /* We got chip-mem. Better try again and again and again etc., we might get fast-mem sooner or later... */
+ /* Using gctest to check the effectiveness of doing this, does seldom give a very good result. */
+ /* However, real programs doesn't normally rapidly allocate and deallocate. */
+// printf("trying to force... %d bytes... ",size);
+ if(
+ AllocFunction!=GC_malloc_uncollectable
#ifdef ATOMIC_UNCOLLECTABLE
- && AllocFunction!=GC_malloc_atomic_uncollectable
+ && AllocFunction!=GC_malloc_atomic_uncollectable
#endif
- ){
- ret2=GC_amiga_rec_alloc(size,AllocFunction,0);
- }else{
- ret2=(*AllocFunction)(size);
+ ){
+ ret2=GC_amiga_rec_alloc(size,AllocFunction,0);
+ }else{
+ ret2=(*AllocFunction)(size);
#ifdef GC_AMIGA_PRINTSTATS
- if((char *)ret2<chipmax || ret2==NULL){
- nsucc++;
- nsucc2+=size;
- ncur0++;
- }else{
- succ++;
- succ2+=size;
- cur0++;
- }
+ if((char *)ret2<chipmax || ret2==NULL){
+ nsucc++;
+ nsucc2+=size;
+ ncur0++;
+ }else{
+ succ++;
+ succ2+=size;
+ cur0++;
+ }
#endif
- }
- if(((char *)ret2)>chipmax){
-// printf("Succeeded.\n");
- GC_free(ret);
- ret=ret2;
- }else{
- GC_free(ret2);
-// printf("But did not succeed.\n");
- }
- }
+ }
+ if(((char *)ret2)>chipmax){
+// printf("Succeeded.\n");
+ GC_free(ret);
+ ret=ret2;
+ }else{
+ GC_free(ret2);
+// printf("But did not succeed.\n");
+ }
+ }
#endif
- }
+ }
- GC_amiga_dontalloc=FALSE;
+ GC_amiga_dontalloc=FALSE;
- return ret;
+ return ret;
}
@@ -517,52 +517,52 @@ void *GC_amiga_allocwrapper_any(size_t size,void *(*AllocFunction)(size_t size2)
void (*GC_amiga_toany)(void)=NULL;
void GC_amiga_set_toany(void (*func)(void)){
- GC_amiga_toany=func;
+ GC_amiga_toany=func;
}
#endif // !GC_AMIGA_ONLYFAST
void *GC_amiga_allocwrapper_fast(size_t size,void *(*AllocFunction)(size_t size2)){
- void *ret;
+ void *ret;
- ret=(*AllocFunction)(size);
+ ret=(*AllocFunction)(size);
- if(ret==NULL){
- // Enable chip-mem allocation.
-// printf("ret==NULL\n");
+ if(ret==NULL){
+ // Enable chip-mem allocation.
+// printf("ret==NULL\n");
#ifdef GC_AMIGA_GC
- if(!GC_dont_gc){
- GC_gcollect();
+ if(!GC_dont_gc){
+ GC_gcollect();
#ifdef GC_AMIGA_PRINTSTATS
- numcollects++;
+ numcollects++;
#endif
- ret=(*AllocFunction)(size);
- }
+ ret=(*AllocFunction)(size);
+ }
#endif
- if(ret==NULL){
+ if(ret==NULL){
#ifndef GC_AMIGA_ONLYFAST
- GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
- if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
- GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
- return GC_amiga_allocwrapper_any(size,AllocFunction);
+ GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
+ if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
+ GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
+ return GC_amiga_allocwrapper_any(size,AllocFunction);
#endif
- }
+ }
#ifdef GC_AMIGA_PRINTSTATS
- else{
- nullretries++;
- }
+ else{
+ nullretries++;
+ }
#endif
- }
+ }
- return ret;
+ return ret;
}
void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)){
- atexit(&GC_amiga_free_all_mem);
- chipmax=(char *)SysBase->MaxLocMem; // For people still having SysBase in chip-mem, this might speed up a bit.
- GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast;
- return GC_amiga_allocwrapper_fast(size,AllocFunction);
+ atexit(&GC_amiga_free_all_mem);
+ chipmax=(char *)SysBase->MaxLocMem; // For people still having SysBase in chip-mem, this might speed up a bit.
+ GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast;
+ return GC_amiga_allocwrapper_fast(size,AllocFunction);
}
@@ -576,45 +576,45 @@ void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t
*/
void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes){
#ifndef GC_AMIGA_FASTALLOC
- return GC_realloc(old_object,new_size_in_bytes);
+ return GC_realloc(old_object,new_size_in_bytes);
#else
- void *ret;
- latestsize=new_size_in_bytes;
- ret=GC_realloc(old_object,new_size_in_bytes);
- if(ret==NULL && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){
- /* Out of fast-mem. */
+ void *ret;
+ latestsize=new_size_in_bytes;
+ ret=GC_realloc(old_object,new_size_in_bytes);
+ if(ret==NULL && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){
+ /* Out of fast-mem. */
#ifdef GC_AMIGA_GC
- if(!GC_dont_gc){
- GC_gcollect();
+ if(!GC_dont_gc){
+ GC_gcollect();
#ifdef GC_AMIGA_PRINTSTATS
- numcollects++;
+ numcollects++;
#endif
- ret=GC_realloc(old_object,new_size_in_bytes);
- }
+ ret=GC_realloc(old_object,new_size_in_bytes);
+ }
#endif
- if(ret==NULL){
+ if(ret==NULL){
#ifndef GC_AMIGA_ONLYFAST
- GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
- if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
- GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
- ret=GC_realloc(old_object,new_size_in_bytes);
+ GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR;
+ if(GC_amiga_toany!=NULL) (*GC_amiga_toany)();
+ GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any;
+ ret=GC_realloc(old_object,new_size_in_bytes);
#endif
- }
+ }
#ifdef GC_AMIGA_PRINTSTATS
- else{
- nullretries++;
- }
+ else{
+ nullretries++;
+ }
#endif
- }
- if(ret==NULL){
- WARN("Out of Memory! Returning NIL!\n", 0);
- }
+ }
+ if(ret==NULL){
+ WARN("Out of Memory! Returning NIL!\n", 0);
+ }
#ifdef GC_AMIGA_PRINTSTATS
- if(((char *)ret)<chipmax && ret!=NULL){
- chipa+=new_size_in_bytes;
- }
+ if(((char *)ret)<chipmax && ret!=NULL){
+ chipa+=new_size_in_bytes;
+ }
#endif
- return ret;
+ return ret;
#endif
}
diff --git a/extra/MacOS.c b/extra/MacOS.c
index 6cb5edb7..27f07eb4 100644
--- a/extra/MacOS.c
+++ b/extra/MacOS.c
@@ -1,17 +1,17 @@
/*
- MacOS.c
+ MacOS.c
- Some routines for the Macintosh OS port of the Hans-J. Boehm, Alan J. Demers
- garbage collector.
+ Some routines for the Macintosh OS port of the Hans-J. Boehm, Alan J. Demers
+ garbage collector.
- <Revision History>
+ <Revision History>
- 11/22/94 pcb StripAddress the temporary memory handle for 24-bit mode.
- 11/30/94 pcb Tracking all memory usage so we can deallocate it all at once.
- 02/10/96 pcb Added routine to perform a final collection when
+ 11/22/94 pcb StripAddress the temporary memory handle for 24-bit mode.
+ 11/30/94 pcb Tracking all memory usage so we can deallocate it all at once.
+ 02/10/96 pcb Added routine to perform a final collection when
unloading shared library.
- by Patrick C. Beard.
+ by Patrick C. Beard.
*/
/* Boehm, February 15, 1996 2:55 pm PST */
@@ -22,29 +22,30 @@ unloading shared library.
#include <stdlib.h>
#include <string.h>
+#define GC_BUILD
#include "gc.h"
#include "gc_priv.h"
// use 'CODE' resource 0 to get exact location of the beginning of global space.
typedef struct {
- unsigned long aboveA5;
- unsigned long belowA5;
- unsigned long JTSize;
- unsigned long JTOffset;
+ unsigned long aboveA5;
+ unsigned long belowA5;
+ unsigned long JTSize;
+ unsigned long JTOffset;
} *CodeZeroPtr, **CodeZeroHandle;
void* GC_MacGetDataStart()
{
- CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0);
- if (code0) {
- long belowA5Size = (**code0).belowA5;
- ReleaseResource((Handle)code0);
- return (LMGetCurrentA5() - belowA5Size);
- }
- fprintf(stderr, "Couldn't load the jump table.");
- exit(-1);
- return 0;
+ CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0);
+ if (code0) {
+ long belowA5Size = (**code0).belowA5;
+ ReleaseResource((Handle)code0);
+ return (LMGetCurrentA5() - belowA5Size);
+ }
+ fprintf(stderr, "Couldn't load the jump table.");
+ exit(-1);
+ return 0;
}
/* track the use of temporary memory so it can be freed all at once. */
@@ -52,8 +53,8 @@ void* GC_MacGetDataStart()
typedef struct TemporaryMemoryBlock TemporaryMemoryBlock, **TemporaryMemoryHandle;
struct TemporaryMemoryBlock {
- TemporaryMemoryHandle nextBlock;
- char data[];
+ TemporaryMemoryHandle nextBlock;
+ char data[];
};
static TemporaryMemoryHandle theTemporaryMemory = NULL;
@@ -63,32 +64,32 @@ void GC_MacFreeTemporaryMemory(void);
Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory)
{
- static Boolean firstTime = true;
- OSErr result;
- TemporaryMemoryHandle tempMemBlock;
- Ptr tempPtr = nil;
-
- tempMemBlock = (TemporaryMemoryHandle)TempNewHandle(size + sizeof(TemporaryMemoryBlock), &result);
- if (tempMemBlock && result == noErr) {
- HLockHi((Handle)tempMemBlock);
- tempPtr = (**tempMemBlock).data;
- if (clearMemory) memset(tempPtr, 0, size);
- tempPtr = StripAddress(tempPtr);
-
- // keep track of the allocated blocks.
- (**tempMemBlock).nextBlock = theTemporaryMemory;
- theTemporaryMemory = tempMemBlock;
- }
+ static Boolean firstTime = true;
+ OSErr result;
+ TemporaryMemoryHandle tempMemBlock;
+ Ptr tempPtr = nil;
+
+ tempMemBlock = (TemporaryMemoryHandle)TempNewHandle(size + sizeof(TemporaryMemoryBlock), &result);
+ if (tempMemBlock && result == noErr) {
+ HLockHi((Handle)tempMemBlock);
+ tempPtr = (**tempMemBlock).data;
+ if (clearMemory) memset(tempPtr, 0, size);
+ tempPtr = StripAddress(tempPtr);
+
+ // keep track of the allocated blocks.
+ (**tempMemBlock).nextBlock = theTemporaryMemory;
+ theTemporaryMemory = tempMemBlock;
+ }
# if !defined(SHARED_LIBRARY_BUILD)
- // install an exit routine to clean up the memory used at the end.
- if (firstTime) {
- atexit(&GC_MacFreeTemporaryMemory);
- firstTime = false;
- }
+ // install an exit routine to clean up the memory used at the end.
+ if (firstTime) {
+ atexit(&GC_MacFreeTemporaryMemory);
+ firstTime = false;
+ }
# endif
- return tempPtr;
+ return tempPtr;
}
extern word GC_fo_entries;
@@ -118,22 +119,22 @@ void GC_MacFreeTemporaryMemory()
# endif
if (theTemporaryMemory != NULL) {
- long totalMemoryUsed = 0;
- TemporaryMemoryHandle tempMemBlock = theTemporaryMemory;
- while (tempMemBlock != NULL) {
- TemporaryMemoryHandle nextBlock = (**tempMemBlock).nextBlock;
- totalMemoryUsed += GetHandleSize((Handle)tempMemBlock);
- DisposeHandle((Handle)tempMemBlock);
- tempMemBlock = nextBlock;
- }
- theTemporaryMemory = NULL;
+ long totalMemoryUsed = 0;
+ TemporaryMemoryHandle tempMemBlock = theTemporaryMemory;
+ while (tempMemBlock != NULL) {
+ TemporaryMemoryHandle nextBlock = (**tempMemBlock).nextBlock;
+ totalMemoryUsed += GetHandleSize((Handle)tempMemBlock);
+ DisposeHandle((Handle)tempMemBlock);
+ tempMemBlock = nextBlock;
+ }
+ theTemporaryMemory = NULL;
# if !defined(SHARED_LIBRARY_BUILD)
- if (GC_print_stats) {
+ if (GC_print_stats) {
fprintf(stdout, "[total memory used: %ld bytes.]\n",
totalMemoryUsed);
fprintf(stdout, "[total collections: %ld.]\n", GC_gc_no);
- }
+ }
# endif
}
}
@@ -142,15 +143,15 @@ void GC_MacFreeTemporaryMemory()
void* GC_MacGetDataEnd()
{
- CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0);
- if (code0) {
- long aboveA5Size = (**code0).aboveA5;
- ReleaseResource((Handle)code0);
- return (LMGetCurrentA5() + aboveA5Size);
- }
- fprintf(stderr, "Couldn't load the jump table.");
- exit(-1);
- return 0;
+ CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0);
+ if (code0) {
+ long aboveA5Size = (**code0).aboveA5;
+ ReleaseResource((Handle)code0);
+ return (LMGetCurrentA5() + aboveA5Size);
+ }
+ fprintf(stderr, "Couldn't load the jump table.");
+ exit(-1);
+ return 0;
}
#endif /* __option(far_data) */
diff --git a/Mac_files/MacOS_config.h b/extra/Mac_files/MacOS_config.h
index ec8f82c3..ec8f82c3 100644
--- a/Mac_files/MacOS_config.h
+++ b/extra/Mac_files/MacOS_config.h
diff --git a/extra/Mac_files/dataend.c b/extra/Mac_files/dataend.c
new file mode 100644
index 00000000..0bed4694
--- /dev/null
+++ b/extra/Mac_files/dataend.c
@@ -0,0 +1,9 @@
+/*
+ dataend.c
+
+ A hack to get the extent of global data for the Macintosh.
+
+ by Patrick C. Beard.
+ */
+
+long __dataend;
diff --git a/extra/Mac_files/datastart.c b/extra/Mac_files/datastart.c
new file mode 100644
index 00000000..a1dc6f6d
--- /dev/null
+++ b/extra/Mac_files/datastart.c
@@ -0,0 +1,9 @@
+/*
+ datastart.c
+
+ A hack to get the extent of global data for the Macintosh.
+
+ by Patrick C. Beard.
+ */
+
+long __datastart;
diff --git a/extra/gc.c b/extra/gc.c
index 9c16d206..fa70a0a8 100644
--- a/extra/gc.c
+++ b/extra/gc.c
@@ -70,7 +70,9 @@
#include "../specific.c"
#include "../win32_threads.c"
-#include "../pthread_start.c"
+#ifndef GC_PTHREAD_START_STANDALONE
+# include "../pthread_start.c"
+#endif
/* Restore pthread calls redirection (if altered in */
/* pthread_stop_world.c, pthread_support.c or win32_threads.c). */
diff --git a/extra/msvc_dbg.c b/extra/msvc_dbg.c
index 5bc216a1..ea7fc7b6 100644
--- a/extra/msvc_dbg.c
+++ b/extra/msvc_dbg.c
@@ -24,8 +24,8 @@
/* X86_64 is currently missing some meachine-dependent code below. */
+#define GC_BUILD
#include "private/msvc_dbg.h"
-
#include "gc.h"
#define WIN32_LEAN_AND_MEAN
@@ -314,7 +314,7 @@ size_t GetDescriptionFromAddress(void* address, const char* format,
*buffer = 0;
}
buffer += GetFileLineFromAddress(address, buffer, size, &line_number, NULL);
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
if (line_number) {
wsprintf(str, "(%d) : ", line_number);
@@ -322,26 +322,26 @@ size_t GetDescriptionFromAddress(void* address, const char* format,
strncpy(buffer, str, size)[size - 1] = 0;
}
buffer += strlen(str);
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
}
if (size) {
strncpy(buffer, "at ", size)[size - 1] = 0;
}
buffer += strlen("at ");
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
buffer += GetSymbolNameFromAddress(address, buffer, size, NULL);
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
if (size) {
strncpy(buffer, " in ", size)[size - 1] = 0;
}
buffer += strlen(" in ");
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
buffer += GetModuleNameFromAddress(address, buffer, size);
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
return buffer - begin;
}
@@ -356,7 +356,7 @@ size_t GetDescriptionFromStack(void* const frames[], size_t count,
size_t i;
for (i = 0; i < count; ++i) {
if (description) description[i] = buffer;
- size = end < buffer ? 0 : end - buffer;
+ size = (GC_ULONG_PTR)end < (GC_ULONG_PTR)buffer ? 0 : end - buffer;
buffer += 1 + GetDescriptionFromAddress(frames[i], NULL, buffer, size);
}
if (description) description[count] = NULL;
diff --git a/extra/symbian.cpp b/extra/symbian.cpp
new file mode 100644
index 00000000..94dd4de4
--- /dev/null
+++ b/extra/symbian.cpp
@@ -0,0 +1,55 @@
+
+#include <e32cmn.h>
+#include <e32std.h>
+#include <f32file.h>
+#include <aknutils.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int GC_get_main_symbian_stack_base()
+ {
+ TThreadStackInfo aInfo;
+ TInt err = RThread().StackInfo(aInfo);
+ if ( !err )
+ {
+ return aInfo.iBase;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+char* GC_get_private_path_and_zero_file()
+ {
+ // always on c: drive
+ RFs fs;
+ fs.Connect();
+ fs.CreatePrivatePath( EDriveC );
+ TFileName path;
+ fs.PrivatePath( path );
+ fs.Close();
+ _LIT( KCDrive, "c:" );
+ path.Insert( 0, KCDrive );
+
+
+ //convert to char*, assume ascii
+ TBuf8<KMaxFileName> path8;
+ path8.Copy( path );
+ _LIT8( KZero8, "zero" );
+ path8.Append( KZero8 );
+
+ size_t size = path8.Length() + 1;
+ char* copyChar = (char*) malloc( size );
+ memcpy( copyChar, path8.PtrZ(), size );
+
+ return copyChar; // ownership passed
+ }
+
+#ifdef __cplusplus
+ }
+#endif
diff --git a/extra/symbian/global_end.cpp b/extra/symbian/global_end.cpp
new file mode 100644
index 00000000..3e2e6d59
--- /dev/null
+++ b/extra/symbian/global_end.cpp
@@ -0,0 +1,16 @@
+// Symbian-specific file.
+
+// INCLUDE FILES
+#include "private/gcconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int winscw_data_end;
+
+#ifdef __cplusplus
+ }
+#endif
+
+// End Of File
diff --git a/extra/symbian/global_start.cpp b/extra/symbian/global_start.cpp
new file mode 100644
index 00000000..c6d67c3c
--- /dev/null
+++ b/extra/symbian/global_start.cpp
@@ -0,0 +1,16 @@
+// Symbian-specific file.
+
+// INCLUDE FILES
+#include "private/gcconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int winscw_data_start;
+
+#ifdef __cplusplus
+ }
+#endif
+
+// End Of File
diff --git a/extra/symbian/init_global_static_roots.cpp b/extra/symbian/init_global_static_roots.cpp
new file mode 100644
index 00000000..092d3415
--- /dev/null
+++ b/extra/symbian/init_global_static_roots.cpp
@@ -0,0 +1,33 @@
+// Symbian-specific file.
+
+// INCLUDE FILES
+#include <e32def.h>
+
+#include "private/gcconfig.h"
+#include "gc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GC_init_global_static_roots()
+{
+ ptr_t dataStart = NULL;
+ ptr_t dataEnd = NULL;
+# if defined (__WINS__)
+ extern int winscw_data_start, winscw_data_end;
+ dataStart = ((ptr_t)&winscw_data_start);
+ dataEnd = ((ptr_t)&winscw_data_end);
+# else
+ extern int Image$$RW$$Limit[], Image$$RW$$Base[];
+ dataStart = ((ptr_t)Image$$RW$$Base);
+ dataEnd = ((ptr_t)Image$$RW$$Limit);
+# endif
+
+ GC_add_roots(dataStart, dataEnd);
+
+}
+
+#ifdef __cplusplus
+ }
+#endif
diff --git a/finalize.c b/finalize.c
index 56f62f1f..c2e8af02 100644
--- a/finalize.c
+++ b/finalize.c
@@ -16,77 +16,79 @@
#include "private/gc_pmark.h"
-#ifdef FINALIZE_ON_DEMAND
- int GC_finalize_on_demand = 1;
-#else
- int GC_finalize_on_demand = 0;
-#endif
-
-#ifdef JAVA_FINALIZATION
- int GC_java_finalization = 1;
-#else
- int GC_java_finalization = 0;
-#endif
+#ifndef GC_NO_FINALIZATION
/* Type of mark procedure used for marking from finalizable object. */
/* This procedure normally does not mark the object, only its */
-/* descendents. */
+/* descendants. */
typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */);
#define HASH3(addr,size,log_size) \
((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \
& ((size) - 1))
-#define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size)
+#define HASH2(addr,log_size) HASH3(addr, 1 << (log_size), log_size)
struct hash_chain_entry {
word hidden_key;
struct hash_chain_entry * next;
};
-static struct disappearing_link {
+struct disappearing_link {
struct hash_chain_entry prolog;
# define dl_hidden_link prolog.hidden_key
/* Field to be cleared. */
# define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
-# define dl_set_next(x,y) (x)->prolog.next = (struct hash_chain_entry *)(y)
-
+# define dl_set_next(x, y) \
+ (void)((x)->prolog.next = (struct hash_chain_entry *)(y))
word dl_hidden_obj; /* Pointer to object base */
-} **dl_head = 0;
+};
-static signed_word log_dl_table_size = -1;
- /* Binary log of */
- /* current size of array pointed to by dl_head. */
- /* -1 ==> size is 0. */
+struct dl_hashtbl_s {
+ struct disappearing_link **head;
+ signed_word log_size;
+ word entries;
+};
-STATIC word GC_dl_entries = 0;
- /* Number of entries currently in disappearing */
- /* link table. */
+STATIC struct dl_hashtbl_s GC_dl_hashtbl = {
+ /* head */ NULL, /* log_size */ -1, /* entries */ 0 };
+#ifndef GC_LONG_REFS_NOT_NEEDED
+ STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 };
+#endif
-static struct finalizable_object {
+STATIC struct finalizable_object {
struct hash_chain_entry prolog;
# define fo_hidden_base prolog.hidden_key
/* Pointer to object base. */
/* No longer hidden once object */
/* is on finalize_now queue. */
# define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
-# define fo_set_next(x,y) (x)->prolog.next = (struct hash_chain_entry *)(y)
+# define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y))
GC_finalization_proc fo_fn; /* Finalizer. */
ptr_t fo_client_data;
word fo_object_size; /* In bytes. */
finalization_mark_proc fo_mark_proc; /* Mark-through procedure */
-} **fo_head = 0;
+} **GC_fo_head = 0;
STATIC struct finalizable_object * GC_finalize_now = 0;
/* List of objects that should be finalized now. */
static signed_word log_fo_table_size = -1;
-word GC_fo_entries = 0; /* used also in extra/MacOS.c */
-
GC_INNER void GC_push_finalizer_structures(void)
{
- GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word));
- GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word));
+ GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0);
+ GC_ASSERT((word)&GC_fo_head % sizeof(word) == 0);
+ GC_ASSERT((word)&GC_finalize_now % sizeof(word) == 0);
+
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_ASSERT((word)&GC_ll_hashtbl.head % sizeof(word) == 0);
+ GC_push_all((ptr_t)(&GC_ll_hashtbl.head),
+ (ptr_t)(&GC_ll_hashtbl.head) + sizeof(word));
+# endif
+
+ GC_push_all((ptr_t)(&GC_dl_hashtbl.head),
+ (ptr_t)(&GC_dl_hashtbl.head) + sizeof(word));
+ GC_push_all((ptr_t)(&GC_fo_head), (ptr_t)(&GC_fo_head) + sizeof(word));
GC_push_all((ptr_t)(&GC_finalize_now),
(ptr_t)(&GC_finalize_now) + sizeof(word));
}
@@ -94,8 +96,7 @@ GC_INNER void GC_push_finalizer_structures(void)
/* Double the size of a hash table. *size_ptr is the log of its current */
/* size. May be a no-op. */
/* *table is a pointer to an array of hash headers. If we succeed, we */
-/* update both *table and *log_size_ptr. */
-/* Lock is held. */
+/* update both *table and *log_size_ptr. Lock is held. */
STATIC void GC_grow_table(struct hash_chain_entry ***table,
signed_word *log_size_ptr)
{
@@ -137,35 +138,33 @@ GC_API int GC_CALL GC_register_disappearing_link(void * * link)
{
ptr_t base;
- base = (ptr_t)GC_base((void *)link);
+ base = (ptr_t)GC_base(link);
if (base == 0)
ABORT("Bad arg to GC_register_disappearing_link");
return(GC_general_register_disappearing_link(link, base));
}
-GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
- const void * obj)
+STATIC int GC_register_disappearing_link_inner(
+ struct dl_hashtbl_s *dl_hashtbl, void **link,
+ const void *obj)
{
struct disappearing_link *curr_dl;
size_t index;
struct disappearing_link * new_dl;
DCL_LOCK_STATE;
- if (((word)link & (ALIGNMENT-1)) || link == NULL)
- ABORT("Bad arg to GC_general_register_disappearing_link");
LOCK();
- GC_ASSERT(obj != NULL && GC_base((void *)obj) == obj);
- if (log_dl_table_size == -1
- || GC_dl_entries > ((word)1 << log_dl_table_size)) {
- GC_grow_table((struct hash_chain_entry ***)(&dl_head),
- &log_dl_table_size);
- if (GC_print_stats) {
- GC_log_printf("Grew dl table to %u entries\n",
- (1 << (unsigned)log_dl_table_size));
- }
+ GC_ASSERT(obj != NULL && GC_base_C(obj) == obj);
+ if (dl_hashtbl -> log_size == -1
+ || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) {
+ GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head,
+ &dl_hashtbl -> log_size);
+ GC_COND_LOG_PRINTF("Grew dl table to %u entries\n",
+ 1 << (unsigned)dl_hashtbl -> log_size);
}
- index = HASH2(link, log_dl_table_size);
- for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) {
+ index = HASH2(link, dl_hashtbl -> log_size);
+ for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
+ curr_dl = dl_next(curr_dl)) {
if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
UNLOCK();
@@ -185,9 +184,9 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
/* It's not likely we'll make it here, but ... */
LOCK();
/* Recalculate index since the table may grow. */
- index = HASH2(link, log_dl_table_size);
+ index = HASH2(link, dl_hashtbl -> log_size);
/* Check again that our disappearing link not in the table. */
- for (curr_dl = dl_head[index]; curr_dl != 0;
+ for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
curr_dl = dl_next(curr_dl)) {
if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
@@ -202,107 +201,178 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
}
new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
- dl_set_next(new_dl, dl_head[index]);
- dl_head[index] = new_dl;
- GC_dl_entries++;
+ dl_set_next(new_dl, dl_hashtbl -> head[index]);
+ dl_hashtbl -> head[index] = new_dl;
+ dl_hashtbl -> entries++;
UNLOCK();
return GC_SUCCESS;
}
-GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
+GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
+ const void * obj)
{
- struct disappearing_link *curr_dl, *prev_dl;
- size_t index;
- DCL_LOCK_STATE;
+ if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
+ ABORT("Bad arg to GC_general_register_disappearing_link");
+ return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj);
+}
- if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
+#ifdef DBG_HDRS_ALL
+# define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL)
+#else
+# define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl)
+#endif
- LOCK();
- index = HASH2(link, log_dl_table_size);
- prev_dl = 0; curr_dl = dl_head[index];
- while (curr_dl != 0) {
+/* Unregisters given link and returns the link entry to free. */
+/* Assume the lock is held. */
+GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner(
+ struct dl_hashtbl_s *dl_hashtbl, void **link)
+{
+ struct disappearing_link *curr_dl;
+ struct disappearing_link *prev_dl = NULL;
+ size_t index = HASH2(link, dl_hashtbl->log_size);
+
+ for (curr_dl = dl_hashtbl -> head[index]; curr_dl;
+ curr_dl = dl_next(curr_dl)) {
if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
- if (prev_dl == 0) {
- dl_head[index] = dl_next(curr_dl);
+ /* Remove found entry from the table. */
+ if (NULL == prev_dl) {
+ dl_hashtbl -> head[index] = dl_next(curr_dl);
} else {
dl_set_next(prev_dl, dl_next(curr_dl));
}
- GC_dl_entries--;
- UNLOCK();
-# ifdef DBG_HDRS_ALL
- dl_set_next(curr_dl, 0);
-# else
- GC_free((void *)curr_dl);
-# endif
- return(1);
+ dl_hashtbl -> entries--;
+ break;
}
prev_dl = curr_dl;
- curr_dl = dl_next(curr_dl);
}
+ return curr_dl;
+}
+
+GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
+{
+ struct disappearing_link *curr_dl;
+ DCL_LOCK_STATE;
+
+ if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
+
+ LOCK();
+ curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link);
UNLOCK();
- return(0);
+ if (NULL == curr_dl) return 0;
+ FREE_DL_ENTRY(curr_dl);
+ return 1;
}
+#ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj)
+ {
+ if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link)
+ ABORT("Bad arg to GC_register_long_link");
+ return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj);
+ }
+
+ GC_API int GC_CALL GC_unregister_long_link(void * * link)
+ {
+ struct disappearing_link *curr_dl;
+ DCL_LOCK_STATE;
+
+ if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
+
+ LOCK();
+ curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link);
+ UNLOCK();
+ if (NULL == curr_dl) return 0;
+ FREE_DL_ENTRY(curr_dl);
+ return 1;
+ }
+#endif /* !GC_LONG_REFS_NOT_NEEDED */
+
#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
- GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link)
+ /* Moves a link. Assume the lock is held. */
+ STATIC int GC_move_disappearing_link_inner(
+ struct dl_hashtbl_s *dl_hashtbl,
+ void **link, void **new_link)
{
struct disappearing_link *curr_dl, *prev_dl, *new_dl;
size_t curr_index, new_index;
word curr_hidden_link;
word new_hidden_link;
- DCL_LOCK_STATE;
- if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
- ABORT("Bad new_link arg to GC_move_disappearing_link");
- if (((word)link & (ALIGNMENT-1)) != 0)
- return GC_NOT_FOUND; /* Nothing to do. */
-
- LOCK();
/* Find current link. */
- curr_index = HASH2(link, log_dl_table_size);
+ curr_index = HASH2(link, dl_hashtbl -> log_size);
curr_hidden_link = GC_HIDE_POINTER(link);
- prev_dl = 0;
- for (curr_dl = dl_head[curr_index]; curr_dl != 0;
+ prev_dl = NULL;
+ for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl;
curr_dl = dl_next(curr_dl)) {
if (curr_dl -> dl_hidden_link == curr_hidden_link)
break;
prev_dl = curr_dl;
}
- if (curr_dl == 0) {
- UNLOCK();
+ if (NULL == curr_dl) {
return GC_NOT_FOUND;
}
if (link == new_link) {
- UNLOCK();
return GC_SUCCESS; /* Nothing to do. */
}
/* link found; now check new_link not present. */
- new_index = HASH2(new_link, log_dl_table_size);
+ new_index = HASH2(new_link, dl_hashtbl -> log_size);
new_hidden_link = GC_HIDE_POINTER(new_link);
- for (new_dl = dl_head[new_index]; new_dl != 0;
+ for (new_dl = dl_hashtbl -> head[new_index]; new_dl;
new_dl = dl_next(new_dl)) {
if (new_dl -> dl_hidden_link == new_hidden_link) {
/* Target already registered; bail. */
- UNLOCK();
return GC_DUPLICATE;
}
}
/* Remove from old, add to new, update link. */
- if (prev_dl == 0) {
- dl_head[curr_index] = dl_next(curr_dl);
+ if (NULL == prev_dl) {
+ dl_hashtbl -> head[curr_index] = dl_next(curr_dl);
} else {
dl_set_next(prev_dl, dl_next(curr_dl));
}
curr_dl -> dl_hidden_link = new_hidden_link;
- dl_set_next(curr_dl, dl_head[new_index]);
- dl_head[new_index] = curr_dl;
- UNLOCK();
+ dl_set_next(curr_dl, dl_hashtbl -> head[new_index]);
+ dl_hashtbl -> head[new_index] = curr_dl;
return GC_SUCCESS;
}
+
+ GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link)
+ {
+ int result;
+ DCL_LOCK_STATE;
+
+ if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
+ ABORT("Bad new_link arg to GC_move_disappearing_link");
+ if (((word)link & (ALIGNMENT-1)) != 0)
+ return GC_NOT_FOUND; /* Nothing to do. */
+
+ LOCK();
+ result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link);
+ UNLOCK();
+ return result;
+ }
+
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_API int GC_CALL GC_move_long_link(void **link, void **new_link)
+ {
+ int result;
+ DCL_LOCK_STATE;
+
+ if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL)
+ ABORT("Bad new_link arg to GC_move_disappearing_link");
+ if (((word)link & (ALIGNMENT-1)) != 0)
+ return GC_NOT_FOUND; /* Nothing to do. */
+
+ LOCK();
+ result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link);
+ UNLOCK();
+ return result;
+ }
+# endif /* !GC_LONG_REFS_NOT_NEEDED */
#endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */
/* Possible finalization_marker procedures. Note that mark stack */
@@ -332,10 +402,10 @@ STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p)
} else {
scan_limit = target_limit + 1 - sizeof(word);
}
- for (q = p; q <= scan_limit; q += ALIGNMENT) {
+ for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) {
r = *(word *)q;
- if ((ptr_t)r < p || (ptr_t)r > target_limit) {
- GC_PUSH_ONE_HEAP(r, q);
+ if (r < (word)p || r > (word)target_limit) {
+ GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top);
}
}
}
@@ -376,18 +446,17 @@ STATIC void GC_register_finalizer_inner(void * obj,
LOCK();
if (log_fo_table_size == -1
|| GC_fo_entries > ((word)1 << log_fo_table_size)) {
- GC_grow_table((struct hash_chain_entry ***)(&fo_head),
+ GC_grow_table((struct hash_chain_entry ***)&GC_fo_head,
&log_fo_table_size);
- if (GC_print_stats) {
- GC_log_printf("Grew fo table to %u entries\n",
- (1 << (unsigned)log_fo_table_size));
- }
+ GC_COND_LOG_PRINTF("Grew fo table to %u entries\n",
+ 1 << (unsigned)log_fo_table_size);
}
/* in the THREADS case we hold allocation lock. */
base = (ptr_t)obj;
for (;;) {
index = HASH2(base, log_fo_table_size);
- prev_fo = 0; curr_fo = fo_head[index];
+ prev_fo = 0;
+ curr_fo = GC_fo_head[index];
while (curr_fo != 0) {
GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) {
@@ -398,7 +467,7 @@ STATIC void GC_register_finalizer_inner(void * obj,
if (ofn) *ofn = curr_fo -> fo_fn;
/* Delete the structure for base. */
if (prev_fo == 0) {
- fo_head[index] = fo_next(curr_fo);
+ GC_fo_head[index] = fo_next(curr_fo);
} else {
fo_set_next(prev_fo, fo_next(curr_fo));
}
@@ -417,7 +486,7 @@ STATIC void GC_register_finalizer_inner(void * obj,
/* Reinsert it. We deleted it first to maintain */
/* consistency in the event of a signal. */
if (prev_fo == 0) {
- fo_head[index] = curr_fo;
+ GC_fo_head[index] = curr_fo;
} else {
fo_set_next(prev_fo, curr_fo);
}
@@ -477,9 +546,9 @@ STATIC void GC_register_finalizer_inner(void * obj,
new_fo -> fo_client_data = (ptr_t)cd;
new_fo -> fo_object_size = hhdr -> hb_sz;
new_fo -> fo_mark_proc = mp;
- fo_set_next(new_fo, fo_head[index]);
+ fo_set_next(new_fo, GC_fo_head[index]);
GC_fo_entries++;
- fo_head[index] = new_fo;
+ GC_fo_head[index] = new_fo;
UNLOCK();
}
@@ -521,26 +590,42 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
}
#ifndef NO_DEBUGGING
- void GC_dump_finalization(void)
+ STATIC void GC_dump_finalization_links(
+ const struct dl_hashtbl_s *dl_hashtbl)
{
- struct disappearing_link * curr_dl;
- struct finalizable_object * curr_fo;
+ struct disappearing_link *curr_dl;
ptr_t real_ptr, real_link;
- int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
- int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
- int i;
+ size_t dl_size = dl_hashtbl->log_size == -1 ? 0 :
+ 1 << dl_hashtbl->log_size;
+ size_t i;
- GC_printf("Disappearing links:\n");
for (i = 0; i < dl_size; i++) {
- for (curr_dl = dl_head[i]; curr_dl != 0; curr_dl = dl_next(curr_dl)) {
+ for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0;
+ curr_dl = dl_next(curr_dl)) {
real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
- GC_printf("Object: %p, Link:%p\n", real_ptr, real_link);
+ GC_printf("Object: %p, link: %p\n", real_ptr, real_link);
}
}
+ }
+
+ void GC_dump_finalization(void)
+ {
+ struct finalizable_object * curr_fo;
+ size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
+ ptr_t real_ptr;
+ size_t i;
+
+ GC_printf("Disappearing (short) links:\n");
+ GC_dump_finalization_links(&GC_dl_hashtbl);
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_printf("Disappearing long links:\n");
+ GC_dump_finalization_links(&GC_ll_hashtbl);
+# endif
GC_printf("Finalizers:\n");
for (i = 0; i < fo_size; i++) {
- for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
+ for (curr_fo = GC_fo_head[i]; curr_fo != 0;
+ curr_fo = fo_next(curr_fo)) {
real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
GC_printf("Finalizable object: %p\n", real_ptr);
}
@@ -550,12 +635,18 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
#ifndef SMALL_CONFIG
STATIC word GC_old_dl_entries = 0; /* for stats printing */
-#endif
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ STATIC word GC_old_ll_entries = 0;
+# endif
+#endif /* !SMALL_CONFIG */
#ifndef THREADS
/* Global variables to minimize the level of recursion when a client */
/* finalizer allocates memory. */
- STATIC unsigned char GC_finalizer_nested = 0;
+ STATIC int GC_finalizer_nested = 0;
+ /* Only the lowest byte is used, the rest is */
+ /* padding for proper global data alignment */
+ /* required for some compilers (like Watcom). */
STATIC unsigned GC_finalizer_skipped = 0;
/* Checks and updates the level of finalizers recursion. */
@@ -564,7 +655,7 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
/* otherwise returns a pointer to GC_finalizer_nested. */
STATIC unsigned char *GC_check_finalizer_nested(void)
{
- unsigned nesting_level = GC_finalizer_nested;
+ unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested;
if (nesting_level) {
/* We are inside another GC_invoke_finalizers(). */
/* Skip some implicitly-called GC_invoke_finalizers() */
@@ -572,57 +663,100 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
GC_finalizer_skipped = 0;
}
- GC_finalizer_nested = (unsigned char)(nesting_level + 1);
- return &GC_finalizer_nested;
+ *(char *)&GC_finalizer_nested = (char)(nesting_level + 1);
+ return (unsigned char *)&GC_finalizer_nested;
}
#endif /* THREADS */
+#define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \
+ { \
+ size_t i; \
+ size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \
+ 1 << dl_hashtbl->log_size; \
+ for (i = 0; i < dl_size; i++) { \
+ curr_dl = dl_hashtbl -> head[i]; \
+ prev_dl = NULL; \
+ while (curr_dl) {
+
+#define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \
+ prev_dl = curr_dl; \
+ curr_dl = dl_next(curr_dl); \
+ } \
+ } \
+ }
+
+#define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \
+ { \
+ next_dl = dl_next(curr_dl); \
+ if (NULL == prev_dl) { \
+ dl_hashtbl -> head[i] = next_dl; \
+ } else { \
+ dl_set_next(prev_dl, next_dl); \
+ } \
+ GC_clear_mark_bit(curr_dl); \
+ dl_hashtbl -> entries--; \
+ curr_dl = next_dl; \
+ continue; \
+ }
+
+GC_INLINE void GC_make_disappearing_links_disappear(
+ struct dl_hashtbl_s* dl_hashtbl)
+{
+ struct disappearing_link *curr, *prev, *next;
+ ptr_t real_ptr, real_link;
+
+ ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
+ real_ptr = GC_REVEAL_POINTER(curr -> dl_hidden_obj);
+ real_link = GC_REVEAL_POINTER(curr -> dl_hidden_link);
+ if (!GC_is_marked(real_ptr)) {
+ *(word *)real_link = 0;
+ GC_clear_mark_bit(curr);
+ DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
+ }
+ ITERATE_DL_HASHTBL_END(curr, prev)
+}
+
+GC_INLINE void GC_remove_dangling_disappearing_links(
+ struct dl_hashtbl_s* dl_hashtbl)
+{
+ struct disappearing_link *curr, *prev, *next;
+ ptr_t real_link;
+
+ ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
+ real_link = GC_base(GC_REVEAL_POINTER(curr -> dl_hidden_link));
+ if (NULL != real_link && !GC_is_marked(real_link)) {
+ GC_clear_mark_bit(curr);
+ DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
+ }
+ ITERATE_DL_HASHTBL_END(curr, prev)
+}
+
/* Called with held lock (but the world is running). */
/* Cause disappearing links to disappear and unreachable objects to be */
/* enqueued for finalization. */
GC_INNER void GC_finalize(void)
{
- struct disappearing_link * curr_dl, * prev_dl, * next_dl;
struct finalizable_object * curr_fo, * prev_fo, * next_fo;
- ptr_t real_ptr, real_link;
+ ptr_t real_ptr;
size_t i;
- size_t dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
- size_t fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
+ size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
# ifndef SMALL_CONFIG
- /* Save current GC_dl_entries value for stats printing */
- GC_old_dl_entries = GC_dl_entries;
+ /* Save current GC_[dl/ll]_entries value for stats printing */
+ GC_old_dl_entries = GC_dl_hashtbl.entries;
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_old_ll_entries = GC_ll_hashtbl.entries;
+# endif
# endif
- /* Make disappearing links disappear */
- for (i = 0; i < dl_size; i++) {
- curr_dl = dl_head[i];
- prev_dl = 0;
- while (curr_dl != 0) {
- real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
- real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
- if (!GC_is_marked(real_ptr)) {
- *(word *)real_link = 0;
- next_dl = dl_next(curr_dl);
- if (prev_dl == 0) {
- dl_head[i] = next_dl;
- } else {
- dl_set_next(prev_dl, next_dl);
- }
- GC_clear_mark_bit(curr_dl);
- GC_dl_entries--;
- curr_dl = next_dl;
- } else {
- prev_dl = curr_dl;
- curr_dl = dl_next(curr_dl);
- }
- }
- }
+ GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
+
/* Mark all objects reachable via chains of 1 or more pointers */
/* from finalizable objects. */
GC_ASSERT(GC_mark_state == MS_NONE);
for (i = 0; i < fo_size; i++) {
- for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
+ for (curr_fo = GC_fo_head[i]; curr_fo != 0;
+ curr_fo = fo_next(curr_fo)) {
GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
if (!GC_is_marked(real_ptr)) {
@@ -638,7 +772,7 @@ GC_INNER void GC_finalize(void)
/* unreachable. */
GC_bytes_finalized = 0;
for (i = 0; i < fo_size; i++) {
- curr_fo = fo_head[i];
+ curr_fo = GC_fo_head[i];
prev_fo = 0;
while (curr_fo != 0) {
real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
@@ -649,7 +783,7 @@ GC_INNER void GC_finalize(void)
/* Delete from hash table */
next_fo = fo_next(curr_fo);
if (prev_fo == 0) {
- fo_head[i] = next_fo;
+ GC_fo_head[i] = next_fo;
} else {
fo_set_next(prev_fo, next_fo);
}
@@ -664,7 +798,7 @@ GC_INNER void GC_finalize(void)
GC_bytes_finalized +=
curr_fo -> fo_object_size
+ sizeof(struct finalizable_object);
- GC_ASSERT(GC_is_marked(GC_base((ptr_t)curr_fo)));
+ GC_ASSERT(GC_is_marked(GC_base(curr_fo)));
curr_fo = next_fo;
} else {
prev_fo = curr_fo;
@@ -712,9 +846,9 @@ GC_INNER void GC_finalize(void)
curr_fo->fo_object_size + sizeof(struct finalizable_object);
i = HASH2(real_ptr, log_fo_table_size);
- fo_set_next (curr_fo, fo_head[i]);
+ fo_set_next (curr_fo, GC_fo_head[i]);
GC_fo_entries++;
- fo_head[i] = curr_fo;
+ GC_fo_head[i] = curr_fo;
curr_fo = prev_fo;
}
}
@@ -724,28 +858,12 @@ GC_INNER void GC_finalize(void)
}
}
- /* Remove dangling disappearing links. */
- for (i = 0; i < dl_size; i++) {
- curr_dl = dl_head[i];
- prev_dl = 0;
- while (curr_dl != 0) {
- real_link = GC_base(GC_REVEAL_POINTER(curr_dl -> dl_hidden_link));
- if (real_link != 0 && !GC_is_marked(real_link)) {
- next_dl = dl_next(curr_dl);
- if (prev_dl == 0) {
- dl_head[i] = next_dl;
- } else {
- dl_set_next(prev_dl, next_dl);
- }
- GC_clear_mark_bit(curr_dl);
- GC_dl_entries--;
- curr_dl = next_dl;
- } else {
- prev_dl = curr_dl;
- curr_dl = dl_next(curr_dl);
- }
- }
- }
+ GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
+ GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
+# endif
+
if (GC_fail_count) {
/* Don't prevent running finalizers if there has been an allocation */
/* failure recently. */
@@ -767,10 +885,10 @@ GC_INNER void GC_finalize(void)
register int i;
int fo_size;
- fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
+ fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
GC_bytes_finalized = 0;
for (i = 0; i < fo_size; i++) {
- curr_fo = fo_head[i];
+ curr_fo = GC_fo_head[i];
prev_fo = 0;
while (curr_fo != 0) {
real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
@@ -780,7 +898,7 @@ GC_INNER void GC_finalize(void)
/* Delete from hash table */
next_fo = fo_next(curr_fo);
if (prev_fo == 0) {
- fo_head[i] = next_fo;
+ GC_fo_head[i] = next_fo;
} else {
fo_set_next(prev_fo, next_fo);
}
@@ -791,7 +909,7 @@ GC_INNER void GC_finalize(void)
GC_finalize_now = curr_fo;
/* unhide object pointer so any future collections will */
- /* see it. */
+ /* see it. */
curr_fo -> fo_hidden_base =
(word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
GC_bytes_finalized +=
@@ -836,7 +954,7 @@ GC_INNER void GC_finalize(void)
#endif /* !JAVA_FINALIZATION_NOT_NEEDED */
/* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
-/* finalizers can only be called from some kind of `safe state' and */
+/* finalizers can only be called from some kind of "safe state" and */
/* getting into that safe state is expensive.) */
GC_API int GC_CALL GC_should_invoke_finalizers(void)
{
@@ -889,10 +1007,6 @@ GC_API int GC_CALL GC_invoke_finalizers(void)
return count;
}
-/* All accesses to it should be synchronized to avoid data races. */
-GC_finalizer_notifier_proc GC_finalizer_notifier =
- (GC_finalizer_notifier_proc)0;
-
static GC_word last_finalizer_notification = 0;
GC_INNER void GC_notify_or_invoke_finalizers(void)
@@ -967,39 +1081,34 @@ GC_INNER void GC_notify_or_invoke_finalizers(void)
(*notifier_fn)(); /* Invoke the notifier */
}
-GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn,
- void * client_data)
-{
- void * result;
- DCL_LOCK_STATE;
-
-# ifdef THREADS
- LOCK();
- /* FIXME - This looks wrong!! */
- SET_LOCK_HOLDER();
-# endif
- result = (*fn)(client_data);
-# ifdef THREADS
-# ifndef GC_ASSERTIONS
- UNSET_LOCK_HOLDER();
-# endif /* o.w. UNLOCK() does it implicitly */
- UNLOCK();
-# endif
- return(result);
-}
-
#ifndef SMALL_CONFIG
+# ifndef GC_LONG_REFS_NOT_NEEDED
+# define IF_LONG_REFS_PRESENT_ELSE(x,y) (x)
+# else
+# define IF_LONG_REFS_PRESENT_ELSE(x,y) (y)
+# endif
+
GC_INNER void GC_print_finalization_stats(void)
{
- struct finalizable_object *fo = GC_finalize_now;
+ struct finalizable_object *fo;
unsigned long ready = 0;
- GC_log_printf(
- "%lu finalization table entries; %lu disappearing links alive\n",
- (unsigned long)GC_fo_entries, (unsigned long)GC_dl_entries);
- for (; 0 != fo; fo = fo_next(fo)) ++ready;
- GC_log_printf("%lu objects are eligible for immediate finalization; "
- "%ld links cleared\n",
- ready, (long)GC_old_dl_entries - (long)GC_dl_entries);
+ GC_log_printf("%lu finalization entries;"
+ " %lu/%lu short/long disappearing links alive\n",
+ (unsigned long)GC_fo_entries,
+ (unsigned long)GC_dl_hashtbl.entries,
+ (unsigned long)IF_LONG_REFS_PRESENT_ELSE(
+ GC_ll_hashtbl.entries, 0));
+
+ for (fo = GC_finalize_now; 0 != fo; fo = fo_next(fo))
+ ++ready;
+ GC_log_printf("%lu finalization-ready objects;"
+ " %ld/%ld short/long links cleared\n",
+ ready,
+ (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries,
+ (long)IF_LONG_REFS_PRESENT_ELSE(
+ GC_old_ll_entries - GC_ll_hashtbl.entries, 0));
}
#endif /* !SMALL_CONFIG */
+
+#endif /* !GC_NO_FINALIZATION */
diff --git a/fnlz_mlc.c b/fnlz_mlc.c
index 12767aa5..0dad28a6 100644
--- a/fnlz_mlc.c
+++ b/fnlz_mlc.c
@@ -92,7 +92,8 @@ GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
lb += sizeof(void *);
GC_ASSERT(done_init);
- if (EXPECT(SMALL_OBJ(lb), TRUE)) {
+ if (SMALL_OBJ(lb)) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
lg = GC_size_map[lb];
opp = &GC_finalized_objfreelist[lg];
LOCK();
@@ -102,12 +103,15 @@ GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
op = GC_generic_malloc((word)lb, GC_finalized_kind);
if (NULL == op)
return NULL;
+ /* GC_generic_malloc has extended the size map for us. */
+ lg = GC_size_map[lb];
} else {
*opp = obj_link(op);
obj_link(op) = 0;
GC_bytes_allocd += GRANULES_TO_BYTES(lg);
UNLOCK();
}
+ GC_ASSERT(lg > 0);
((const void **)op)[GRANULES_TO_WORDS(lg) - 1] = fclos;
} else {
size_t op_sz;
diff --git a/gc_cpp.cc b/gc_cpp.cc
index 229a0c3e..0f6c9d2b 100644
--- a/gc_cpp.cc
+++ b/gc_cpp.cc
@@ -12,7 +12,7 @@
This implementation module for gc_c++.h provides an implementation of
the global operators "new" and "delete" that calls the Boehm
allocator. All objects allocated by this implementation will be
-non-collectable but part of the root set of the collector.
+uncollectible but part of the root set of the collector.
You should ensure (using implementation-dependent techniques) that the
linker finds this module before the library that defines the default
@@ -20,7 +20,7 @@ built-in "new" and "delete".
**************************************************************************/
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef GC_BUILD
@@ -29,22 +29,36 @@ built-in "new" and "delete".
#include "gc_cpp.h"
-void* operator new( size_t size ) {
+#if !defined(GC_NEW_DELETE_NEED_THROW) && defined(__GNUC__) \
+ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+# define GC_NEW_DELETE_NEED_THROW
+#endif
+
+#ifdef GC_NEW_DELETE_NEED_THROW
+# include <new> /* for std::bad_alloc */
+# define GC_DECL_NEW_THROW throw(std::bad_alloc)
+# define GC_DECL_DELETE_THROW throw()
+#else
+# define GC_DECL_NEW_THROW /* empty */
+# define GC_DECL_DELETE_THROW /* empty */
+#endif /* !GC_NEW_DELETE_NEED_THROW */
+
+void* operator new( size_t size ) GC_DECL_NEW_THROW {
return GC_MALLOC_UNCOLLECTABLE(size);
}
#if !defined(__CYGWIN__)
- void operator delete( void* obj ) {
+ void operator delete( void* obj ) GC_DECL_DELETE_THROW {
GC_FREE(obj);
}
#endif /* !__CYGWIN__ */
#ifdef GC_OPERATOR_NEW_ARRAY
- void* operator new[]( size_t size ) {
+ void* operator new[]( size_t size ) GC_DECL_NEW_THROW {
return GC_MALLOC_UNCOLLECTABLE(size);
}
- void operator delete[]( void* obj ) {
+ void operator delete[]( void* obj ) GC_DECL_DELETE_THROW {
GC_FREE(obj);
}
#endif /* GC_OPERATOR_NEW_ARRAY */
@@ -53,7 +67,7 @@ void* operator new( size_t size ) {
// This new operator is used by VC++ in case of Debug builds!
void* operator new( size_t size, int /* nBlockUse */,
- const char * szFileName, int nLine )
+ const char * szFileName, int nLine ) GC_DECL_NEW_THROW
{
# ifndef GC_DEBUG
return GC_malloc_uncollectable(size);
@@ -65,7 +79,7 @@ void* operator new( size_t size ) {
# if _MSC_VER > 1020
// This new operator is used by VC++ 7.0 and later in Debug builds.
void* operator new[]( size_t size, int nBlockUse,
- const char* szFileName, int nLine )
+ const char* szFileName, int nLine ) GC_DECL_NEW_THROW
{
return operator new(size, nBlockUse, szFileName, nLine);
}
diff --git a/gcj_mlc.c b/gcj_mlc.c
index 5bbcbc69..78950e25 100644
--- a/gcj_mlc.c
+++ b/gcj_mlc.c
@@ -88,8 +88,8 @@ GC_API void GC_CALL GC_init_gcj_malloc(int mp_index,
# else
ignore_gcj_info = (0 != GETENV("GC_IGNORE_GCJ_INFO"));
# endif
- if (GC_print_stats && ignore_gcj_info) {
- GC_log_printf("Gcj-style type information is disabled!\n");
+ if (ignore_gcj_info) {
+ GC_COND_LOG_PRINTF("Gcj-style type information is disabled!\n");
}
GC_ASSERT(GC_mark_procs[mp_index] == (GC_mark_proc)0); /* unused */
GC_mark_procs[mp_index] = (GC_mark_proc)(word)mp;
@@ -168,6 +168,7 @@ static void maybe_finalize(void)
word lg;
DCL_LOCK_STATE;
+ GC_DBG_COLLECT_AT_MALLOC(lb);
if(SMALL_OBJ(lb)) {
lg = GC_size_map[lb];
opp = &(GC_gcjobjfreelist[lg]);
@@ -219,10 +220,8 @@ GC_API void * GC_CALL GC_debug_gcj_malloc(size_t lb,
if (result == 0) {
GC_oom_func oom_fn = GC_oom_fn;
UNLOCK();
- GC_err_printf("GC_debug_gcj_malloc(%ld, %p) returning NULL (",
- (unsigned long)lb, ptr_to_struct_containing_descr);
- GC_err_puts(s);
- GC_err_printf(":%d)\n", i);
+ GC_err_printf("GC_debug_gcj_malloc(%lu, %p) returning NULL (%s:%d)\n",
+ (unsigned long)lb, ptr_to_struct_containing_descr, s, i);
return((*oom_fn)(lb));
}
*((void **)((ptr_t)result + sizeof(oh))) = ptr_to_struct_containing_descr;
@@ -243,6 +242,7 @@ GC_API void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb,
word lg;
DCL_LOCK_STATE;
+ GC_DBG_COLLECT_AT_MALLOC(lb);
if(SMALL_OBJ(lb)) {
lg = GC_size_map[lb];
opp = &(GC_gcjobjfreelist[lg]);
diff --git a/headers.c b/headers.c
index de82c204..8e92e384 100644
--- a/headers.c
+++ b/headers.c
@@ -122,7 +122,7 @@ GC_INNER ptr_t GC_scratch_alloc(size_t bytes)
bytes += GRANULE_BYTES-1;
bytes &= ~(GRANULE_BYTES-1);
scratch_free_ptr += bytes;
- if (scratch_free_ptr <= GC_scratch_end_ptr) {
+ if ((word)scratch_free_ptr <= (word)GC_scratch_end_ptr) {
return(result);
}
{
@@ -144,8 +144,7 @@ GC_INNER ptr_t GC_scratch_alloc(size_t bytes)
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_log_printf("Out of memory - trying to allocate less\n");
+ WARN("Out of memory - trying to allocate less\n", 0);
scratch_free_ptr -= bytes;
bytes_to_get = bytes;
# ifdef USE_MMAP
@@ -196,6 +195,10 @@ GC_INNER void GC_init_headers(void)
register unsigned i;
GC_all_nils = (bottom_index *)GC_scratch_alloc((word)sizeof(bottom_index));
+ if (GC_all_nils == NULL) {
+ GC_err_printf("Insufficient memory for GC_all_nils\n");
+ EXIT();
+ }
BZERO(GC_all_nils, sizeof(bottom_index));
for (i = 0; i < TOP_SZ; i++) {
GC_top_index[i] = GC_all_nils;
@@ -276,11 +279,11 @@ GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */)
struct hblk * hbp;
word i;
- for (hbp = h; (char *)hbp < (char *)h + sz; hbp += BOTTOM_SZ) {
+ for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) {
if (!get_index((word) hbp)) return(FALSE);
}
if (!get_index((word)h + sz - 1)) return(FALSE);
- for (hbp = h + 1; (char *)hbp < (char *)h + sz; hbp += 1) {
+ for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) {
i = HBLK_PTR_DIFF(hbp, h);
SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i));
}
@@ -300,7 +303,7 @@ GC_INNER void GC_remove_header(struct hblk *h)
GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */)
{
register struct hblk * hbp;
- for (hbp = h+1; (char *)hbp < (char *)h + sz; hbp += 1) {
+ for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) {
SET_HDR(hbp, 0);
}
}
diff --git a/include/cord.h b/include/cord.h
index 05eb4c4c..d3d555b6 100644
--- a/include/cord.h
+++ b/include/cord.h
@@ -326,7 +326,7 @@ CORD_API size_t CORD_rchr(CORD x, size_t i, int c);
/* the correct buffer size. */
/* 4. Most of the conversions are implement through the native */
/* vsprintf. Hence they are usually no faster, and */
-/* idiosyncracies of the native printf are preserved. However, */
+/* idiosyncrasies of the native printf are preserved. However, */
/* CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied; */
/* the result shares the original structure. This may make them */
/* very efficient in some unusual applications. */
diff --git a/include/ec.h b/include/ec.h
index b88e7035..30806ed2 100644
--- a/include/ec.h
+++ b/include/ec.h
@@ -52,16 +52,14 @@ void CORD_ec_flush_buf(CORD_ec x);
# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord)
/* Initialize an extensible cord. */
-# define CORD_ec_init(x) ((x)[0].ec_cord = 0, (x)[0].ec_bufptr = (x)[0].ec_buf)
+#define CORD_ec_init(x) \
+ ((x)[0].ec_cord = 0, (void)((x)[0].ec_bufptr = (x)[0].ec_buf))
/* Append a character to an extensible cord. */
-# define CORD_ec_append(x, c) \
- { \
- if ((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ) { \
- CORD_ec_flush_buf(x); \
- } \
- *((x)[0].ec_bufptr)++ = (c); \
- }
+#define CORD_ec_append(x, c) \
+ (((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ ? \
+ (CORD_ec_flush_buf(x), 0) : 0), \
+ (void)(*(x)[0].ec_bufptr++ = (c)))
/* Append a cord to an extensible cord. Structure remains shared with */
/* original. */
diff --git a/include/gc.h b/include/gc.h
index 6ae6f772..a3a13b90 100644
--- a/include/gc.h
+++ b/include/gc.h
@@ -44,6 +44,7 @@
extern "C" {
#endif
+typedef void * GC_PTR; /* preserved only for backward compatibility */
/* Define word and signed_word to be unsigned and signed types of the */
/* size as char * or void *. There seems to be no way to do this */
@@ -72,7 +73,8 @@ GC_API unsigned GC_CALL GC_get_version(void);
/* Public read-only variables */
/* The supplied getter functions are preferred for new code. */
-GC_API GC_word GC_gc_no;/* Counter incremented per collection. */
+GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no;
+ /* Counter incremented per collection. */
/* Includes empty GCs at startup. */
GC_API GC_word GC_CALL GC_get_gc_no(void);
/* GC_get_gc_no() is unsynchronized, so */
@@ -80,18 +82,22 @@ GC_API GC_word GC_CALL GC_get_gc_no(void);
/* avoid data races on multiprocessors. */
#ifdef GC_THREADS
- GC_API int GC_parallel;
+ GC_API GC_ATTR_DEPRECATED int GC_parallel;
/* GC is parallelized for performance on */
/* multiprocessors. Currently set only */
/* implicitly if collector is built with */
/* PARALLEL_MARK defined and if either: */
/* Env variable GC_NPROC is set to > 1, or */
/* GC_NPROC is not set and this is an MP. */
- /* If GC_parallel is set, incremental */
+ /* If GC_parallel is on (non-zero), incremental */
/* collection is only partially functional, */
- /* and may not be desirable. This getter does */
+ /* and may not be desirable. The getter does */
/* not use or need synchronization (i.e. */
- /* acquiring the GC lock). */
+ /* acquiring the GC lock). Starting from */
+ /* GC v7.3, GC_parallel value is equal to the */
+ /* number of marker threads minus one (i.e. */
+ /* number of existing parallel marker threads */
+ /* excluding the initiating one). */
GC_API int GC_CALL GC_get_parallel(void);
#endif
@@ -100,7 +106,7 @@ GC_API GC_word GC_CALL GC_get_gc_no(void);
/* The supplied setter and getter functions are preferred for new code. */
typedef void * (GC_CALLBACK * GC_oom_func)(size_t /* bytes_requested */);
-GC_API GC_oom_func GC_oom_fn;
+GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn;
/* When there is insufficient memory to satisfy */
/* an allocation request, we return */
/* (*GC_oom_fn)(size). By default this just */
@@ -110,11 +116,11 @@ GC_API GC_oom_func GC_oom_fn;
/* object. GC_oom_fn must not be 0. */
/* Both the supplied setter and the getter */
/* acquire the GC lock (to avoid data races). */
-GC_API void GC_CALL GC_set_oom_fn(GC_oom_func);
+GC_API void GC_CALL GC_set_oom_fn(GC_oom_func) GC_ATTR_NONNULL(1);
GC_API GC_oom_func GC_CALL GC_get_oom_fn(void);
typedef void (GC_CALLBACK * GC_on_heap_resize_proc)(GC_word /* new_size */);
-GC_API GC_on_heap_resize_proc GC_on_heap_resize;
+GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize;
/* Invoked when the heap grows or shrinks. */
/* Called with the world stopped (and the */
/* allocation lock held). May be 0. */
@@ -123,7 +129,7 @@ GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void);
/* Both the supplied setter and the getter */
/* acquire the GC lock (to avoid data races). */
-GC_API int GC_find_leak;
+GC_API GC_ATTR_DEPRECATED int GC_find_leak;
/* Do not actually garbage collect, but simply */
/* report inaccessible memory that was not */
/* deallocated with GC_free. Initial value */
@@ -134,7 +140,7 @@ GC_API int GC_find_leak;
GC_API void GC_CALL GC_set_find_leak(int);
GC_API int GC_CALL GC_get_find_leak(void);
-GC_API int GC_all_interior_pointers;
+GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers;
/* Arrange for pointers to object interiors to */
/* be recognized as valid. Typically should */
/* not be changed after GC initialization (in */
@@ -150,7 +156,7 @@ GC_API int GC_all_interior_pointers;
GC_API void GC_CALL GC_set_all_interior_pointers(int);
GC_API int GC_CALL GC_get_all_interior_pointers(void);
-GC_API int GC_finalize_on_demand;
+GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand;
/* If nonzero, finalizers will only be run in */
/* response to an explicit GC_invoke_finalizers */
/* call. The default is determined by whether */
@@ -160,7 +166,7 @@ GC_API int GC_finalize_on_demand;
GC_API void GC_CALL GC_set_finalize_on_demand(int);
GC_API int GC_CALL GC_get_finalize_on_demand(void);
-GC_API int GC_java_finalization;
+GC_API GC_ATTR_DEPRECATED int GC_java_finalization;
/* Mark objects reachable from finalizable */
/* objects in a separate post-pass. This makes */
/* it a bit safer to use non-topologically- */
@@ -173,7 +179,7 @@ GC_API void GC_CALL GC_set_java_finalization(int);
GC_API int GC_CALL GC_get_java_finalization(void);
typedef void (GC_CALLBACK * GC_finalizer_notifier_proc)(void);
-GC_API GC_finalizer_notifier_proc GC_finalizer_notifier;
+GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier;
/* Invoked by the collector when there are */
/* objects to be finalized. Invoked at most */
/* once per GC cycle. Never invoked unless */
@@ -186,23 +192,28 @@ GC_API GC_finalizer_notifier_proc GC_finalizer_notifier;
GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc);
GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void);
-GC_API int GC_dont_gc; /* != 0 ==> Don't collect. In versions 6.2a1+, */
+GC_API
+# ifndef GC_DONT_GC
+ GC_ATTR_DEPRECATED
+# endif
+ int GC_dont_gc; /* != 0 ==> Don't collect. In versions 6.2a1+, */
/* this overrides explicit GC_gcollect() calls. */
/* Used as a counter, so that nested enabling */
/* and disabling work correctly. Should */
/* normally be updated with GC_enable() and */
- /* GC_disable() calls. */
- /* Direct assignment to GC_dont_gc is */
- /* deprecated. */
+ /* GC_disable() calls. Direct assignment to */
+ /* GC_dont_gc is deprecated. To check whether */
+ /* GC is disabled, GC_is_disabled() is */
+ /* preferred for new code. */
-GC_API int GC_dont_expand;
- /* Don't expand the heap unless explicitly */
+GC_API GC_ATTR_DEPRECATED int GC_dont_expand;
+ /* Do not expand the heap unless explicitly */
/* requested or forced to. The setter and */
/* getter are unsynchronized. */
GC_API void GC_CALL GC_set_dont_expand(int);
GC_API int GC_CALL GC_get_dont_expand(void);
-GC_API int GC_use_entire_heap;
+GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap;
/* Causes the non-incremental collector to use the */
/* entire heap before collecting. This was the only */
/* option for GC versions < 5.0. This sometimes */
@@ -213,7 +224,8 @@ GC_API int GC_use_entire_heap;
/* frequencies, and hence fewer instructions executed */
/* in the collector. */
-GC_API int GC_full_freq; /* Number of partial collections between */
+GC_API GC_ATTR_DEPRECATED int GC_full_freq;
+ /* Number of partial collections between */
/* full collections. Matters only if */
/* GC_incremental is set. */
/* Full collections are also triggered if */
@@ -229,7 +241,7 @@ GC_API int GC_full_freq; /* Number of partial collections between */
GC_API void GC_CALL GC_set_full_freq(int);
GC_API int GC_CALL GC_get_full_freq(void);
-GC_API GC_word GC_non_gc_bytes;
+GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes;
/* Bytes not considered candidates for */
/* collection. Used only to control scheduling */
/* of collections. Updated by */
@@ -242,7 +254,7 @@ GC_API GC_word GC_non_gc_bytes;
GC_API void GC_CALL GC_set_non_gc_bytes(GC_word);
GC_API GC_word GC_CALL GC_get_non_gc_bytes(void);
-GC_API int GC_no_dls;
+GC_API GC_ATTR_DEPRECATED int GC_no_dls;
/* Don't register dynamic library data segments. */
/* Wizards only. Should be used only if the */
/* application explicitly registers all roots. */
@@ -254,7 +266,7 @@ GC_API int GC_no_dls;
GC_API void GC_CALL GC_set_no_dls(int);
GC_API int GC_CALL GC_get_no_dls(void);
-GC_API GC_word GC_free_space_divisor;
+GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor;
/* We try to make sure that we allocate at */
/* least N/GC_free_space_divisor bytes between */
/* collections, where N is twice the number */
@@ -274,7 +286,7 @@ GC_API GC_word GC_free_space_divisor;
GC_API void GC_CALL GC_set_free_space_divisor(GC_word);
GC_API GC_word GC_CALL GC_get_free_space_divisor(void);
-GC_API GC_word GC_max_retries;
+GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries;
/* The maximum number of GCs attempted before */
/* reporting out of memory after heap */
/* expansion fails. Initially 0. */
@@ -286,7 +298,8 @@ GC_API void GC_CALL GC_set_max_retries(GC_word);
GC_API GC_word GC_CALL GC_get_max_retries(void);
-GC_API char *GC_stackbottom; /* Cool end of user stack. */
+GC_API GC_ATTR_DEPRECATED char *GC_stackbottom;
+ /* Cool end of user stack. */
/* May be set in the client prior to */
/* calling any GC_ routines. This */
/* avoids some overhead, and */
@@ -296,9 +309,13 @@ GC_API char *GC_stackbottom; /* Cool end of user stack. */
/* automatically. */
/* For multi-threaded code, this is the */
/* cold end of the stack for the */
- /* primordial thread. */
+ /* primordial thread. Portable clients */
+ /* should use GC_get_stack_base(), */
+ /* GC_call_with_gc_active() and */
+ /* GC_register_my_thread() instead. */
-GC_API int GC_dont_precollect; /* Don't collect as part of GC */
+GC_API GC_ATTR_DEPRECATED int GC_dont_precollect;
+ /* Do not collect as part of GC */
/* initialization. Should be set only */
/* if the client wants a chance to */
/* manually initialize the root set */
@@ -311,7 +328,7 @@ GC_API int GC_dont_precollect; /* Don't collect as part of GC */
GC_API void GC_CALL GC_set_dont_precollect(int);
GC_API int GC_CALL GC_get_dont_precollect(void);
-GC_API unsigned long GC_time_limit;
+GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit;
/* If incremental collection is enabled, */
/* We try to terminate collections */
/* after this many milliseconds. Not a */
@@ -349,6 +366,31 @@ GC_API void GC_CALL GC_set_pages_executable(int);
/* use or need synchronization (i.e. acquiring the allocator lock). */
GC_API int GC_CALL GC_get_pages_executable(void);
+/* Overrides the default handle-fork mode. Non-zero value means GC */
+/* should install proper pthread_atfork handlers. Has effect only if */
+/* called before GC_INIT. Clients should invoke GC_set_handle_fork */
+/* with non-zero argument if going to use fork with GC functions called */
+/* in the forked child. (Note that such client and atfork handlers */
+/* activities are not fully POSIX-compliant.) GC_set_handle_fork */
+/* instructs GC_init to setup GC fork handlers using pthread_atfork, */
+/* the latter might fail (or, even, absent on some targets) causing */
+/* abort at GC initialization. Starting from 7.3alpha3, problems with */
+/* missing (or failed) pthread_atfork() could be avoided by invocation */
+/* of GC_set_handle_fork(-1) at application start-up and surrounding */
+/* each fork() with the relevant GC_atfork_prepare/parent/child calls. */
+GC_API void GC_CALL GC_set_handle_fork(int);
+
+/* Routines to handle POSIX fork() manually (no-op if handled */
+/* automatically). GC_atfork_prepare should be called immediately */
+/* before fork(); GC_atfork_parent should be invoked just after fork in */
+/* the branch that corresponds to parent process (i.e., fork result is */
+/* non-zero); GC_atfork_child is to be called immediately in the child */
+/* branch (i.e., fork result is 0). Note that GC_atfork_child() call */
+/* should, of course, precede GC_start_mark_threads call (if any). */
+GC_API void GC_CALL GC_atfork_prepare(void);
+GC_API void GC_CALL GC_atfork_parent(void);
+GC_API void GC_CALL GC_atfork_child(void);
+
/* Initialize the collector. Portable clients should call GC_INIT() */
/* from the main program instead. */
GC_API void GC_CALL GC_init(void);
@@ -359,29 +401,30 @@ GC_API void GC_CALL GC_init(void);
/* new object is cleared. GC_malloc_stubborn promises that no changes */
/* to the object will occur after GC_end_stubborn_change has been */
/* called on the result of GC_malloc_stubborn. GC_malloc_uncollectable */
-/* allocates an object that is scanned for pointers to collectable */
-/* objects, but is not itself collectable. The object is scanned even */
+/* allocates an object that is scanned for pointers to collectible */
+/* objects, but is not itself collectible. The object is scanned even */
/* if it does not appear to be reachable. GC_malloc_uncollectable and */
/* GC_free called on the resulting object implicitly update */
/* GC_non_gc_bytes appropriately. */
/* Note that the GC_malloc_stubborn support doesn't really exist */
/* anymore. MANUAL_VDB provides comparable functionality. */
-GC_API void * GC_CALL GC_malloc(size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_malloc_atomic(size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API char * GC_CALL GC_strdup(const char *) GC_ATTR_MALLOC;
-GC_API char * GC_CALL GC_strndup(const char *, size_t) GC_ATTR_MALLOC;
-GC_API void * GC_CALL GC_malloc_uncollectable(size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_malloc_stubborn(size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_atomic(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+ GC_strndup(const char *, size_t) GC_ATTR_NONNULL(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_uncollectable(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_stubborn(size_t /* size_in_bytes */);
/* GC_memalign() is not well tested. */
-GC_API void * GC_CALL GC_memalign(size_t /* align */, size_t /* lb */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2) void * GC_CALL
+ GC_memalign(size_t /* align */, size_t /* lb */);
GC_API int GC_CALL GC_posix_memalign(void ** /* memptr */, size_t /* align */,
- size_t /* lb */);
+ size_t /* lb */) GC_ATTR_NONNULL(1);
/* Explicitly deallocate an object. Dangerous if used incorrectly. */
/* Requires a pointer to the base of an object. */
@@ -404,12 +447,12 @@ GC_API void GC_CALL GC_free(void *);
/* allowing more than one stubborn object to be changed at once, but it */
/* is acceptable to do so. The same applies to dropping stubborn */
/* objects that are still changeable. */
-GC_API void GC_CALL GC_change_stubborn(const void *);
-GC_API void GC_CALL GC_end_stubborn_change(const void *);
+GC_API void GC_CALL GC_change_stubborn(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_end_stubborn_change(const void *) GC_ATTR_NONNULL(1);
/* Return a pointer to the base (lowest address) of an object given */
/* a pointer to a location within the object. */
-/* I.e. map an interior pointer to the corresponding bas pointer. */
+/* I.e., map an interior pointer to the corresponding base pointer. */
/* Note that with debugging allocation, this returns a pointer to the */
/* actual base of the object, i.e. the debug information, not to */
/* the base of the user object. */
@@ -420,16 +463,16 @@ GC_API void GC_CALL GC_end_stubborn_change(const void *);
/* GC_free. */
GC_API void * GC_CALL GC_base(void * /* displaced_pointer */);
-/* Return TRUE if and only if the argument points to somewhere in GC */
-/* heap. Primary use is as a fast alternative to GC_base to check */
-/* whether the pointed object is allocated by GC or not. It is assumed */
-/* that the collector is already initialized. */
+/* Return non-zero (TRUE) if and only if the argument points to */
+/* somewhere in GC heap. Primary use is as a fast alternative to */
+/* GC_base to check whether the pointed object is allocated by GC */
+/* or not. It is assumed that the collector is already initialized. */
GC_API int GC_CALL GC_is_heap_ptr(const void *);
/* Given a pointer to the base of an object, return its size in bytes. */
/* The returned size may be slightly larger than what was originally */
/* requested. */
-GC_API size_t GC_CALL GC_size(const void * /* object_addr */);
+GC_API size_t GC_CALL GC_size(const void * /* obj_addr */) GC_ATTR_NONNULL(1);
/* For compatibility with C library. This is occasionally faster than */
/* a malloc followed by a bcopy. But if you rely on that, either here */
@@ -459,16 +502,15 @@ GC_API void GC_CALL GC_set_max_heap_size(GC_word /* n */);
/* need not be scanned. This is sometimes important if the application */
/* maps large read/write files into the address space, which could be */
/* mistaken for dynamic library data segments on some systems. */
-/* The section (referred to by low_address) must be pointer-aligned. */
-/* low_address must not be greater than high_address_plus_1. */
+/* Both section start and end are not needed to be pointer-aligned. */
GC_API void GC_CALL GC_exclude_static_roots(void * /* low_address */,
- void * /* high_address_plus_1 */);
+ void * /* high_address_plus_1 */);
/* Clear the set of root segments. Wizards only. */
GC_API void GC_CALL GC_clear_roots(void);
/* Add a root segment. Wizards only. */
-/* The segment (referred to by low_address) must be pointer-aligned. */
+/* Both segment start and end are not needed to be pointer-aligned. */
/* low_address must not be greater than high_address_plus_1. */
GC_API void GC_CALL GC_add_roots(void * /* low_address */,
void * /* high_address_plus_1 */);
@@ -520,13 +562,15 @@ GC_API void GC_CALL GC_gcollect_and_unmap(void);
/* GC_try_to_collect() returns 0 if the collection was aborted (or the */
/* collections are disabled), 1 if it succeeded. */
typedef int (GC_CALLBACK * GC_stop_func)(void);
-GC_API int GC_CALL GC_try_to_collect(GC_stop_func /* stop_func */);
+GC_API int GC_CALL GC_try_to_collect(GC_stop_func /* stop_func */)
+ GC_ATTR_NONNULL(1);
/* Set and get the default stop_func. The default stop_func is used by */
/* GC_gcollect() and by implicitly trigged collections (except for the */
/* case when handling out of memory). Must not be 0. */
/* Both the setter and getter acquire the GC lock to avoid data races. */
-GC_API void GC_CALL GC_set_stop_func(GC_stop_func /* stop_func */);
+GC_API void GC_CALL GC_set_stop_func(GC_stop_func /* stop_func */)
+ GC_ATTR_NONNULL(1);
GC_API GC_stop_func GC_CALL GC_get_stop_func(void);
/* Return the number of bytes in the heap. Excludes collector private */
@@ -535,7 +579,8 @@ GC_API GC_stop_func GC_CALL GC_get_stop_func(void);
/* that were allocated but never written. */
/* This is an unsynchronized getter, so it should be called typically */
/* with the GC lock held to avoid data races on multiprocessors (the */
-/* alternative is to use GC_get_heap_usage_safe API call instead). */
+/* alternative is to use GC_get_heap_usage_safe or GC_get_prof_stats */
+/* API calls instead). */
/* This getter remains lock-free (unsynchronized) for compatibility */
/* reason since some existing clients call it from a GC callback */
/* holding the allocator lock. (This API function and the following */
@@ -569,22 +614,81 @@ GC_API size_t GC_CALL GC_get_total_bytes(void);
/* the allocator lock thus preventing data racing and returning the */
/* consistent result.) Passing NULL pointer is allowed for any */
/* argument. Returned (filled in) values are of word type. */
-/* (This API function and the accompanying macro were introduced in */
-/* GC v7.2alpha7 at the moment when GC_get_heap_size and the friends */
-/* were made lock-free again.) */
-#define GC_HAVE_GET_HEAP_USAGE_SAFE 1
+/* (This API function was introduced in GC v7.2alpha7 at the same time */
+/* when GC_get_heap_size and the friends were made lock-free again.) */
GC_API void GC_CALL GC_get_heap_usage_safe(GC_word * /* pheap_size */,
GC_word * /* pfree_bytes */,
GC_word * /* punmapped_bytes */,
GC_word * /* pbytes_since_gc */,
GC_word * /* ptotal_bytes */);
+/* Structure used to query GC statistics (profiling information). */
+/* More fields could be added in the future. To preserve compatibility */
+/* new fields should be added only to the end, and no deprecated fields */
+/* should be removed from. */
+struct GC_prof_stats_s {
+ GC_word heapsize_full;
+ /* Heap size in bytes (including the area unmapped to OS). */
+ /* Same as GC_get_heap_size() + GC_get_unmapped_bytes(). */
+ GC_word free_bytes_full;
+ /* Total bytes contained in free and unmapped blocks. */
+ /* Same as GC_get_free_bytes() + GC_get_unmapped_bytes(). */
+ GC_word unmapped_bytes;
+ /* Amount of memory unmapped to OS. Same as the value */
+ /* returned by GC_get_unmapped_bytes(). */
+ GC_word bytes_allocd_since_gc;
+ /* Number of bytes allocated since the recent collection. */
+ /* Same as returned by GC_get_bytes_since_gc(). */
+ GC_word allocd_bytes_before_gc;
+ /* Number of bytes allocated before the recent garbage */
+ /* collection. The value may wrap. Same as the result of */
+ /* GC_get_total_bytes() - GC_get_bytes_since_gc(). */
+ GC_word non_gc_bytes;
+ /* Number of bytes not considered candidates for garbage */
+ /* collection. Same as returned by GC_get_non_gc_bytes(). */
+ GC_word gc_no;
+ /* Garbage collection cycle number. The value may wrap */
+ /* (and could be -1). Same as returned by GC_get_gc_no(). */
+ GC_word markers_m1;
+ /* Number of marker threads (excluding the initiating one). */
+ /* Same as returned by GC_get_parallel (or 0 if the */
+ /* collector is single-threaded). */
+ GC_word bytes_reclaimed_since_gc;
+ /* Approximate number of reclaimed bytes after recent GC. */
+ GC_word reclaimed_bytes_before_gc;
+ /* Approximate number of bytes reclaimed before the recent */
+ /* garbage collection. The value may wrap. */
+};
+
+/* Atomically get GC statistics (various global counters). Clients */
+/* should pass the size of the buffer (of GC_prof_stats_s type) to fill */
+/* in the values - this is for interoperability between different GC */
+/* versions, an old client could have fewer fields, and vice versa, */
+/* client could use newer gc.h (with more entries declared in the */
+/* structure) than that of the linked libgc binary; in the latter case, */
+/* unsupported (unknown) fields are filled in with -1. Return the size */
+/* (in bytes) of the filled in part of the structure (excluding all */
+/* unknown fields, if any). */
+GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *,
+ size_t /* stats_sz */);
+#ifdef GC_THREADS
+ /* Same as above but unsynchronized (i.e., not holding the allocation */
+ /* lock). Clients should call it using GC_call_with_alloc_lock to */
+ /* avoid data races on multiprocessors. */
+ GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s *,
+ size_t /* stats_sz */);
+#endif
+
/* Disable garbage collection. Even GC_gcollect calls will be */
/* ineffective. */
GC_API void GC_CALL GC_disable(void);
-/* Re-enable garbage collection. GC_disable() and GC_enable() calls */
-/* nest. Garbage collection is enabled if the number of calls to both */
+/* Return non-zero (TRUE) if and only if garbage collection is disabled */
+/* (i.e., GC_dont_gc value is non-zero). Does not acquire the lock. */
+GC_API int GC_CALL GC_is_disabled(void);
+
+/* Try to re-enable garbage collection. GC_disable() and GC_enable() */
+/* calls nest. Garbage collection is enabled if the number of calls to */
/* both functions is equal. */
GC_API void GC_CALL GC_enable(void);
@@ -592,7 +696,7 @@ GC_API void GC_CALL GC_enable(void);
/* dirty bits are available or most heap objects are pointer-free */
/* (atomic) or immutable. Don't use in leak finding mode. Ignored if */
/* GC_dont_gc is non-zero. Only the generational piece of this is */
-/* functional if GC_parallel is TRUE or if GC_time_limit is */
+/* functional if GC_parallel is non-zero or if GC_time_limit is */
/* GC_TIME_UNLIMITED. Causes thread-local variant of GC_gcj_malloc() */
/* to revert to locked allocation. Must be called before any such */
/* GC_gcj_malloc() calls. For best performance, should be called as */
@@ -609,6 +713,7 @@ GC_API void GC_CALL GC_enable_incremental(void);
#define GC_PROTECTS_STACK 8 /* Probably impractical. */
#define GC_PROTECTS_NONE 0
+/* The collector is assumed to be initialized before this call. */
GC_API int GC_CALL GC_incremental_protection_needs(void);
/* Perform some garbage collection work, if appropriate. */
@@ -634,10 +739,10 @@ GC_API int GC_CALL GC_collect_a_little(void);
/* for arrays likely to be larger than 100K or so. For other systems, */
/* or if the collector is not configured to recognize all interior */
/* pointers, the threshold is normally much higher. */
-GC_API void * GC_CALL GC_malloc_ignore_off_page(size_t /* lb */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_malloc_atomic_ignore_off_page(size_t /* lb */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_ignore_off_page(size_t /* lb */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_atomic_ignore_off_page(size_t /* lb */);
#ifdef GC_ADD_CALLER
# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__
@@ -649,43 +754,40 @@ GC_API void * GC_CALL GC_malloc_atomic_ignore_off_page(size_t /* lb */)
/* The following is only defined if the library has been suitably */
/* compiled: */
-GC_API void * GC_CALL GC_malloc_atomic_uncollectable(
- size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_malloc_atomic_uncollectable(size_t,
- GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_atomic_uncollectable(size_t /* size_in_bytes */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_atomic_uncollectable(size_t, GC_EXTRA_PARAMS);
/* Debugging (annotated) allocation. GC_gcollect will check */
/* objects allocated in this way for overwrites, etc. */
-GC_API void * GC_CALL GC_debug_malloc(size_t /* size_in_bytes */,
- GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_malloc_atomic(size_t /* size_in_bytes */,
- GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API char * GC_CALL GC_debug_strdup(const char *,
- GC_EXTRA_PARAMS) GC_ATTR_MALLOC;
-GC_API char * GC_CALL GC_debug_strndup(const char *, size_t,
- GC_EXTRA_PARAMS) GC_ATTR_MALLOC;
-GC_API void * GC_CALL GC_debug_malloc_uncollectable(
- size_t /* size_in_bytes */, GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t /* size_in_bytes */,
- GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(
- size_t /* size_in_bytes */, GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(
- size_t /* size_in_bytes */, GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_atomic(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+ GC_debug_strdup(const char *, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC char * GC_CALL
+ GC_debug_strndup(const char *, size_t, GC_EXTRA_PARAMS)
+ GC_ATTR_NONNULL(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_uncollectable(size_t /* size_in_bytes */,
+ GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_stubborn(size_t /* size_in_bytes */, GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_ignore_off_page(size_t /* size_in_bytes */,
+ GC_EXTRA_PARAMS);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_atomic_ignore_off_page(size_t /* size_in_bytes */,
+ GC_EXTRA_PARAMS);
GC_API void GC_CALL GC_debug_free(void *);
GC_API void * GC_CALL GC_debug_realloc(void * /* old_object */,
size_t /* new_size_in_bytes */, GC_EXTRA_PARAMS)
/* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2);
-GC_API void GC_CALL GC_debug_change_stubborn(const void *);
-GC_API void GC_CALL GC_debug_end_stubborn_change(const void *);
+GC_API void GC_CALL GC_debug_change_stubborn(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_debug_end_stubborn_change(const void *)
+ GC_ATTR_NONNULL(1);
/* Routines that allocate objects with debug information (like the */
/* above), but just fill in dummy file and line number information. */
@@ -699,11 +801,11 @@ GC_API void GC_CALL GC_debug_end_stubborn_change(const void *);
/* platforms it may be more convenient not to recompile, e.g. for */
/* leak detection. This can be accomplished by instructing the */
/* linker to replace malloc/realloc with these. */
-GC_API void * GC_CALL GC_debug_malloc_replacement(size_t /* size_in_bytes */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
-GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */,
- size_t /* size_in_bytes */)
- /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_malloc_replacement(size_t /* size_in_bytes */);
+GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL
+ GC_debug_realloc_replacement(void * /* object_addr */,
+ size_t /* size_in_bytes */);
#ifdef GC_DEBUG_REPLACEMENT
# define GC_MALLOC(sz) GC_debug_malloc_replacement(sz)
@@ -743,6 +845,8 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */,
# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \
GC_general_register_disappearing_link(link, \
GC_base((/* no const */ void *)(obj)))
+# define GC_REGISTER_LONG_LINK(link, obj) \
+ GC_register_long_link(link, GC_base((/* no const */ void *)(obj)))
# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n)
#else
# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz)
@@ -768,6 +872,8 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */,
# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p)
# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \
GC_general_register_disappearing_link(link, obj)
+# define GC_REGISTER_LONG_LINK(link, obj) \
+ GC_register_long_link(link, obj)
# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n)
#endif /* !GC_DEBUG */
@@ -784,9 +890,10 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */,
#ifdef GC_REQUIRE_WCSDUP
/* This might be unavailable on some targets (or not needed). */
/* wchar_t should be defined in stddef.h */
- GC_API wchar_t * GC_CALL GC_wcsdup(const wchar_t *) GC_ATTR_MALLOC;
- GC_API wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *,
- GC_EXTRA_PARAMS) GC_ATTR_MALLOC;
+ GC_API GC_ATTR_MALLOC wchar_t * GC_CALL
+ GC_wcsdup(const wchar_t *) GC_ATTR_NONNULL(1);
+ GC_API GC_ATTR_MALLOC wchar_t * GC_CALL
+ GC_debug_wcsdup(const wchar_t *, GC_EXTRA_PARAMS) GC_ATTR_NONNULL(1);
# ifdef GC_DEBUG
# define GC_WCSDUP(s) GC_debug_wcsdup(s, GC_EXTRAS)
# else
@@ -806,10 +913,12 @@ typedef void (GC_CALLBACK * GC_finalization_proc)(void * /* obj */,
GC_API void GC_CALL GC_register_finalizer(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
/* When obj is no longer accessible, invoke */
/* (*fn)(obj, cd). If a and b are inaccessible, and */
/* a points to b (after disappearing links have been */
@@ -845,7 +954,7 @@ GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */,
/* allocated by GC_malloc or friends. Obj may also be */
/* NULL or point to something outside GC heap (in this */
/* case, fn is ignored, *ofn and *ocd are set to NULL). */
- /* Note that any garbage collectable object referenced */
+ /* Note that any garbage collectible object referenced */
/* by cd will be considered accessible until the */
/* finalizer is invoked. */
@@ -860,10 +969,12 @@ GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */,
/* refers to the object itself. */
GC_API void GC_CALL GC_register_finalizer_ignore_self(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
/* Another version of the above. It ignores all cycles. */
/* It should probably only be used by Java implementations. */
@@ -871,10 +982,12 @@ GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void * /* obj */,
/* refers to the object itself. */
GC_API void GC_CALL GC_register_finalizer_no_order(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_debug_register_finalizer_no_order(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
/* This is a special finalizer that is useful when an object's */
/* finalizer must be run when the object is known to be no */
@@ -894,10 +1007,12 @@ GC_API void GC_CALL GC_debug_register_finalizer_no_order(void * /* obj */,
/* unordered finalization (e.g. Java, C#). */
GC_API void GC_CALL GC_register_finalizer_unreachable(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void * /* obj */,
GC_finalization_proc /* fn */, void * /* cd */,
- GC_finalization_proc * /* ofn */, void ** /* ocd */);
+ GC_finalization_proc * /* ofn */, void ** /* ocd */)
+ GC_ATTR_NONNULL(1);
#define GC_NO_MEMORY 2 /* Failure due to lack of memory. */
@@ -908,7 +1023,8 @@ GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void * /* obj */,
/* where p is a pointer that is not followed by finalization */
/* code, and should not be considered in determining */
/* finalization order. */
-GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */);
+GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */)
+ GC_ATTR_NONNULL(1);
/* Link should point to a field of a heap allocated */
/* object obj. *link will be cleared when obj is */
/* found to be inaccessible. This happens BEFORE any */
@@ -930,7 +1046,8 @@ GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */);
/* Only exists for backward compatibility. See below: */
GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */,
- const void * /* obj */);
+ const void * /* obj */)
+ GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2);
/* A slight generalization of the above. *link is */
/* cleared when obj first becomes inaccessible. This */
/* can be used to implement weak pointers easily and */
@@ -963,7 +1080,8 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */,
/* memory (and GC_oom_fn did not handle the problem). */
GC_API int GC_CALL GC_move_disappearing_link(void ** /* link */,
- void ** /* new_link */);
+ void ** /* new_link */)
+ GC_ATTR_NONNULL(2);
/* Moves a link previously registered via */
/* GC_general_register_disappearing_link (or */
/* GC_register_disappearing_link). Does not change the */
@@ -981,6 +1099,27 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */);
/* routines. Returns 0 if link was not actually */
/* registered (otherwise returns 1). */
+GC_API int GC_CALL GC_register_long_link(void ** /* link */,
+ const void * /* obj */)
+ GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2);
+ /* Similar to GC_general_register_disappearing_link but */
+ /* *link only gets cleared when obj becomes truly */
+ /* inaccessible. An object becomes truly inaccessible */
+ /* when it can no longer be resurrected from its */
+ /* finalizer (e.g. by assigning itself to a pointer */
+ /* traceable from root). This can be used to implement */
+ /* long weak pointers easily and safely. */
+
+GC_API int GC_CALL GC_move_long_link(void ** /* link */,
+ void ** /* new_link */)
+ GC_ATTR_NONNULL(2);
+ /* Similar to GC_move_disappearing_link but for a link */
+ /* previously registered via GC_register_long_link. */
+
+GC_API int GC_CALL GC_unregister_long_link(void ** /* link */);
+ /* Similar to GC_unregister_disappearing_link but for a */
+ /* registration by either of the above two routines. */
+
/* Returns !=0 if GC_invoke_finalizers has something to do. */
GC_API int GC_CALL GC_should_invoke_finalizers(void);
@@ -1010,11 +1149,12 @@ GC_API int GC_CALL GC_invoke_finalizers(void);
#endif
/* GC_set_warn_proc can be used to redirect or filter warning messages. */
-/* p may not be a NULL pointer. Both the setter and the getter acquire */
-/* the GC lock (to avoid data races). */
+/* p may not be a NULL pointer. msg is printf format string (arg must */
+/* match the format). Both the setter and the getter acquire the GC */
+/* lock (to avoid data races). */
typedef void (GC_CALLBACK * GC_warn_proc)(char * /* msg */,
GC_word /* arg */);
-GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc /* p */);
+GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc /* p */) GC_ATTR_NONNULL(1);
/* GC_get_warn_proc returns the current warn_proc. */
GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void);
@@ -1022,6 +1162,17 @@ GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void);
/* to suppress all warnings (unless statistics printing is turned on). */
GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word);
+/* abort_func is invoked on GC fatal aborts (just before OS-dependent */
+/* abort or exit(1) is called). Must be non-NULL. The default one */
+/* outputs msg to stderr provided msg is non-NULL. msg is NULL if */
+/* invoked before exit(1) otherwise msg is non-NULL (i.e., if invoked */
+/* before abort). Both the setter and getter acquire the GC lock. */
+/* Both the setter and getter are defined only if the library has been */
+/* compiled without SMALL_CONFIG. */
+typedef void (GC_CALLBACK * GC_abort_func)(const char * /* msg */);
+GC_API void GC_CALL GC_set_abort_func(GC_abort_func) GC_ATTR_NONNULL(1);
+GC_API GC_abort_func GC_CALL GC_get_abort_func(void);
+
/* The following is intended to be used by a higher level */
/* (e.g. Java-like) finalization facility. It is expected */
/* that finalization code will arrange for hidden pointers to */
@@ -1037,7 +1188,7 @@ typedef GC_word GC_hidden_pointer;
/* allocator lock to avoid a race with the collector. */
#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p))
-#ifdef I_HIDE_POINTERS
+#if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS)
/* This exists only for compatibility (the GC-prefixed symbols are */
/* preferred for new code). */
# define HIDE_POINTER(p) GC_HIDE_POINTER(p)
@@ -1046,7 +1197,7 @@ typedef GC_word GC_hidden_pointer;
typedef void * (GC_CALLBACK * GC_fn_type)(void * /* client_data */);
GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type /* fn */,
- void * /* client_data */);
+ void * /* client_data */) GC_ATTR_NONNULL(1);
/* These routines are intended to explicitly notify the collector */
/* of new threads. Often this is unnecessary because thread creation */
@@ -1075,7 +1226,7 @@ typedef void * (GC_CALLBACK * GC_stack_base_func)(
/* be used to provide a sufficiently accurate stack base. And we */
/* implement it everywhere. */
GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */,
- void * /* arg */);
+ void * /* arg */) GC_ATTR_NONNULL(1);
#define GC_SUCCESS 0
#define GC_DUPLICATE 1 /* Was already registered. */
@@ -1095,14 +1246,27 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */,
#endif
#ifdef GC_THREADS
- /* Return the signal number (constant) used by the garbage collector */
- /* to suspend threads on POSIX systems. Return -1 otherwise. */
+ /* Suggest the GC to use the specific signal to suspend threads. */
+ /* Has no effect after GC_init and on non-POSIX systems. */
+ GC_API void GC_CALL GC_set_suspend_signal(int);
+
+ /* Suggest the GC to use the specific signal to resume threads. */
+ /* Has no effect after GC_init and on non-POSIX systems. */
+ GC_API void GC_CALL GC_set_thr_restart_signal(int);
+
+ /* Return the signal number (constant after initialization) used by */
+ /* the GC to suspend threads on POSIX systems. Return -1 otherwise. */
GC_API int GC_CALL GC_get_suspend_signal(void);
- /* Return the signal number (constant) used by the garbage collector */
- /* to restart (resume) threads on POSIX systems. Return -1 otherwise. */
+ /* Return the signal number (constant after initialization) used by */
+ /* the garbage collector to restart (resume) threads on POSIX */
+ /* systems. Return -1 otherwise. */
GC_API int GC_CALL GC_get_thr_restart_signal(void);
+ /* Restart marker threads after POSIX fork in child. Meaningless in */
+ /* other situations. Should not be called if fork followed by exec. */
+ GC_API void GC_CALL GC_start_mark_threads(void);
+
/* Explicitly enable GC_register_my_thread() invocation. */
/* Done implicitly if a GC thread-creation function is called (or */
/* implicit thread registration is activated). Otherwise, it must */
@@ -1131,10 +1295,11 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */,
/* latter case, the explicit call is normally required for threads */
/* created by third-party libraries. */
/* A manually registered thread requires manual unregistering. */
- GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *);
+ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *)
+ GC_ATTR_NONNULL(1);
- /* Return TRUE if and only if the calling thread is registered with */
- /* the garbage collector. */
+ /* Return non-zero (TRUE) if and only if the calling thread is */
+ /* registered with the garbage collector. */
GC_API int GC_CALL GC_thread_is_registered(void);
/* Unregister the current thread. Only an explicitly registered */
@@ -1162,7 +1327,7 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */,
/* allowed for fn to call GC_call_with_gc_active() (even recursively), */
/* thus temporarily toggling the collector's state back to "active". */
GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */,
- void * /* client_data */);
+ void * /* client_data */) GC_ATTR_NONNULL(1);
/* Call a function switching to the "active" state of the collector for */
/* the current thread (i.e. the user function is allowed to call any */
@@ -1174,7 +1339,7 @@ GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */,
/* GC_do_blocking. GC_call_with_gc_active() often can be used to */
/* provide a sufficiently accurate stack base. */
GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */,
- void * /* client_data */);
+ void * /* client_data */) GC_ATTR_NONNULL(1);
/* Attempt to fill in the GC_stack_base structure with the stack base */
/* for this thread. This appears to be required to implement anything */
@@ -1183,7 +1348,8 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */,
/* It is also unfortunately hard to implement well on many platforms. */
/* Returns GC_SUCCESS or GC_UNIMPLEMENTED. This function acquires the */
/* GC lock on some platforms. */
-GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *);
+GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *)
+ GC_ATTR_NONNULL(1);
/* The following routines are primarily intended for use with a */
/* preprocessor which inserts calls to check C pointer arithmetic. */
@@ -1200,8 +1366,10 @@ GC_API void * GC_CALL GC_same_obj(void * /* p */, void * /* q */);
/* the second argument is in units of bytes, not multiples of the */
/* object size. This should either be invoked from a macro, or the */
/* call should be automatically generated. */
-GC_API void * GC_CALL GC_pre_incr(void **, ptrdiff_t /* how_much */);
-GC_API void * GC_CALL GC_post_incr(void **, ptrdiff_t /* how_much */);
+GC_API void * GC_CALL GC_pre_incr(void **, ptrdiff_t /* how_much */)
+ GC_ATTR_NONNULL(1);
+GC_API void * GC_CALL GC_post_incr(void **, ptrdiff_t /* how_much */)
+ GC_ATTR_NONNULL(1);
/* Check that p is visible */
/* to the collector as a possibly pointer containing location. */
@@ -1278,7 +1446,7 @@ GC_API void (GC_CALLBACK * GC_is_visible_print_proc)(void *);
/* This returns a list of objects, linked through their first word. */
/* Its use can greatly reduce lock contention problems, since the */
/* allocation lock can be acquired and released many fewer times. */
-GC_API void * GC_CALL GC_malloc_many(size_t /* lb */);
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t /* lb */);
#define GC_NEXT(p) (*(void * *)(p)) /* Retrieve the next element */
/* in returned list. */
@@ -1307,7 +1475,7 @@ GC_API void GC_CALL GC_register_has_static_roots_callback(
/* Note: for Cygwin and win32-pthread, this is skipped */
/* unless windows.h is included before gc.h. */
-# ifndef GC_NO_THREAD_DECLS
+# if !defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD)
# ifdef __cplusplus
} /* Including windows.h in an extern "C" context no longer works. */
@@ -1339,6 +1507,14 @@ GC_API void GC_CALL GC_register_has_static_roots_callback(
LPVOID /* reserved */);
# endif /* GC_INSIDE_DLL */
+# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \
+ && !defined(UINTPTR_MAX)
+ typedef GC_word GC_uintptr_t;
+# else
+ typedef uintptr_t GC_uintptr_t;
+# endif
+# define GC_WIN32_SIZE_T GC_uintptr_t
+
/* All threads must be created using GC_CreateThread or */
/* GC_beginthreadex, or must explicitly call GC_register_my_thread */
/* (and call GC_unregister_my_thread before thread termination), so */
@@ -1351,7 +1527,7 @@ GC_API void GC_CALL GC_register_has_static_roots_callback(
/* so that the thread is properly unregistered. */
GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES /* lpThreadAttributes */,
- DWORD /* dwStackSize */,
+ GC_WIN32_SIZE_T /* dwStackSize */,
LPTHREAD_START_ROUTINE /* lpStartAddress */,
LPVOID /* lpParameter */, DWORD /* dwCreationFlags */,
LPDWORD /* lpThreadId */);
@@ -1365,13 +1541,6 @@ GC_API void GC_CALL GC_register_has_static_roots_callback(
DWORD /* dwExitCode */);
# if !defined(_WIN32_WCE) && !defined(__CEGCC__)
-# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \
- && !defined(UINTPTR_MAX)
- typedef GC_word GC_uintptr_t;
-# else
- typedef uintptr_t GC_uintptr_t;
-# endif
-
GC_API GC_uintptr_t GC_CALL GC_beginthreadex(
void * /* security */, unsigned /* stack_size */,
unsigned (__stdcall *)(void *),
@@ -1427,9 +1596,9 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
/* Similarly gnu-win32 DLLs need explicit initialization from the */
/* main program, as does AIX. */
extern int _data_start__[], _data_end__[], _bss_start__[], _bss_end__[];
-# define GC_DATASTART (_data_start__ < _bss_start__ ? \
+# define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__ ? \
(void *)_data_start__ : (void *)_bss_start__)
-# define GC_DATAEND (_data_end__ > _bss_end__ ? \
+# define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__ ? \
(void *)_data_end__ : (void *)_bss_end__)
# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND); \
GC_gcollect() /* For blacklisting. */
@@ -1439,6 +1608,13 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
# define GC_DATASTART ((void *)((ulong)_data))
# define GC_DATAEND ((void *)((ulong)_end))
# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND)
+#elif (defined(PLATFORM_ANDROID) || defined(__ANDROID__)) \
+ && !defined(GC_NOT_DLL)
+ /* Required if GC is built as shared lib with -D IGNORE_DYNAMIC_LOADING. */
+# pragma weak __data_start
+ extern int __data_start[], _end[];
+# define GC_INIT_CONF_ROOTS (void)((GC_word)(__data_start) != 0 ? \
+ (GC_add_roots(__data_start, _end), 0) : 0)
#else
# define GC_INIT_CONF_ROOTS /* empty */
#endif
@@ -1458,7 +1634,11 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT /* empty */
#endif
-#ifdef GC_MAX_RETRIES
+#ifdef GC_DONT_GC
+ /* This is for debugging only (useful if environment variables are */
+ /* unsupported); cannot call GC_disable as goes before GC_init. */
+# define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc = 1)
+#elif defined(GC_MAX_RETRIES)
/* Set GC_max_retries to the desired value at start-up */
# define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES)
#else
@@ -1487,6 +1667,19 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
# define GC_INIT_CONF_TIME_LIMIT /* empty */
#endif
+#if defined(GC_SIG_SUSPEND) && defined(GC_THREADS)
+# define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND)
+#else
+# define GC_INIT_CONF_SUSPEND_SIGNAL /* empty */
+#endif
+
+#if defined(GC_SIG_THR_RESTART) && defined(GC_THREADS)
+# define GC_INIT_CONF_THR_RESTART_SIGNAL \
+ GC_set_thr_restart_signal(GC_SIG_THR_RESTART)
+#else
+# define GC_INIT_CONF_THR_RESTART_SIGNAL /* empty */
+#endif
+
#ifdef GC_MAXIMUM_HEAP_SIZE
/* Limit the heap size to the desired value (useful for debugging). */
/* The limit could be overridden either at the program start-up by */
@@ -1524,6 +1717,8 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
GC_INIT_CONF_FREE_SPACE_DIVISOR; \
GC_INIT_CONF_FULL_FREQ; \
GC_INIT_CONF_TIME_LIMIT; \
+ GC_INIT_CONF_SUSPEND_SIGNAL; \
+ GC_INIT_CONF_THR_RESTART_SIGNAL; \
GC_INIT_CONF_MAXIMUM_HEAP_SIZE; \
GC_init(); /* real GC initialization */ \
GC_INIT_CONF_ROOTS; /* post-init */ \
@@ -1534,6 +1729,10 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
/* This explicitly deallocates the heap. */
GC_API void GC_CALL GC_win32_free_heap(void);
+#if defined(__SYMBIAN32__)
+ void GC_init_global_static_roots(void);
+#endif
+
#if defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB)
/* Allocation really goes through GC_amiga_allocwrapper_do. */
void *GC_amiga_realloc(void *, size_t);
diff --git a/include/gc_allocator.h b/include/gc_allocator.h
index 086fac4b..367cfe24 100644
--- a/include/gc_allocator.h
+++ b/include/gc_allocator.h
@@ -24,12 +24,12 @@
/*
* This implements standard-conforming allocators that interact with
- * the garbage collector. Gc_alloctor<T> allocates garbage-collectable
+ * the garbage collector. Gc_allocator<T> allocates garbage-collectible
* objects of type T. Traceable_allocator<T> allocates objects that
* are not themselves garbage collected, but are scanned by the
- * collector for pointers to collectable objects. Traceable_alloc
+ * collector for pointers to collectible objects. Traceable_alloc
* should be used for explicitly managed STL containers that may
- * point to collectable objects.
+ * point to collectible objects.
*
* This code was derived from an earlier version of the GNU C++ standard
* library, which itself was derived from the SGI STL implementation.
diff --git a/include/gc_backptr.h b/include/gc_backptr.h
index 6256f416..c7e29d28 100644
--- a/include/gc_backptr.h
+++ b/include/gc_backptr.h
@@ -68,8 +68,8 @@ typedef enum {
} GC_ref_kind;
GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void * /* dest */,
- void ** /* base_p */,
- size_t * /* offset_p */);
+ void ** /* base_p */, size_t * /* offset_p */)
+ GC_ATTR_NONNULL(1);
/* Generate a random heap address. */
/* The resulting address is in the heap, but */
@@ -89,7 +89,7 @@ GC_API void GC_CALL GC_generate_random_backtrace(void);
/* Print a backtrace from a specific address. Used by the */
/* above. The client should call GC_gcollect() immediately */
/* before invocation. */
-GC_API void GC_CALL GC_print_backtrace(void *);
+GC_API void GC_CALL GC_print_backtrace(void *) GC_ATTR_NONNULL(1);
#ifdef __cplusplus
} /* end of extern "C" */
diff --git a/include/gc_config_macros.h b/include/gc_config_macros.h
index d3b5f6a6..7e967571 100644
--- a/include/gc_config_macros.h
+++ b/include/gc_config_macros.h
@@ -125,8 +125,7 @@
#undef GC_PTHREADS
#if (!defined(GC_WIN32_THREADS) || defined(GC_WIN32_PTHREADS) \
- || defined(GC_RTEMS_PTHREADS) || defined(__CYGWIN32__) \
- || defined(__CYGWIN__)) && defined(GC_THREADS)
+ || defined(__CYGWIN32__) || defined(__CYGWIN__)) && defined(GC_THREADS)
/* Posix threads. */
# define GC_PTHREADS
#endif
@@ -164,8 +163,9 @@
# endif
#endif /* _WIN32_WCE */
-#if defined(_DLL) && !defined(GC_NOT_DLL) && !defined(GC_DLL) \
- && !defined(__GNUC__)
+#if !defined(GC_NOT_DLL) && !defined(GC_DLL) \
+ && ((defined(_DLL) && !defined(__GNUC__)) \
+ || (defined(DLL_EXPORT) && defined(GC_BUILD)))
# define GC_DLL
#endif
@@ -193,6 +193,13 @@
# define GC_API extern __declspec(dllimport)
# endif
+# elif defined(__SYMBIAN32__)
+# ifdef GC_BUILD
+# define GC_API extern EXPORT_C
+# else
+# define GC_API extern IMPORT_C
+# endif
+
# elif defined(__GNUC__)
/* Only matters if used in conjunction with -fvisibility=hidden option. */
# if defined(GC_BUILD) && (__GNUC__ >= 4 \
@@ -220,9 +227,13 @@
/* non-NULL pointer it returns cannot alias any other pointer valid */
/* when the function returns). If the client code violates this rule */
/* by using custom GC_oom_func then define GC_OOM_FUNC_RETURNS_ALIAS. */
-# if !defined(GC_OOM_FUNC_RETURNS_ALIAS) && defined(__GNUC__) \
- && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# ifdef GC_OOM_FUNC_RETURNS_ALIAS
+# define GC_ATTR_MALLOC /* empty */
+# elif defined(__GNUC__) && (__GNUC__ > 3 \
+ || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define GC_ATTR_MALLOC __attribute__((__malloc__))
+# elif defined(_MSC_VER) && _MSC_VER >= 14
+# define GC_ATTR_MALLOC __declspec(noalias) __declspec(restrict)
# else
# define GC_ATTR_MALLOC
# endif
@@ -232,13 +243,36 @@
/* 'alloc_size' attribute improves __builtin_object_size correctness. */
/* Only single-argument form of 'alloc_size' attribute is used. */
# if defined(__GNUC__) && (__GNUC__ > 4 \
- || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 && !defined(__ICC)))
+ || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 && !defined(__ICC)) \
+ || __clang_major__ > 3 \
+ || (__clang_major__ == 3 && __clang_minor__ >= 2))
# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum)))
# else
# define GC_ATTR_ALLOC_SIZE(argnum)
# endif
#endif
+#ifndef GC_ATTR_NONNULL
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define GC_ATTR_NONNULL(argnum) __attribute__((__nonnull__(argnum)))
+# else
+# define GC_ATTR_NONNULL(argnum) /* empty */
+# endif
+#endif
+
+#ifndef GC_ATTR_DEPRECATED
+# ifdef GC_BUILD
+# undef GC_ATTR_DEPRECATED
+# define GC_ATTR_DEPRECATED /* empty */
+# elif defined(__GNUC__) && __GNUC__ >= 4
+# define GC_ATTR_DEPRECATED __attribute__((__deprecated__))
+# elif defined(_MSC_VER) && _MSC_VER >= 12
+# define GC_ATTR_DEPRECATED __declspec(deprecated)
+# else
+# define GC_ATTR_DEPRECATED /* empty */
+# endif
+#endif
+
#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720
# define GC_ADD_CALLER
# define GC_RETURN_ADDR (GC_word)__return_address
@@ -253,7 +287,7 @@
&& !defined(GC_HAVE_BUILTIN_BACKTRACE)
# define GC_HAVE_BUILTIN_BACKTRACE
# endif
-# if defined(__i386__) || defined(__x86_64__)
+# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__)
# define GC_CAN_SAVE_CALL_STACKS
# endif
#endif /* GLIBC */
@@ -289,6 +323,11 @@
/* gcc knows how to retrieve return address, but we don't know */
/* how to generate call stacks. */
# define GC_RETURN_ADDR (GC_word)__builtin_return_address(0)
+# if (__GNUC__ >= 4) && (defined(__i386__) || defined(__amd64__) \
+ || defined(__x86_64__) /* and probably others... */)
+# define GC_RETURN_ADDR_PARENT \
+ (GC_word)__builtin_extract_return_addr(__builtin_return_address(1))
+# endif
# else
/* Just pass 0 for gcc compatibility. */
# define GC_RETURN_ADDR 0
diff --git a/include/gc_cpp.h b/include/gc_cpp.h
index 820dce22..6f3fce88 100644
--- a/include/gc_cpp.h
+++ b/include/gc_cpp.h
@@ -24,43 +24,43 @@ basic facilities similar to those described in "Safe, Efficient
Garbage Collection for C++", by John R. Elis and David L. Detlefs
(ftp://ftp.parc.xerox.com/pub/ellis/gc).
-All heap-allocated objects are either "collectable" or
-"uncollectable". Programs must explicitly delete uncollectable
+All heap-allocated objects are either "collectible" or
+"uncollectible". Programs must explicitly delete uncollectible
objects, whereas the garbage collector will automatically delete
-collectable objects when it discovers them to be inaccessible.
-Collectable objects may freely point at uncollectable objects and vice
+collectible objects when it discovers them to be inaccessible.
+Collectible objects may freely point at uncollectible objects and vice
versa.
-Objects allocated with the built-in "::operator new" are uncollectable.
+Objects allocated with the built-in "::operator new" are uncollectible.
-Objects derived from class "gc" are collectable. For example:
+Objects derived from class "gc" are collectible. For example:
class A: public gc {...};
- A* a = new A; // a is collectable.
+ A* a = new A; // a is collectible.
-Collectable instances of non-class types can be allocated using the GC
+Collectible instances of non-class types can be allocated using the GC
(or UseGC) placement:
typedef int A[ 10 ];
A* a = new (GC) A;
-Uncollectable instances of classes derived from "gc" can be allocated
+Uncollectible instances of classes derived from "gc" can be allocated
using the NoGC placement:
class A: public gc {...};
- A* a = new (NoGC) A; // a is uncollectable.
+ A* a = new (NoGC) A; // a is uncollectible.
-The new(PointerFreeGC) syntax allows the allocation of collectable
+The new(PointerFreeGC) syntax allows the allocation of collectible
objects that are not scanned by the collector. This useful if you
are allocating compressed data, bitmaps, or network packets. (In
the latter case, it may remove danger of unfriendly network packets
intentionally containing values that cause spurious memory retention.)
-Both uncollectable and collectable objects can be explicitly deleted
+Both uncollectible and collectible objects can be explicitly deleted
with "delete", which invokes an object's destructors and frees its
storage immediately.
-A collectable object may have a clean-up function, which will be
+A collectible object may have a clean-up function, which will be
invoked when the collector discovers the object to be inaccessible.
An object derived from "gc_cleanup" or containing a member derived
from "gc_cleanup" has a default clean-up function that invokes the
@@ -79,7 +79,7 @@ B, B is considered accessible. After A's clean-up is invoked and its
storage released, B will then become inaccessible and will have its
clean-up invoked. If A points at B and B points to A, forming a
cycle, then that's considered a storage leak, and neither will be
-collectable. See the interface gc.h for low-level facilities for
+collectible. See the interface gc.h for low-level facilities for
handling such cycles of objects with clean-up.
The collector cannot guarantee that it will find all inaccessible
@@ -96,14 +96,14 @@ add -DGC_OPERATOR_NEW_ARRAY to the Makefile.
If your compiler doesn't support "operator new[]", beware that an
array of type T, where T is derived from "gc", may or may not be
-allocated as a collectable object (it depends on the compiler). Use
-the explicit GC placement to make the array collectable. For example:
+allocated as a collectible object (it depends on the compiler). Use
+the explicit GC placement to make the array collectible. For example:
class A: public gc {...};
- A* a1 = new A[ 10 ]; // collectable or uncollectable?
- A* a2 = new (GC) A[ 10 ]; // collectable
+ A* a1 = new A[ 10 ]; // collectible or uncollectible?
+ A* a2 = new (GC) A[ 10 ]; // collectible.
-3. The destructors of collectable arrays of objects derived from
+3. The destructors of collectible arrays of objects derived from
"gc_cleanup" will not be invoked properly. For example:
class A: public gc_cleanup {...};
@@ -142,6 +142,12 @@ by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined.
#include "gc.h"
+#ifdef GC_NAMESPACE
+# define GC_NS_QUALIFY(T) boehmgc::T
+#else
+# define GC_NS_QUALIFY(T) T
+#endif
+
#ifndef THINK_CPLUS
# define GC_cdecl GC_CALLBACK
#else
@@ -168,13 +174,18 @@ by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined.
# define GC_PLACEMENT_DELETE
#endif
+#ifdef GC_NAMESPACE
+namespace boehmgc
+{
+#endif
+
enum GCPlacement {
UseGC,
# ifndef GC_NAME_CONFLICT
GC=UseGC,
# endif
- NoGC,
- PointerFreeGC
+ NoGC,
+ PointerFreeGC
};
class gc {
@@ -225,20 +236,24 @@ extern "C" {
typedef void (GC_CALLBACK * GCCleanUpFunc)( void* obj, void* clientData );
}
+#ifdef GC_NAMESPACE
+}
+#endif
+
#ifdef _MSC_VER
// Disable warning that "no matching operator delete found; memory will
// not be freed if initialization throws an exception"
# pragma warning(disable:4291)
#endif
-inline void* operator new( size_t size, GCPlacement gcp,
- GCCleanUpFunc cleanup = 0,
+inline void* operator new( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+ GC_NS_QUALIFY(GCCleanUpFunc) cleanup = 0,
void* clientData = 0 );
/*
- Allocates a collectable or uncollected object, according to the
+ Allocates a collectible or uncollectible object, according to the
value of "gcp".
- For collectable objects, if "cleanup" is non-null, then when the
+ For collectible objects, if "cleanup" is non-null, then when the
allocated object "obj" becomes inaccessible, the collector will
invoke the function "cleanup( obj, clientData )" but will not
invoke the object's destructors. It is an error to explicitly
@@ -249,7 +264,8 @@ inline void* operator new( size_t size, GCPlacement gcp,
from "gc_cleanup". */
#ifdef GC_PLACEMENT_DELETE
- inline void operator delete( void*, GCPlacement, GCCleanUpFunc, void * );
+ inline void operator delete( void*, GC_NS_QUALIFY(GCPlacement),
+ GC_NS_QUALIFY(GCCleanUpFunc), void * );
#endif
#ifdef _MSC_VER
@@ -273,8 +289,8 @@ inline void* operator new( size_t size, GCPlacement gcp,
#endif /* _MSC_VER */
#ifdef GC_OPERATOR_NEW_ARRAY
- inline void* operator new[]( size_t size, GCPlacement gcp,
- GCCleanUpFunc cleanup = 0,
+ inline void* operator new[]( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+ GC_NS_QUALIFY(GCCleanUpFunc) cleanup = 0,
void* clientData = 0 );
/* The operator new for arrays, identical to the above. */
#endif /* GC_OPERATOR_NEW_ARRAY */
@@ -285,6 +301,11 @@ Inline implementation
****************************************************************************/
+#ifdef GC_NAMESPACE
+namespace boehmgc
+{
+#endif
+
inline void* gc::operator new( size_t size ) {
return GC_MALLOC( size );
}
@@ -363,17 +384,22 @@ inline gc_cleanup::gc_cleanup() {
}
}
-inline void* operator new( size_t size, GCPlacement gcp,
- GCCleanUpFunc cleanup, void* clientData )
+#ifdef GC_NAMESPACE
+}
+#endif
+
+inline void* operator new( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+ GC_NS_QUALIFY(GCCleanUpFunc) cleanup,
+ void* clientData )
{
void* obj;
- if (gcp == UseGC) {
+ if (gcp == GC_NS_QUALIFY(UseGC)) {
obj = GC_MALLOC( size );
if (cleanup != 0)
GC_REGISTER_FINALIZER_IGNORE_SELF( obj, cleanup, clientData,
0, 0 );
- } else if (gcp == PointerFreeGC) {
+ } else if (gcp == GC_NS_QUALIFY(PointerFreeGC)) {
obj = GC_MALLOC_ATOMIC( size );
} else {
obj = GC_MALLOC_UNCOLLECTABLE( size );
@@ -382,8 +408,8 @@ inline void* operator new( size_t size, GCPlacement gcp,
}
#ifdef GC_PLACEMENT_DELETE
- inline void operator delete( void *p, GCPlacement /* gcp */,
- GCCleanUpFunc /* cleanup */,
+ inline void operator delete( void *p, GC_NS_QUALIFY(GCPlacement) /* gcp */,
+ GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */,
void* /* clientData */ )
{
GC_FREE(p);
@@ -391,8 +417,9 @@ inline void* operator new( size_t size, GCPlacement gcp,
#endif /* GC_PLACEMENT_DELETE */
#ifdef GC_OPERATOR_NEW_ARRAY
- inline void* operator new[]( size_t size, GCPlacement gcp,
- GCCleanUpFunc cleanup, void* clientData )
+ inline void* operator new[]( size_t size, GC_NS_QUALIFY(GCPlacement) gcp,
+ GC_NS_QUALIFY(GCCleanUpFunc) cleanup,
+ void* clientData )
{
return ::operator new( size, gcp, cleanup, clientData );
}
diff --git a/include/gc_disclaim.h b/include/gc_disclaim.h
index beb1bc12..3631e6e8 100644
--- a/include/gc_disclaim.h
+++ b/include/gc_disclaim.h
@@ -48,7 +48,8 @@ struct GC_finalizer_closure {
/* dedicated object kind with a disclaim procedure, and is more */
/* efficient than GC_register_finalizer and friends. */
/* GC_init_finalized_malloc must be called before using this. */
-GC_API void *GC_CALL GC_finalized_malloc(size_t /*size*/,
- const struct GC_finalizer_closure * /*fc*/);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_finalized_malloc(size_t /*size*/,
+ const struct GC_finalizer_closure * /*fc*/);
#endif
diff --git a/include/gc_gcj.h b/include/gc_gcj.h
index 78658785..eba714c6 100644
--- a/include/gc_gcj.h
+++ b/include/gc_gcj.h
@@ -72,21 +72,22 @@ GC_API void GC_CALL GC_init_gcj_malloc(int /* mp_index */,
/* Allocate an object, clear it, and store the pointer to the */
/* type structure (vtable in gcj). */
/* This adds a byte at the end of the object if GC_malloc would.*/
-GC_API void * GC_CALL GC_gcj_malloc(size_t /* lb */,
- void * /* ptr_to_struct_containing_descr */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_gcj_malloc(size_t /* lb */,
+ void * /* ptr_to_struct_containing_descr */);
+
/* The debug versions allocate such that the specified mark_proc */
/* is always invoked. */
-GC_API void * GC_CALL GC_debug_gcj_malloc(size_t /* lb */,
- void * /* ptr_to_struct_containing_descr */,
- GC_EXTRA_PARAMS)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_debug_gcj_malloc(size_t /* lb */,
+ void * /* ptr_to_struct_containing_descr */,
+ GC_EXTRA_PARAMS);
/* Similar to GC_gcj_malloc, but assumes that a pointer to near the */
/* beginning of the resulting object is always maintained. */
-GC_API void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t /* lb */,
- void * /* ptr_to_struct_containing_descr */)
- GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_gcj_malloc_ignore_off_page(size_t /* lb */,
+ void * /* ptr_to_struct_containing_descr */);
/* The kind numbers of normal and debug gcj objects. */
/* Useful only for debug support, we hope. */
diff --git a/include/gc_inline.h b/include/gc_inline.h
index cc49da22..8d93f44a 100644
--- a/include/gc_inline.h
+++ b/include/gc_inline.h
@@ -68,14 +68,14 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */,
/* amount of code. */
# define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct,\
kind,default_expr,init) \
-{ \
+ do { \
if (GC_EXPECT((granules) >= GC_TINY_FREELISTS,0)) { \
result = (default_expr); \
} else { \
void **my_fl = (tiny_fl) + (granules); \
void *my_entry=*my_fl; \
void *next; \
- \
+ \
while (GC_EXPECT((GC_word)my_entry \
<= (num_direct) + GC_TINY_FREELISTS + 1, 0)) { \
/* Entry contains counter or NULL */ \
@@ -104,8 +104,8 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */,
GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \
GC_ASSERT((kind) == PTRFREE || ((GC_word *)result)[1] == 0); \
out: ; \
- } \
-}
+ } \
+ } while (0)
# define GC_WORDS_TO_WHOLE_GRANULES(n) \
GC_WORDS_TO_GRANULES((n) + GC_GRANULE_WORDS - 1)
@@ -118,29 +118,29 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */,
/* free list array. For single-threaded applications, this may be */
/* a global array. */
# define GC_MALLOC_WORDS(result,n,tiny_fl) \
-{ \
+ do { \
size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
*(void **)(result) = 0); \
-}
+ } while (0)
# define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \
-{ \
+ do { \
size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
PTRFREE, GC_malloc_atomic(grans*GC_GRANULE_BYTES), \
(void)0 /* no initialization */); \
-}
+ } while (0)
/* And once more for two word initialized objects: */
# define GC_CONS(result, first, second, tiny_fl) \
-{ \
+ do { \
size_t grans = GC_WORDS_TO_WHOLE_GRANULES(2); \
GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \
NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \
*(void **)(result) = (void *)(first)); \
((void **)(result))[1] = (void *)(second); \
-}
+ } while (0)
#endif /* !GC_INLINE_H */
diff --git a/include/gc_mark.h b/include/gc_mark.h
index e8d7ece4..2f88f38a 100644
--- a/include/gc_mark.h
+++ b/include/gc_mark.h
@@ -56,7 +56,12 @@
/* not count on the presence of a type descriptor, and must handle this */
/* case correctly somehow. */
#define GC_PROC_BYTES 100
-struct GC_ms_entry;
+
+#ifdef GC_BUILD
+ struct GC_ms_entry;
+#else
+ struct GC_ms_entry { void *opaque; };
+#endif
typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */,
struct GC_ms_entry * /* mark_stack_ptr */,
struct GC_ms_entry * /* mark_stack_limit */,
@@ -103,8 +108,8 @@ typedef struct GC_ms_entry * (*GC_mark_proc)(GC_word * /* addr */,
/* The latter alternative can be used if each */
/* object contains a type descriptor in the */
/* first word. */
- /* Note that in multithreaded environments */
- /* per object descriptors must be located in */
+ /* Note that in the multi-threaded environments */
+ /* per-object descriptors must be located in */
/* either the first two or last two words of */
/* the object, since only those are guaranteed */
/* to be cleared while the allocation lock is */
@@ -164,27 +169,38 @@ GC_API void ** GC_CALL GC_new_free_list_inner(void);
/* Return a new kind, as specified. */
GC_API unsigned GC_CALL GC_new_kind(void ** /* free_list */,
- GC_word /* mark_descriptor_template */,
- int /* add_size_to_descriptor */,
- int /* clear_new_objects */);
+ GC_word /* mark_descriptor_template */,
+ int /* add_size_to_descriptor */,
+ int /* clear_new_objects */) GC_ATTR_NONNULL(1);
/* The last two parameters must be zero or one. */
GC_API unsigned GC_CALL GC_new_kind_inner(void ** /* free_list */,
- GC_word /* mark_descriptor_template */,
- int /* add_size_to_descriptor */,
- int /* clear_new_objects */);
+ GC_word /* mark_descriptor_template */,
+ int /* add_size_to_descriptor */,
+ int /* clear_new_objects */) GC_ATTR_NONNULL(1);
/* Return a new mark procedure identifier, suitable for use as */
/* the first argument in GC_MAKE_PROC. */
GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc);
GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc);
-/* Allocate an object of a given kind. Note that in multithreaded */
+/* Allocate an object of a given kind. By default, there are only */
+/* a few kinds: composite (pointer-free), atomic, uncollectible, etc. */
+/* We claim it is possible for clever client code that understands the */
+/* GC internals to add more, e.g. to communicate object layout */
+/* information to the collector. Note that in the multi-threaded */
/* contexts, this is usually unsafe for kinds that have the descriptor */
/* in the object itself, since there is otherwise a window in which */
/* the descriptor is not correct. Even in the single-threaded case, */
/* we need to be sure that cleared objects on a free list don't */
/* cause a GC crash if they are accidentally traced. */
-GC_API void * GC_CALL GC_generic_malloc(size_t /* lb */, int /* k */);
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t /* lb */,
+ int /* k */);
+
+GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_ignore_off_page(
+ size_t /* lb */, int /* k */);
+ /* As above, but pointers to past the */
+ /* first page of the resulting object */
+ /* are ignored. */
typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */,
char * /* out_buf */);
@@ -207,6 +223,11 @@ GC_API void GC_CALL GC_register_describe_type_fn(int /* kind */,
/* to be used when printing objects */
/* of a particular kind. */
+/* Clear some of the inaccessible part of the stack. Returns its */
+/* argument, so it can be used in a tail call position, hence clearing */
+/* another frame. Argument may be NULL. */
+GC_API void * GC_CALL GC_clear_stack(void *);
+
/* Set and get the client notifier on collections. The client function */
/* is called at the start of every full GC (called with the allocation */
/* lock held). May be 0. This is a really tricky interface to use */
@@ -221,9 +242,24 @@ GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void);
/* Slow/general mark bit manipulation. The caller must hold the */
/* allocation lock. GC_is_marked returns 1 (TRUE) or 0. */
-GC_API int GC_CALL GC_is_marked(const void *);
-GC_API void GC_CALL GC_clear_mark_bit(const void *);
-GC_API void GC_CALL GC_set_mark_bit(const void *);
+GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1);
+GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1);
+
+/* Push everything in the given range onto the mark stack. */
+/* (GC_push_conditional pushes either all or only dirty pages depending */
+/* on the third argument.) */
+GC_API void GC_CALL GC_push_all(char * /* bottom */, char * /* top */);
+GC_API void GC_CALL GC_push_conditional(char * /* bottom */, char * /* top */,
+ int /* bool all */);
+
+/* Set and get the client push-other-roots procedure. A client */
+/* supplied procedure should also call the original procedure. */
+/* Note that both the setter and getter require some external */
+/* synchronization to avoid data race. */
+typedef void (GC_CALLBACK * GC_push_other_roots_proc)(void);
+GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc);
+GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void);
#ifdef __cplusplus
} /* end of extern "C" */
diff --git a/include/gc_pthread_redirects.h b/include/gc_pthread_redirects.h
index b4b2b131..f0ab25e7 100644
--- a/include/gc_pthread_redirects.h
+++ b/include/gc_pthread_redirects.h
@@ -25,7 +25,7 @@
/* that we can locate thread stacks and stop the world. */
/* Note also that the collector cannot always see thread specific data. */
/* Thread specific data should generally consist of pointers to */
-/* uncollectable objects (allocated with GC_malloc_uncollectable, */
+/* uncollectible objects (allocated with GC_malloc_uncollectable, */
/* not the system malloc), which are deallocated using the destructor */
/* facility in thr_keycreate. Alternatively, keep a redundant pointer */
/* to thread specific data on the thread stack. */
@@ -58,7 +58,8 @@ GC_API int GC_pthread_detach(pthread_t);
GC_API int GC_pthread_cancel(pthread_t);
#endif
-#ifdef GC_PTHREAD_EXIT_ATTRIBUTE
+#if defined(GC_PTHREAD_EXIT_ATTRIBUTE) && !defined(GC_PTHREAD_EXIT_DECLARED)
+# define GC_PTHREAD_EXIT_DECLARED
GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE;
#endif
diff --git a/include/gc_tiny_fl.h b/include/gc_tiny_fl.h
index 4684cea5..0382b417 100644
--- a/include/gc_tiny_fl.h
+++ b/include/gc_tiny_fl.h
@@ -47,7 +47,8 @@
/* library that may be shared between applications, since it affects */
/* the binary interface to the library. */
# if defined(__LP64__) || defined (_LP64) || defined(_WIN64) \
- || defined(__s390x__) || defined(__x86_64__) \
+ || defined(__s390x__) \
+ || (defined(__x86_64__) && !defined(__ILP32__)) \
|| defined(__alpha__) || defined(__powerpc64__) \
|| defined(__arch64__)
# define GC_GRANULE_BYTES 16
diff --git a/include/gc_typed.h b/include/gc_typed.h
index 0eeb2538..8261cd7b 100644
--- a/include/gc_typed.h
+++ b/include/gc_typed.h
@@ -12,6 +12,7 @@
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
+
/*
* Some simple primitives for allocation with explicit type information.
* Facilities for dynamic type inference may be added later.
@@ -76,19 +77,21 @@ GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * /* GC_bitmap bm */,
/* ... */
/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T)); */
-GC_API void * GC_CALL GC_malloc_explicitly_typed(size_t /* size_in_bytes */,
- GC_descr /* d */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_explicitly_typed(size_t /* size_in_bytes */,
+ GC_descr /* d */);
/* Allocate an object whose layout is described by d. */
/* The resulting object MAY NOT BE PASSED TO REALLOC. */
/* The returned object is cleared. */
-GC_API void * GC_CALL GC_malloc_explicitly_typed_ignore_off_page(
- size_t /* size_in_bytes */,
- GC_descr /* d */);
+GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
+ GC_malloc_explicitly_typed_ignore_off_page(size_t /* size_in_bytes */,
+ GC_descr /* d */);
-GC_API void * GC_CALL GC_calloc_explicitly_typed(size_t /* nelements */,
- size_t /* element_size_in_bytes */,
- GC_descr /* d */);
+GC_API GC_ATTR_MALLOC void * GC_CALL
+ GC_calloc_explicitly_typed(size_t /* nelements */,
+ size_t /* element_size_in_bytes */,
+ GC_descr /* d */);
/* Allocate an array of nelements elements, each of the */
/* given size, and with the given descriptor. */
/* The element size must be a multiple of the byte */
diff --git a/include/gc_version.h b/include/gc_version.h
index 51ef0e15..38afc0f3 100644
--- a/include/gc_version.h
+++ b/include/gc_version.h
@@ -23,7 +23,7 @@
/* it to keep the old-style build process working. */
#define GC_TMP_VERSION_MAJOR 7
#define GC_TMP_VERSION_MINOR 3
-#define GC_TMP_ALPHA_VERSION 1 /* 7.3alpha1 */
+#define GC_TMP_ALPHA_VERSION 3 /* 7.3alpha3 */
#ifndef GC_NOT_ALPHA
# define GC_NOT_ALPHA 0xff
diff --git a/include/new_gc_alloc.h b/include/new_gc_alloc.h
index bf09780e..94020cdd 100644
--- a/include/new_gc_alloc.h
+++ b/include/new_gc_alloc.h
@@ -128,14 +128,14 @@ public:
// real one must be updated with a procedure call.
static size_t GC_bytes_recently_allocd;
- // Same for uncollectable memory. Not yet reflected in either
+ // Same for uncollectible memory. Not yet reflected in either
// GC_bytes_recently_allocd or GC_non_gc_bytes.
static size_t GC_uncollectable_bytes_recently_allocd;
// Similar counter for explicitly deallocated memory.
static size_t GC_bytes_recently_freed;
- // Again for uncollectable memory.
+ // Again for uncollectible memory.
static size_t GC_uncollectable_bytes_recently_freed;
static void * GC_out_of_line_malloc(size_t nwords, int kind);
@@ -247,7 +247,7 @@ class single_client_gc_alloc_template {
typedef single_client_gc_alloc_template<0> single_client_gc_alloc;
-// Once more, for uncollectable objects.
+// Once more, for uncollectible objects.
template <int dummy>
class single_client_traceable_alloc_template {
public:
diff --git a/include/private/darwin_semaphore.h b/include/private/darwin_semaphore.h
index 379a7f71..da97b39d 100644
--- a/include/private/darwin_semaphore.h
+++ b/include/private/darwin_semaphore.h
@@ -19,15 +19,13 @@
#define GC_DARWIN_SEMAPHORE_H
#if !defined(GC_DARWIN_THREADS)
-#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
+# error darwin_semaphore.h included with GC_DARWIN_THREADS not defined
#endif
-/*
- This is a very simple semaphore implementation for darwin. It
- is implemented in terms of pthreads calls so it isn't async signal
- safe. This isn't a problem because signals aren't used to
- suspend threads on darwin.
-*/
+/* This is a very simple semaphore implementation for Darwin. It is */
+/* implemented in terms of pthread calls so it is not async signal */
+/* safe. But this is not a problem because signals are not used to */
+/* suspend threads on Darwin. */
typedef struct {
pthread_mutex_t mutex;
@@ -35,7 +33,7 @@ typedef struct {
int value;
} sem_t;
-static int sem_init(sem_t *sem, int pshared, int value) {
+GC_INLINE int sem_init(sem_t *sem, int pshared, int value) {
int ret;
if(pshared)
ABORT("sem_init with pshared set");
@@ -48,7 +46,7 @@ static int sem_init(sem_t *sem, int pshared, int value) {
return 0;
}
-static int sem_post(sem_t *sem) {
+GC_INLINE int sem_post(sem_t *sem) {
if(pthread_mutex_lock(&sem->mutex) < 0)
return -1;
sem->value++;
@@ -61,7 +59,7 @@ static int sem_post(sem_t *sem) {
return 0;
}
-static int sem_wait(sem_t *sem) {
+GC_INLINE int sem_wait(sem_t *sem) {
if(pthread_mutex_lock(&sem->mutex) < 0)
return -1;
while(sem->value == 0) {
@@ -73,7 +71,7 @@ static int sem_wait(sem_t *sem) {
return 0;
}
-static int sem_destroy(sem_t *sem) {
+GC_INLINE int sem_destroy(sem_t *sem) {
int ret;
ret = pthread_cond_destroy(&sem->cond);
if(ret < 0) return -1;
diff --git a/include/private/dbg_mlc.h b/include/private/dbg_mlc.h
index 7b2969d2..deda69c7 100644
--- a/include/private/dbg_mlc.h
+++ b/include/private/dbg_mlc.h
@@ -112,7 +112,7 @@ typedef struct {
#else
/* Add space for END_FLAG, but use any extra space that was already */
/* added to catch off-the-end pointers. */
- /* For uncollectable objects, the extra byte is not added. */
+ /* For uncollectible objects, the extra byte is not added. */
# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word))
# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES)
#endif
diff --git a/include/private/gc_hdrs.h b/include/private/gc_hdrs.h
index ad46de0c..89914284 100644
--- a/include/private/gc_hdrs.h
+++ b/include/private/gc_hdrs.h
@@ -104,7 +104,7 @@ typedef struct hce {
/* Returns zero if p points to somewhere other than the first page */
/* of an object, and it is not a valid pointer to the object. */
#define HC_GET_HDR(p, hhdr, source, exit_label) \
- { \
+ do { \
hdr_cache_entry * hce = HCE(p); \
if (EXPECT(HCE_VALID_FOR(hce, p), TRUE)) { \
HC_HIT(); \
@@ -113,7 +113,7 @@ typedef struct hce {
hhdr = HEADER_CACHE_MISS(p, hce, source); \
if (0 == hhdr) goto exit_label; \
} \
- }
+ } while (0)
typedef struct bi {
hdr * index[BOTTOM_SZ];
@@ -163,33 +163,41 @@ typedef struct bi {
# else
# define HDR(p) HDR_INNER(p)
# endif
-# define GET_BI(p, bottom_indx) (bottom_indx) = BI(p)
-# define GET_HDR(p, hhdr) (hhdr) = HDR(p)
-# define SET_HDR(p, hhdr) HDR_INNER(p) = (hhdr)
-# define GET_HDR_ADDR(p, ha) (ha) = &(HDR_INNER(p))
+# define GET_BI(p, bottom_indx) (void)((bottom_indx) = BI(p))
+# define GET_HDR(p, hhdr) (void)((hhdr) = HDR(p))
+# define SET_HDR(p, hhdr) (void)(HDR_INNER(p) = (hhdr))
+# define GET_HDR_ADDR(p, ha) (void)((ha) = &HDR_INNER(p))
#else /* hash */
/* Hash function for tree top level */
# define TL_HASH(hi) ((hi) & (TOP_SZ - 1))
/* Set bottom_indx to point to the bottom index for address p */
# define GET_BI(p, bottom_indx) \
- { \
+ do { \
register word hi = \
(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \
register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \
while (_bi -> key != hi && _bi != GC_all_nils) \
_bi = _bi -> hash_link; \
(bottom_indx) = _bi; \
- }
+ } while (0)
# define GET_HDR_ADDR(p, ha) \
- { \
+ do { \
register bottom_index * bi; \
GET_BI(p, bi); \
- (ha) = &(HDR_FROM_BI(bi, p)); \
- }
-# define GET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \
- (hhdr) = *_ha; }
-# define SET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \
- *_ha = (hhdr); }
+ (ha) = &HDR_FROM_BI(bi, p); \
+ } while (0)
+# define GET_HDR(p, hhdr) \
+ do { \
+ register hdr ** _ha; \
+ GET_HDR_ADDR(p, _ha); \
+ (hhdr) = *_ha; \
+ } while (0)
+# define SET_HDR(p, hhdr) \
+ do { \
+ register hdr ** _ha; \
+ GET_HDR_ADDR(p, _ha); \
+ *_ha = (hhdr); \
+ } while (0)
# define HDR(p) GC_find_header((ptr_t)(p))
#endif
diff --git a/include/private/gc_locks.h b/include/private/gc_locks.h
index a5d9c35f..328d13c2 100644
--- a/include/private/gc_locks.h
+++ b/include/private/gc_locks.h
@@ -32,7 +32,6 @@
# include "atomic_ops.h"
# endif
- GC_API void GC_CALL GC_noop1(word);
# ifdef PCR
# include <base/PCR_Base.h>
# include <th/PCR_Th.h>
@@ -43,15 +42,9 @@
# define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml)
# endif
-# if !defined(AO_HAVE_test_and_set_acquire) && defined(GC_PTHREADS)
-# define USE_PTHREAD_LOCKS
-# endif
-
-# if defined(GC_WIN32_THREADS) && defined(GC_PTHREADS)
-# define USE_PTHREAD_LOCKS
-# endif
-
-# if defined(GC_RTEMS_PTHREADS)
+# if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_RTEMS_PTHREADS) \
+ || defined(SN_TARGET_PS3) || defined(GC_WIN32_THREADS) \
+ || defined(LINT2)) && defined(GC_PTHREADS)
# define USE_PTHREAD_LOCKS
# endif
@@ -62,30 +55,26 @@
# define NOSERVICE
# include <windows.h>
# define NO_THREAD (DWORD)(-1)
- GC_EXTERN DWORD GC_lock_holder;
GC_EXTERN CRITICAL_SECTION GC_allocate_ml;
# ifdef GC_ASSERTIONS
-# define UNCOND_LOCK() \
- { EnterCriticalSection(&GC_allocate_ml); \
+ GC_EXTERN DWORD GC_lock_holder;
+# define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId()
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() (!GC_need_to_lock \
+ || GC_lock_holder == GetCurrentThreadId())
+# define I_DONT_HOLD_LOCK() (!GC_need_to_lock \
+ || GC_lock_holder != GetCurrentThreadId())
+# define UNCOND_LOCK() \
+ { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+ EnterCriticalSection(&GC_allocate_ml); \
SET_LOCK_HOLDER(); }
-# define UNCOND_UNLOCK() \
+# define UNCOND_UNLOCK() \
{ GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
LeaveCriticalSection(&GC_allocate_ml); }
# else
# define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml)
# define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml)
# endif /* !GC_ASSERTIONS */
-# define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId()
-# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
-# define I_HOLD_LOCK() (!GC_need_to_lock \
- || GC_lock_holder == GetCurrentThreadId())
-# define I_DONT_HOLD_LOCK() (!GC_need_to_lock \
- || GC_lock_holder != GetCurrentThreadId())
-# elif defined(SN_TARGET_PS3)
-# include <pthread.h>
- GC_EXTERN pthread_mutex_t GC_allocate_ml;
-# define LOCK() pthread_mutex_lock(&GC_allocate_ml)
-# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
# elif defined(GC_PTHREADS)
# include <pthread.h>
@@ -128,7 +117,8 @@
/* GC_call_with_alloc_lock. */
# ifdef GC_ASSERTIONS
# define UNCOND_LOCK() \
- { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
+ { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+ if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
GC_lock(); \
SET_LOCK_HOLDER(); }
# define UNCOND_UNLOCK() \
@@ -136,74 +126,86 @@
AO_CLEAR(&GC_allocate_lock); }
# else
# define UNCOND_LOCK() \
- { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
+ { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+ if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
GC_lock(); }
# define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock)
# endif /* !GC_ASSERTIONS */
-# else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
+# else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
# ifndef USE_PTHREAD_LOCKS
# define USE_PTHREAD_LOCKS
# endif
-# endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCK */
+# endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
# ifdef USE_PTHREAD_LOCKS
# include <pthread.h>
GC_EXTERN pthread_mutex_t GC_allocate_ml;
# ifdef GC_ASSERTIONS
-# define UNCOND_LOCK() { GC_lock(); SET_LOCK_HOLDER(); }
+# define UNCOND_LOCK() { GC_ASSERT(I_DONT_HOLD_LOCK()); \
+ GC_lock(); SET_LOCK_HOLDER(); }
# define UNCOND_UNLOCK() \
{ GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \
pthread_mutex_unlock(&GC_allocate_ml); }
# else /* !GC_ASSERTIONS */
# if defined(NO_PTHREAD_TRYLOCK)
-# define UNCOND_LOCK() GC_lock()
-# else /* !defined(NO_PTHREAD_TRYLOCK) */
-# define UNCOND_LOCK() \
- { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \
- GC_lock(); }
+# ifdef USE_SPIN_LOCK
+# define UNCOND_LOCK() GC_lock()
+# else
+# define UNCOND_LOCK() pthread_mutex_lock(&GC_allocate_ml)
+# endif
+# else
+# define UNCOND_LOCK() \
+ { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \
+ GC_lock(); }
# endif
# define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
# endif /* !GC_ASSERTIONS */
# endif /* USE_PTHREAD_LOCKS */
-# define SET_LOCK_HOLDER() \
+# ifdef GC_ASSERTIONS
+ GC_EXTERN unsigned long GC_lock_holder;
+# define SET_LOCK_HOLDER() \
GC_lock_holder = NUMERIC_THREAD_ID(pthread_self())
-# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
-# define I_HOLD_LOCK() \
+# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD
+# define I_HOLD_LOCK() \
(!GC_need_to_lock || \
GC_lock_holder == NUMERIC_THREAD_ID(pthread_self()))
-# ifndef NUMERIC_THREAD_ID_UNIQUE
-# define I_DONT_HOLD_LOCK() 1 /* Conservatively say yes */
-# else
-# define I_DONT_HOLD_LOCK() \
+# ifndef NUMERIC_THREAD_ID_UNIQUE
+# define I_DONT_HOLD_LOCK() 1 /* Conservatively say yes */
+# else
+# define I_DONT_HOLD_LOCK() \
(!GC_need_to_lock \
|| GC_lock_holder != NUMERIC_THREAD_ID(pthread_self()))
-# endif
+# endif
+# endif /* GC_ASSERTIONS */
GC_EXTERN volatile GC_bool GC_collecting;
# define ENTER_GC() GC_collecting = 1;
# define EXIT_GC() GC_collecting = 0;
GC_INNER void GC_lock(void);
- GC_EXTERN unsigned long GC_lock_holder;
-# if defined(GC_ASSERTIONS) && defined(PARALLEL_MARK)
- GC_EXTERN unsigned long GC_mark_lock_holder;
-# endif
# endif /* GC_PTHREADS with linux_threads.c implementation */
GC_EXTERN GC_bool GC_need_to_lock;
# else /* !THREADS */
-# define LOCK()
-# define UNLOCK()
-# define SET_LOCK_HOLDER()
-# define UNSET_LOCK_HOLDER()
-# define I_HOLD_LOCK() TRUE
-# define I_DONT_HOLD_LOCK() TRUE
+# define LOCK() (void)0
+# define UNLOCK() (void)0
+# ifdef GC_ASSERTIONS
+# define I_HOLD_LOCK() TRUE
+# define I_DONT_HOLD_LOCK() TRUE
/* Used only in positive assertions or to test whether */
/* we still need to acquire the lock. TRUE works in */
/* either case. */
+# endif
# endif /* !THREADS */
#if defined(UNCOND_LOCK) && !defined(LOCK)
+# ifdef LINT2
+ /* Instruct code analysis tools not to care about GC_need_to_lock */
+ /* influence to LOCK/UNLOCK semantic. */
+# define LOCK() UNCOND_LOCK()
+# define UNLOCK() UNCOND_UNLOCK()
+# else
/* At least two thread running; need to lock. */
-# define LOCK() { if (GC_need_to_lock) UNCOND_LOCK(); }
-# define UNLOCK() { if (GC_need_to_lock) UNCOND_UNLOCK(); }
+# define LOCK() do { if (GC_need_to_lock) UNCOND_LOCK(); } while (0)
+# define UNLOCK() do { if (GC_need_to_lock) UNCOND_UNLOCK(); } while (0)
+# endif
#endif
# ifndef ENTER_GC
diff --git a/include/private/gc_pmark.h b/include/private/gc_pmark.h
index 42fbb204..9c3aee54 100644
--- a/include/private/gc_pmark.h
+++ b/include/private/gc_pmark.h
@@ -23,7 +23,7 @@
#define GC_PMARK_H
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef GC_BUILD
@@ -69,24 +69,8 @@ GC_EXTERN unsigned GC_n_mark_procs;
/* Number of mark stack entries to discard on overflow. */
#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8)
-typedef struct GC_ms_entry {
- ptr_t mse_start; /* First word of object, word aligned. */
- GC_word mse_descr; /* Descriptor; low order two bits are tags, */
- /* as described in gc_mark.h. */
-} mse;
-
GC_EXTERN size_t GC_mark_stack_size;
-GC_EXTERN mse * GC_mark_stack_limit;
-
-#ifdef PARALLEL_MARK
- GC_EXTERN mse * volatile GC_mark_stack_top;
-#else
- GC_EXTERN mse * GC_mark_stack_top;
-#endif
-
-GC_EXTERN mse * GC_mark_stack;
-
#ifdef PARALLEL_MARK
/*
* Allow multiple threads to participate in the marking process.
@@ -131,18 +115,18 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp);
/* Push the object obj with corresponding heap block header hhdr onto */
/* the mark stack. */
#define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \
-{ \
+ do { \
register word _descr = (hhdr) -> hb_descr; \
GC_ASSERT(!HBLK_IS_FREE(hhdr)); \
if (_descr != 0) { \
mark_stack_top++; \
- if (mark_stack_top >= mark_stack_limit) { \
+ if ((word)mark_stack_top >= (word)(mark_stack_limit)) { \
mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \
} \
mark_stack_top -> mse_start = (obj); \
- mark_stack_top -> mse_descr = _descr; \
+ mark_stack_top -> mse_descr.w = _descr; \
} \
-}
+ } while (0)
/* Push the contents of current onto the mark stack if it is a valid */
/* ptr to a currently unmarked object. Mark it. */
@@ -150,13 +134,13 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp);
/* generate the exit_label transparently. */
#define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \
source, exit_label) \
-{ \
+ do { \
hdr * my_hhdr; \
HC_GET_HDR(current, my_hhdr, source, exit_label); \
PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
source, exit_label, my_hhdr, TRUE); \
-exit_label: ; \
-}
+ exit_label: ; \
+ } while (0)
/* Set mark bit, exit if it was already set. */
#ifdef USE_MARK_BYTES
@@ -164,39 +148,39 @@ exit_label: ; \
/* the bit twice in the concurrent case. This can result in the */
/* object being pushed twice. But that's only a performance issue. */
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
- { \
+ do { \
char * mark_byte_addr = (char *)hhdr -> hb_marks + (bit_no); \
if (*mark_byte_addr) goto exit_label; \
*mark_byte_addr = 1; \
- }
+ } while (0)
#else
# ifdef PARALLEL_MARK
/* This is used only if we explicitly set USE_MARK_BITS. */
/* The following may fail to exit even if the bit was already set. */
/* For our uses, that's benign: */
# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
- { \
+ do { \
if (!(*(addr) & (bits))) { \
- AO_or((AO_t *)(addr), (bits)); \
+ AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \
} else { \
goto exit_label; \
} \
- }
+ } while (0)
# else
# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \
- { \
+ do { \
word old = *(addr); \
word my_bits = (bits); \
if (old & my_bits) goto exit_label; \
*(addr) = (old | my_bits); \
- }
+ } while (0)
# endif /* !PARALLEL_MARK */
# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \
- { \
+ do { \
word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(bit_no); \
OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(bit_no), \
exit_label); \
- }
+ } while (0)
#endif /* !USE_MARK_BYTES */
#ifdef PARALLEL_MARK
@@ -217,17 +201,20 @@ exit_label: ; \
#endif
#if defined(I386) && defined(__GNUC__)
-# define LONG_MULT(hprod, lprod, x, y) { \
+# define LONG_MULT(hprod, lprod, x, y) \
+ do { \
__asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \
: "g"(y), "0"(x)); \
- }
+ } while (0)
#else
-# define LONG_MULT(hprod, lprod, x, y) { \
+# define LONG_MULT(hprod, lprod, x, y) \
+ do { \
unsigned long long prod = (unsigned long long)(x) \
* (unsigned long long)(y); \
+ GC_STATIC_ASSERT(sizeof(x) + sizeof(y) <= sizeof(prod)); \
hprod = prod >> 32; \
lprod = (unsigned32)prod; \
- }
+ } while (0)
#endif /* !I386 */
/* If the mark bit corresponding to current is not set, set it, and */
@@ -242,7 +229,7 @@ exit_label: ; \
#ifdef MARK_BIT_PER_GRANULE
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
source, exit_label, hhdr, do_offset_check) \
-{ \
+ do { \
size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
/* displ is always within range. If current doesn't point to */ \
/* first block, then we are in the all_interior_pointers case, and */ \
@@ -271,7 +258,7 @@ exit_label: ; \
gran_displ = 0; \
GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
hhdr -> hb_block == HBLKPTR(current)); \
- GC_ASSERT((ptr_t)(hhdr -> hb_block) <= (ptr_t) current); \
+ GC_ASSERT((word)hhdr->hb_block <= (word)(current)); \
} else { \
size_t obj_displ = GRANULES_TO_BYTES(gran_offset) \
+ byte_offset; \
@@ -285,24 +272,24 @@ exit_label: ; \
} \
GC_ASSERT(hhdr == GC_find_header(base)); \
GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); \
- TRACE(source, GC_log_printf("GC:%u: passed validity tests\n", \
+ TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \
(unsigned)GC_gc_no)); \
SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ, exit_label); \
- TRACE(source, GC_log_printf("GC:%u: previously unmarked\n", \
+ TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \
(unsigned)GC_gc_no)); \
TRACE_TARGET(base, \
- GC_log_printf("GC:%u: marking %p from %p instead\n", \
+ GC_log_printf("GC #%u: marking %p from %p instead\n", \
(unsigned)GC_gc_no, base, source)); \
INCR_MARKS(hhdr); \
GC_STORE_BACK_PTR((ptr_t)source, base); \
PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
-}
+ } while (0)
#endif /* MARK_BIT_PER_GRANULE */
#ifdef MARK_BIT_PER_OBJ
# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \
source, exit_label, hhdr, do_offset_check) \
-{ \
+ do { \
size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\
unsigned32 low_prod, high_prod; \
unsigned32 inv_sz = hhdr -> hb_inv_sz; \
@@ -310,7 +297,7 @@ exit_label: ; \
LONG_MULT(high_prod, low_prod, displ, inv_sz); \
/* product is > and within sz_in_bytes of displ * sz_in_bytes * 2**32 */ \
if (EXPECT(low_prod >> 16 != 0, FALSE)) { \
- FIXME: fails if offset is a multiple of HBLKSIZE which becomes 0 \
+ /* FIXME: fails if offset is a multiple of HBLKSIZE which becomes 0 */ \
if (inv_sz == LARGE_INV_SZ) { \
size_t obj_displ; \
base = (ptr_t)(hhdr -> hb_block); \
@@ -327,7 +314,7 @@ exit_label: ; \
} \
GC_ASSERT(hhdr -> hb_sz > HBLKSIZE || \
hhdr -> hb_block == HBLKPTR(current)); \
- GC_ASSERT((ptr_t)(hhdr -> hb_block) < (ptr_t) current); \
+ GC_ASSERT((word)hhdr->hb_block < (word)(current)); \
} else { \
/* Accurate enough if HBLKSIZE <= 2**15. */ \
GC_STATIC_ASSERT(HBLKSIZE <= (1 << 15)); \
@@ -341,19 +328,19 @@ exit_label: ; \
} \
/* May get here for pointer to start of block not at */ \
/* beginning of object. If so, it's valid, and we're fine. */ \
- GC_ASSERT(high_prod >= 0 && high_prod <= HBLK_OBJS(hhdr -> hb_sz)); \
- TRACE(source, GC_log_printf("GC:%u: passed validity tests\n", \
+ GC_ASSERT(high_prod <= HBLK_OBJS(hhdr -> hb_sz)); \
+ TRACE(source, GC_log_printf("GC #%u: passed validity tests\n", \
(unsigned)GC_gc_no)); \
SET_MARK_BIT_EXIT_IF_SET(hhdr, high_prod, exit_label); \
- TRACE(source, GC_log_printf("GC:%u: previously unmarked\n", \
+ TRACE(source, GC_log_printf("GC #%u: previously unmarked\n", \
(unsigned)GC_gc_no)); \
TRACE_TARGET(base, \
- GC_log_printf("GC:%u: marking %p from %p instead\n", \
+ GC_log_printf("GC #%u: marking %p from %p instead\n", \
(unsigned)GC_gc_no, base, source)); \
INCR_MARKS(hhdr); \
GC_STORE_BACK_PTR((ptr_t)source, base); \
PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \
-}
+ } while (0)
#endif /* MARK_BIT_PER_OBJ */
#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
@@ -375,36 +362,36 @@ exit_label: ; \
#if NEED_FIXUP_POINTER
/* Try both the raw version and the fixed up one. */
# define GC_PUSH_ONE_STACK(p, source) \
- if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
- && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
+ do { \
+ if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+ && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
PUSH_ONE_CHECKED_STACK(p, source); \
} \
FIXUP_POINTER(p); \
- if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
- && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
+ if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+ && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
PUSH_ONE_CHECKED_STACK(p, source); \
- }
+ } \
+ } while (0)
#else /* !NEED_FIXUP_POINTER */
# define GC_PUSH_ONE_STACK(p, source) \
- if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
- && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
+ do { \
+ if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+ && (word)(p) < (word)GC_greatest_plausible_heap_addr) { \
PUSH_ONE_CHECKED_STACK(p, source); \
- }
+ } \
+ } while (0)
#endif
-
-/*
- * As above, but interior pointer recognition as for
- * normal heap pointers.
- */
-#define GC_PUSH_ONE_HEAP(p,source) \
- FIXUP_POINTER(p); \
- if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \
- && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \
- GC_mark_stack_top = GC_mark_and_push( \
- (void *)(p), GC_mark_stack_top, \
- GC_mark_stack_limit, (void * *)(source)); \
- }
+/* As above, but interior pointer recognition as for normal heap pointers. */
+#define GC_PUSH_ONE_HEAP(p,source,mark_stack_top) \
+ do { \
+ FIXUP_POINTER(p); \
+ if ((word)(p) >= (word)GC_least_plausible_heap_addr \
+ && (word)(p) < (word)GC_greatest_plausible_heap_addr) \
+ mark_stack_top = GC_mark_and_push((void *)(p), mark_stack_top, \
+ GC_mark_stack_limit, (void * *)(source)); \
+ } while (0)
/* Mark starting at mark stack entry top (incl.) down to */
/* mark stack entry bottom (incl.). Stop after performing */
@@ -417,6 +404,8 @@ GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit);
GC_mark_stack, \
GC_mark_stack + GC_mark_stack_size);
+#define GC_mark_stack_empty() ((word)GC_mark_stack_top < (word)GC_mark_stack)
+
/*
* Mark from one finalizable object using the specified
* mark proc. May not mark the object pointed to by
@@ -427,14 +416,14 @@ GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit);
* FIXME: Why do we need the GC_mark_state test below?
*/
#define GC_MARK_FO(real_ptr, mark_proc) \
-{ \
+ do { \
(*(mark_proc))(real_ptr); \
while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK(); \
if (GC_mark_state != MS_NONE) { \
GC_set_mark_bit(real_ptr); \
- while (!GC_mark_some((ptr_t)0)) {} \
+ while (!GC_mark_some((ptr_t)0)) { /* empty */ } \
} \
-}
+ } while (0)
GC_EXTERN GC_bool GC_mark_stack_too_small;
/* We need a larger mark stack. May be */
@@ -466,7 +455,7 @@ typedef int mark_state_t; /* Current state of marking, as follows:*/
/* grungy objects above scan_ptr. */
#define MS_PUSH_UNCOLLECTABLE 2 /* I holds, except that marked */
- /* uncollectable objects above scan_ptr */
+ /* uncollectible objects above scan_ptr */
/* may point to unmarked objects. */
/* Roots may point to unmarked objects */
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index 8ab53a51..f80cd94e 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -19,7 +19,7 @@
#define GC_PRIVATE_H
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#ifndef GC_BUILD
@@ -100,8 +100,8 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
/* for the GC-scope function definitions and prototypes. Must not be */
/* used in gcconfig.h. Shouldn't be used for the debugging-only */
/* functions. Currently, not used for the functions declared in or */
- /* called from the "dated" source files (pcr_interface.c, specific.c */
- /* and in the "extra" folder). */
+ /* called from the "dated" source files (pcr_interface.c and files */
+ /* located in the "extra" folder). */
# if defined(GC_DLL) && defined(__GNUC__) && !defined(MSWIN32) \
&& !defined(MSWINCE) && !defined(CYGWIN32)
# if __GNUC__ >= 4
@@ -130,7 +130,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
#endif
#ifndef GC_ATTR_UNUSED
-# if __GNUC__ >= 4
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define GC_ATTR_UNUSED __attribute__((__unused__))
# else
# define GC_ATTR_UNUSED /* empty */
@@ -145,9 +145,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
#endif /* __GNUC__ */
#ifdef HAVE_CONFIG_H
- /* The `inline' keyword as determined by Autoconf's `AC_C_INLINE'. */
+ /* The "inline" keyword is determined by Autoconf AC_C_INLINE. */
# define GC_INLINE static inline
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__DMC__) \
+ || ((__GNUC__ >= 3) && defined(__STRICT_ANSI__)) \
|| defined(__WATCOMC__)
# define GC_INLINE static __inline
#elif (__GNUC__ >= 3) || defined(__sun)
@@ -184,13 +185,14 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
# ifdef STACK_GROWS_DOWN
# define COOLER_THAN >
# define HOTTER_THAN <
-# define MAKE_COOLER(x,y) if ((x)+(y) > (x)) {(x) += (y);} \
- else {(x) = (ptr_t)ONES;}
+# define MAKE_COOLER(x,y) if ((word)((x) + (y)) > (word)(x)) {(x) += (y);} \
+ else (x) = (ptr_t)ONES
# define MAKE_HOTTER(x,y) (x) -= (y)
# else
# define COOLER_THAN <
# define HOTTER_THAN >
-# define MAKE_COOLER(x,y) if ((x)-(y) < (x)) {(x) -= (y);} else {(x) = 0;}
+# define MAKE_COOLER(x,y) if ((word)((x) - (y)) < (word)(x)) {(x) -= (y);} \
+ else (x) = 0
# define MAKE_HOTTER(x,y) (x) += (y)
# endif
@@ -232,7 +234,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
/* 1. There are probably no interesting, portable, */
/* strictly ANSI conforming C programs. */
/* 2. This option makes it hard for the collector */
- /* to allocate space that is not ``pointed to'' */
+ /* to allocate space that is not "pointed to" */
/* by integers, etc. Under SunOS 4.X with a */
/* statically linked libc, we empirically */
/* observed that it would be difficult to */
@@ -247,7 +249,29 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
/* through GC_all_interior_pointers. */
-#define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers()
+#ifndef GC_NO_FINALIZATION
+# define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers()
+ GC_INNER void GC_notify_or_invoke_finalizers(void);
+ /* If GC_finalize_on_demand is not set, invoke */
+ /* eligible finalizers. Otherwise: */
+ /* Call *GC_finalizer_notifier if there are */
+ /* finalizers to be run, and we haven't called */
+ /* this procedure yet this GC cycle. */
+
+ GC_INNER void GC_push_finalizer_structures(void);
+ GC_INNER void GC_finalize(void);
+ /* Perform all indicated finalization actions */
+ /* on unmarked objects. */
+ /* Unreachable finalizable objects are enqueued */
+ /* for processing by GC_invoke_finalizers. */
+ /* Invoked with lock. */
+
+# ifndef SMALL_CONFIG
+ GC_INNER void GC_print_finalization_stats(void);
+# endif
+#else
+# define GC_INVOKE_FINALIZERS() (void)0
+#endif /* GC_NO_FINALIZATION */
#if !defined(DONT_ADD_BYTE_AT_END)
# ifdef LINT2
@@ -323,9 +347,12 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
# undef GET_TIME
# undef MS_TIME_DIFF
# define CLOCK_TYPE struct timeval
-# define GET_TIME(x) { struct rusage rusage; \
- getrusage (RUSAGE_SELF, &rusage); \
- x = rusage.ru_utime; }
+# define GET_TIME(x) \
+ do { \
+ struct rusage rusage; \
+ getrusage(RUSAGE_SELF, &rusage); \
+ x = rusage.ru_utime; \
+ } while (0)
# define MS_TIME_DIFF(a,b) ((unsigned long)(a.tv_sec - b.tv_sec) * 1000 \
+ (unsigned long)(a.tv_usec - b.tv_usec) / 1000)
#elif defined(MSWIN32) || defined(MSWINCE)
@@ -336,7 +363,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
# include <windows.h>
# include <winbase.h>
# define CLOCK_TYPE DWORD
-# define GET_TIME(x) x = GetTickCount()
+# define GET_TIME(x) (void)(x = GetTickCount())
# define MS_TIME_DIFF(a,b) ((long)((a)-(b)))
#else /* !MSWIN32, !MSWINCE, !BSD_TIME */
# include <time.h>
@@ -357,7 +384,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
/* microseconds (which are not really clock ticks). */
# endif
# define CLOCK_TYPE clock_t
-# define GET_TIME(x) x = clock()
+# define GET_TIME(x) (void)(x = clock())
# define MS_TIME_DIFF(a,b) (CLOCKS_PER_SEC % 1000 == 0 ? \
(unsigned long)((a) - (b)) / (unsigned long)(CLOCKS_PER_SEC / 1000) \
: ((unsigned long)((a) - (b)) * 1000) / (unsigned long)CLOCKS_PER_SEC)
@@ -444,7 +471,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
# ifdef SMALL_CONFIG
# define GC_on_abort(msg) (void)0 /* be silent on abort */
# else
- GC_API_PRIV void GC_on_abort(const char * msg);
+ GC_API_PRIV GC_abort_func GC_on_abort;
# endif /* !SMALL_CONFIG */
# if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2))
/* A more user-friendly abort after showing fatal message. */
@@ -453,24 +480,44 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
# elif defined(MSWINCE) && defined(NO_DEBUGGING)
# define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1))
# elif defined(MSWIN32) || defined(MSWINCE)
-# define ABORT(msg) (GC_on_abort(msg), DebugBreak())
- /* Note that on a WinCE box, this could be silently */
- /* ignored (i.e., the program is not aborted). */
+# define ABORT(msg) { GC_on_abort(msg); DebugBreak(); }
+ /* Note that: on a WinCE box, this could be silently */
+ /* ignored (i.e., the program is not aborted); */
+ /* DebugBreak is a statement in some toolchains. */
# else
# define ABORT(msg) (GC_on_abort(msg), abort())
# endif /* !MSWIN32 */
# endif /* !PCR */
+/* For abort message with 1-3 arguments. C_msg and C_fmt should be */
+/* literals. C_msg should not contain format specifiers. Arguments */
+/* should match their format specifiers. */
+#define ABORT_ARG1(C_msg, C_fmt, arg1) \
+ do { \
+ GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1); \
+ ABORT(C_msg); \
+ } while (0)
+#define ABORT_ARG2(C_msg, C_fmt, arg1, arg2) \
+ do { \
+ GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1, arg2); \
+ ABORT(C_msg); \
+ } while (0)
+#define ABORT_ARG3(C_msg, C_fmt, arg1, arg2, arg3) \
+ do { \
+ GC_COND_LOG_PRINTF(C_msg /* + */ C_fmt, arg1, arg2, arg3); \
+ ABORT(C_msg); \
+ } while (0)
+
/* Same as ABORT but does not have 'no-return' attribute. */
-/* ABORT on dummy condition (which is always true). */
-#define ABORT_RET(msg) { if ((signed_word)GC_current_warn_proc != -1) \
- ABORT(msg); }
+/* ABORT on a dummy condition (which is always true). */
+#define ABORT_RET(msg) \
+ if ((signed_word)GC_current_warn_proc == -1) {} else ABORT(msg)
/* Exit abnormally, but without making a mess (e.g. out of memory) */
# ifdef PCR
# define EXIT() PCR_Base_Exit(1,PCR_waitForever)
# else
-# define EXIT() (void)exit(1)
+# define EXIT() (GC_on_abort(NULL), exit(1 /* EXIT_FAILURE */))
# endif
/* Print warning message, e.g. almost out of memory. */
@@ -806,14 +853,24 @@ typedef word page_hash_table[PHT_SIZE];
#ifdef PARALLEL_MARK
# include "atomic_ops.h"
- typedef AO_t counter_t;
+# define counter_t volatile AO_t
#else
typedef size_t counter_t;
-# if defined(THREADS) && defined(MPROTECT_VDB)
+# if defined(THREADS) && (defined(MPROTECT_VDB) \
+ || (defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC)))
# include "atomic_ops.h"
# endif
#endif /* !PARALLEL_MARK */
+union word_ptr_ao_u {
+ word w;
+ signed_word sw;
+ void *vp;
+# ifdef AO_HAVE_load
+ volatile AO_t ao;
+# endif
+};
+
/* We maintain layout maps for heap blocks containing objects of a given */
/* size. Each entry in this map describes a byte offset and has the */
/* following type. */
@@ -994,7 +1051,11 @@ struct roots {
# define MAX_HEAP_SECTS 768 /* Separately added heap sections. */
# endif
# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES)
-# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */
+# if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32))
+# define MAX_HEAP_SECTS 384
+# else
+# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */
+# endif
# elif CPP_WORDSZ > 32
# define MAX_HEAP_SECTS 1024 /* Roughly 8GB */
# else
@@ -1002,6 +1063,13 @@ struct roots {
# endif
#endif /* !MAX_HEAP_SECTS */
+typedef struct GC_ms_entry {
+ ptr_t mse_start; /* First word of object, word aligned. */
+ union word_ptr_ao_u mse_descr;
+ /* Descriptor; low order two bits are tags, */
+ /* as described in gc_mark.h. */
+} mse;
+
/* Lists of all heap blocks and free lists */
/* as well as other random data structures */
/* that should not be scanned by the */
@@ -1023,7 +1091,6 @@ struct roots {
struct _GC_arrays {
word _heapsize; /* Heap size in bytes. */
- word _max_heapsize;
word _requested_heapsize; /* Heap size due to explicit expansion. */
ptr_t _last_heap_addr;
ptr_t _prev_heap_addr;
@@ -1040,12 +1107,12 @@ struct _GC_arrays {
/* large object blocks. This is used to help decide when it */
/* is safe to split up a large block. */
word _bytes_allocd_before_gc;
- /* Number of words allocated before this */
+ /* Number of bytes allocated before this */
/* collection cycle. */
# ifndef SEPARATE_GLOBALS
# define GC_bytes_allocd GC_arrays._bytes_allocd
word _bytes_allocd;
- /* Number of words allocated during this collection cycle. */
+ /* Number of bytes allocated during this collection cycle. */
# endif
word _bytes_dropped;
/* Number of black-listed bytes dropped during GC cycle */
@@ -1056,9 +1123,6 @@ struct _GC_arrays {
/* Approximate number of bytes in objects (and headers) */
/* that became ready for finalization in the last */
/* collection. */
- word _non_gc_bytes_at_gc;
- /* Number of explicitly managed bytes of storage */
- /* at last collection. */
word _bytes_freed;
/* Number of explicitly deallocated bytes of memory */
/* since last collection. */
@@ -1070,6 +1134,18 @@ struct _GC_arrays {
ptr_t _scratch_last_end_ptr;
/* Used by headers.c, and can easily appear to point to */
/* heap. */
+ mse *_mark_stack;
+ /* Limits of stack for GC_mark routine. All ranges */
+ /* between GC_mark_stack (incl.) and GC_mark_stack_top */
+ /* (incl.) still need to be marked from. */
+ mse *_mark_stack_limit;
+# ifdef PARALLEL_MARK
+ mse *volatile _mark_stack_top;
+ /* Updated only with mark lock held, but read asynchronously. */
+ /* TODO: Use union to avoid casts to AO_t */
+# else
+ mse *_mark_stack_top;
+# endif
GC_mark_proc _mark_procs[MAX_MARK_PROCS];
/* Table of user-defined mark procedures. There is */
/* a small number of these, which can be referenced */
@@ -1083,18 +1159,18 @@ struct _GC_arrays {
/* free list for atomic objs */
# endif
void *_uobjfreelist[MAXOBJGRANULES+1];
- /* Uncollectable but traced objs */
+ /* Uncollectible but traced objs */
/* objects on this and auobjfreelist */
/* are always marked, except during */
/* garbage collections. */
# ifdef ATOMIC_UNCOLLECTABLE
# define GC_auobjfreelist GC_arrays._auobjfreelist
void *_auobjfreelist[MAXOBJGRANULES+1];
- /* Atomic uncollectable but traced objs */
+ /* Atomic uncollectible but traced objs */
# endif
- word _composite_in_use; /* Number of words in accessible */
+ word _composite_in_use; /* Number of bytes in the accessible */
/* composite objects. */
- word _atomic_in_use; /* Number of words in accessible */
+ word _atomic_in_use; /* Number of bytes in the accessible */
/* atomic objects. */
# ifdef USE_MUNMAP
# define GC_unmapped_bytes GC_arrays._unmapped_bytes
@@ -1199,7 +1275,7 @@ struct _GC_arrays {
struct callinfo _last_stack[NFRAMES];
/* Stack at last garbage collection. Useful for */
/* debugging mysterious object disappearances. In the */
- /* multithreaded case, we currently only save the */
+ /* multi-threaded case, we currently only save the */
/* calling stack. */
# endif
};
@@ -1219,11 +1295,12 @@ GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays;
#define GC_large_allocd_bytes GC_arrays._large_allocd_bytes
#define GC_large_free_bytes GC_arrays._large_free_bytes
#define GC_last_heap_addr GC_arrays._last_heap_addr
+#define GC_mark_stack GC_arrays._mark_stack
+#define GC_mark_stack_limit GC_arrays._mark_stack_limit
+#define GC_mark_stack_top GC_arrays._mark_stack_top
#define GC_mark_procs GC_arrays._mark_procs
-#define GC_max_heapsize GC_arrays._max_heapsize
#define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes
#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets
-#define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc
#define GC_prev_heap_addr GC_arrays._prev_heap_addr
#define GC_requested_heapsize GC_arrays._requested_heapsize
#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr
@@ -1266,7 +1343,7 @@ GC_EXTERN struct obj_kind {
/* is reclaimed, but must also tolerate being */
/* called with object from freelist. Non-zero */
/* exit prevents object from being reclaimed. */
-# define OK_DISCLAIM_INITZ /* comma */, FALSE, NULL
+# define OK_DISCLAIM_INITZ /* comma */, FALSE, 0
# else
# define OK_DISCLAIM_INITZ /* empty */
# endif /* !ENABLE_DISCLAIM */
@@ -1282,7 +1359,7 @@ GC_EXTERN struct obj_kind {
#ifdef SEPARATE_GLOBALS
extern word GC_bytes_allocd;
- /* Number of words allocated during this collection cycle */
+ /* Number of bytes allocated during this collection cycle. */
extern ptr_t GC_objfreelist[MAXOBJGRANULES+1];
/* free list for NORMAL objects */
# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist))
@@ -1333,6 +1410,11 @@ GC_EXTERN word GC_black_list_spacing;
/* "stack-blacklisted", i.e. that are */
/* problematic in the interior of an object. */
+#ifdef GC_GCJ_SUPPORT
+ extern struct hblk * GC_hblkfreelist[];
+ /* Remains visible to GNU GCJ. */
+#endif
+
#ifdef GC_DISABLE_INCREMENTAL
# define GC_incremental FALSE
/* Hopefully allow optimizer to remove some code. */
@@ -1463,7 +1545,6 @@ GC_INNER void GC_invalidate_mark_state(void);
/* objects may point to unmarked */
/* ones, and roots may point to */
/* unmarked objects. Reset mark stack. */
-GC_INNER GC_bool GC_mark_stack_empty(void);
GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame);
/* Perform about one pages worth of marking */
/* work of whatever kind is needed. Returns */
@@ -1478,19 +1559,18 @@ GC_INNER void GC_initiate_gc(void);
GC_INNER GC_bool GC_collection_in_progress(void);
/* Collection is in progress, or was abandoned. */
-GC_INNER void GC_push_all(ptr_t bottom, ptr_t top);
- /* Push everything in a range */
- /* onto mark stack. */
#ifndef GC_DISABLE_INCREMENTAL
- GC_INNER void GC_push_conditional(ptr_t b, ptr_t t, GC_bool all);
+# define GC_PUSH_CONDITIONAL(b, t, all) \
+ GC_push_conditional((ptr_t)(b), (ptr_t)(t), all)
+ /* Do either of GC_push_all or GC_push_selected */
+ /* depending on the third arg. */
#else
-# define GC_push_conditional(b, t, all) GC_push_all(b, t)
+# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_all((ptr_t)(b), (ptr_t)(t))
#endif
- /* Do either of the above, depending */
- /* on the third arg. */
+
GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t);
- /* As above, but consider */
- /* interior pointers as valid */
+ /* As GC_push_all but consider */
+ /* interior pointers as valid. */
GC_INNER void GC_push_all_eager(ptr_t b, ptr_t t);
/* Same as GC_push_all_stack, but */
/* ensures that stack is scanned */
@@ -1506,15 +1586,16 @@ GC_INNER void GC_push_all_eager(ptr_t b, ptr_t t);
GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame);
/* Push all or dirty roots. */
-GC_EXTERN void (*GC_push_other_roots)(void);
+GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots;
/* Push system or application specific roots */
/* onto the mark stack. In some environments */
/* (e.g. threads environments) this is */
/* predefined to be non-zero. A client */
/* supplied replacement should also call the */
- /* original function. */
+ /* original function. Remains externally */
+ /* visible as used by some well-known 3rd-party */
+ /* software (e.g., ECL) currently. */
-GC_INNER void GC_push_finalizer_structures(void);
#ifdef THREADS
void GC_push_thread_structures(void);
#endif
@@ -1558,8 +1639,8 @@ GC_INNER void GC_set_hdr_marks(hdr * hhdr);
GC_INNER void GC_set_fl_marks(ptr_t p);
/* Set all mark bits associated with */
/* a free list. */
-#ifdef GC_ASSERTIONS
- void GC_check_fl_marks(ptr_t p);
+#if defined(GC_ASSERTIONS) && defined(THREADS) && defined(THREAD_LOCAL_ALLOC)
+ void GC_check_fl_marks(void **);
/* Check that all mark bits */
/* associated with a free list are */
/* set. Abort if not. */
@@ -1600,9 +1681,8 @@ void GC_register_data_segments(void);
# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \
if (GC_all_interior_pointers) { \
GC_add_to_black_list_stack((word)(bits), (source)); \
- } else { \
- GC_add_to_black_list_normal((word)(bits), (source)); \
- }
+ } else \
+ GC_add_to_black_list_normal((word)(bits), (source))
GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source);
# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \
GC_add_to_black_list_stack((word)(bits), (source))
@@ -1611,20 +1691,21 @@ void GC_register_data_segments(void);
# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \
if (GC_all_interior_pointers) { \
GC_add_to_black_list_stack((word)(bits)); \
- } else { \
- GC_add_to_black_list_normal((word)(bits)); \
- }
+ } else \
+ GC_add_to_black_list_normal((word)(bits))
GC_INNER void GC_add_to_black_list_stack(word p);
# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \
GC_add_to_black_list_stack((word)(bits))
#endif /* PRINT_BLACK_LIST */
-GC_INNER struct hblk * GC_is_black_listed(struct hblk * h, word len);
+struct hblk * GC_is_black_listed(struct hblk * h, word len);
/* If there are likely to be false references */
/* to a block starting at h of the indicated */
/* length, then return the next plausible */
/* starting location for h that might avoid */
- /* these false references. */
+ /* these false references. Remains externally */
+ /* visible as used by GNU GCJ currently. */
+
GC_INNER void GC_promote_black_lists(void);
/* Declare an end to a black listing phase. */
GC_INNER void GC_unpromote_black_lists(void);
@@ -1635,7 +1716,7 @@ GC_INNER void GC_unpromote_black_lists(void);
GC_INNER ptr_t GC_scratch_alloc(size_t bytes);
/* GC internal memory allocation for */
/* small objects. Deallocation is not */
- /* possible. */
+ /* possible. May return NULL. */
/* Heap block layout maps: */
GC_INNER GC_bool GC_add_map_entry(size_t sz);
@@ -1731,23 +1812,10 @@ GC_INNER void GC_collect_a_little_inner(int n);
/* collection work, if appropriate. */
/* A unit is an amount appropriate for */
/* HBLKSIZE bytes of allocation. */
-/* void * GC_generic_malloc(size_t lb, int k); */
- /* Allocate an object of the given */
- /* kind. By default, there are only */
- /* a few kinds: composite(pointerfree), */
- /* atomic, uncollectable, etc. */
- /* We claim it's possible for clever */
- /* client code that understands GC */
- /* internals to add more, e.g. to */
- /* communicate object layout info */
- /* to the collector. */
- /* The actual decl is in gc_mark.h. */
-GC_INNER void * GC_generic_malloc_ignore_off_page(size_t b, int k);
- /* As above, but pointers past the */
- /* first page of the resulting object */
- /* are ignored. */
+
GC_INNER void * GC_generic_malloc_inner(size_t lb, int k);
- /* Ditto, but I already hold lock, etc. */
+ /* Allocate an object of the given */
+ /* kind but assuming lock already held. */
GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k);
/* Allocate an object, where */
/* the client guarantees that there */
@@ -1760,13 +1828,17 @@ GC_INNER ptr_t GC_allocobj(size_t sz, int kind);
/* free list nonempty, and return its */
/* head. Sz is in granules. */
-GC_INNER void * GC_clear_stack(void *);
- /* in misc.c, behaves like identity. */
-
#ifdef GC_ADD_CALLER
-# define GC_DBG_RA GC_RETURN_ADDR,
+ /* GC_DBG_EXTRAS is used by GC debug API functions (unlike GC_EXTRAS */
+ /* used by GC debug API macros) thus GC_RETURN_ADDR_PARENT (pointing */
+ /* to client caller) should be used if possible. */
+# ifdef GC_RETURN_ADDR_PARENT
+# define GC_DBG_EXTRAS GC_RETURN_ADDR_PARENT, NULL, 0
+# else
+# define GC_DBG_EXTRAS GC_RETURN_ADDR, NULL, 0
+# endif
#else
-# define GC_DBG_RA /* empty */
+# define GC_DBG_EXTRAS "unknown", 0
#endif
/* We make the GC_clear_stack() call a tail one, hoping to get more of */
@@ -1776,6 +1848,16 @@ GC_INNER void * GC_clear_stack(void *);
#define GENERAL_MALLOC_IOP(lb,k) \
GC_clear_stack(GC_generic_malloc_ignore_off_page(lb, k))
+#ifdef GC_COLLECT_AT_MALLOC
+ extern size_t GC_dbg_collect_at_malloc_min_lb;
+ /* variable visible outside for debugging */
+# define GC_DBG_COLLECT_AT_MALLOC(lb) \
+ (void)((lb) >= GC_dbg_collect_at_malloc_min_lb ? \
+ (GC_gcollect(), 0) : 0)
+#else
+# define GC_DBG_COLLECT_AT_MALLOC(lb) (void)0
+#endif /* !GC_COLLECT_AT_MALLOC */
+
/* Allocation routines that bypass the thread local cache. */
#ifdef THREAD_LOCAL_ALLOC
GC_INNER void * GC_core_malloc(size_t);
@@ -1800,20 +1882,6 @@ GC_INNER void GC_remove_counts(struct hblk * h, size_t sz);
/* Remove forwarding counts for h. */
GC_INNER hdr * GC_find_header(ptr_t h);
-GC_INNER void GC_finalize(void);
- /* Perform all indicated finalization actions */
- /* on unmarked objects. */
- /* Unreachable finalizable objects are enqueued */
- /* for processing by GC_invoke_finalizers. */
- /* Invoked with lock. */
-
-GC_INNER void GC_notify_or_invoke_finalizers(void);
- /* If GC_finalize_on_demand is not set, invoke */
- /* eligible finalizers. Otherwise: */
- /* Call *GC_finalizer_notifier if there are */
- /* finalizers to be run, and we haven't called */
- /* this procedure yet this GC cycle. */
-
GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes);
/* Add a HBLKSIZE aligned chunk to the heap. */
@@ -1837,9 +1905,9 @@ GC_EXTERN void (*GC_print_all_smashed)(void);
/* Print GC_smashed if it's not empty. */
/* Clear GC_smashed list. */
GC_EXTERN void (*GC_print_heap_obj)(ptr_t p);
- /* If possible print s followed by a more */
- /* detailed description of the object */
- /* referred to by p. */
+ /* If possible print (using GC_err_printf) */
+ /* a more detailed description (terminated with */
+ /* "\n") of the object referred to by p. */
#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG)
void GC_print_address_map(void);
@@ -1856,27 +1924,19 @@ GC_EXTERN void (*GC_print_heap_obj)(ptr_t p);
GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */
/* Call error printing routine */
- /* occasionally. It is ok to read it */
+ /* occasionally. It is OK to read it */
/* without acquiring the lock. */
+#define VERBOSE 2
#ifndef SMALL_CONFIG
- /* GC_print_stats should be visible outside the GC in some cases. */
+ /* GC_print_stats should be visible to extra/MacOS.c. */
extern int GC_print_stats; /* Nonzero generates basic GC log. */
/* VERBOSE generates add'l messages. */
-#else
+#else /* SMALL_CONFIG */
# define GC_print_stats 0
/* Will this remove the message character strings from the executable? */
/* With a particular level of optimizations, it should... */
#endif
-#define VERBOSE 2
-
-#ifndef NO_DEBUGGING
- GC_EXTERN GC_bool GC_dump_regularly;
- /* Generate regular debugging dumps. */
-# define COND_DUMP if (EXPECT(GC_dump_regularly, FALSE)) GC_dump()
-#else
-# define COND_DUMP /* empty */
-#endif
#ifdef KEEP_BACK_PTRS
GC_EXTERN long GC_backtraces;
@@ -1929,6 +1989,23 @@ GC_EXTERN GC_bool GC_print_back_height;
size_t bytes2);
#endif
+#ifdef CAN_HANDLE_FORK
+ GC_EXTERN int GC_handle_fork;
+ /* Fork-handling mode: */
+ /* 0 means no fork handling requested (but client could */
+ /* anyway call fork() provided it is surrounded with */
+ /* GC_atfork_prepare/parent/child calls); */
+ /* -1 means GC tries to use pthread_at_fork if it is */
+ /* available (if it succeeds then GC_handle_fork value */
+ /* is changed to 1), client should nonetheless surround */
+ /* fork() with GC_atfork_prepare/parent/child (for the */
+ /* case of pthread_at_fork failure or absence); */
+ /* 1 (or other values) means client fully relies on */
+ /* pthread_at_fork (so if it is missing or failed then */
+ /* abort occurs in GC_init), GC_atfork_prepare and the */
+ /* accompanying routines are no-op in such a case. */
+#endif
+
#ifndef GC_DISABLE_INCREMENTAL
GC_EXTERN GC_bool GC_dirty_maintained;
/* Dirty bits are being maintained, */
@@ -1950,6 +2027,9 @@ GC_EXTERN GC_bool GC_print_back_height;
GC_INNER void GC_dirty_init(void);
#endif /* !GC_DISABLE_INCREMENTAL */
+/* Same as GC_base but excepts and returns a pointer to const object. */
+#define GC_base_C(p) ((const void *)GC_base((/* no const */ void *)(p)))
+
/* Stubborn objects: */
void GC_read_changed(void); /* Analogous to GC_read_dirty */
GC_bool GC_page_was_changed(struct hblk * h);
@@ -1963,9 +2043,6 @@ void GC_print_block_list(void);
void GC_print_hblkfreelist(void);
void GC_print_heap_sects(void);
void GC_print_static_roots(void);
-#ifndef SMALL_CONFIG
- GC_INNER void GC_print_finalization_stats(void);
-#endif
/* void GC_dump(void); - declared in gc.h */
extern word GC_fo_entries; /* should be visible in extra/MacOS.c */
@@ -1981,15 +2058,7 @@ extern word GC_fo_entries; /* should be visible in extra/MacOS.c */
#endif
/* Make arguments appear live to compiler */
-#if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__CC_ARM)
- void GC_noop(void*, ...);
-#else
-# ifdef __DMC__
- void GC_noop(...);
-# else
- void GC_noop();
-# endif
-#endif
+void GC_noop6(word, word, word, word, word, word);
GC_API void GC_CALL GC_noop1(word);
@@ -2003,6 +2072,9 @@ GC_API void GC_CALL GC_noop1(word);
#endif
/* Logging and diagnostic output: */
+/* GC_printf is used typically on client explicit print requests. */
+/* For all GC_X_printf routines, it is recommended to put "\n" at */
+/* 'format' string end (for output atomicity). */
GC_API_PRIV void GC_printf(const char * format, ...)
GC_ATTR_FORMAT_PRINTF(1, 2);
/* A version of printf that doesn't allocate, */
@@ -2011,12 +2083,53 @@ GC_API_PRIV void GC_printf(const char * format, ...)
/* allocate for long arguments.) */
GC_API_PRIV void GC_err_printf(const char * format, ...)
GC_ATTR_FORMAT_PRINTF(1, 2);
+
+/* Basic logging routine. Typically, GC_log_printf is called directly */
+/* only inside various DEBUG_x blocks. */
+#if defined(__cplusplus) && defined(SYMBIAN)
+ extern "C" {
+#endif
GC_API_PRIV void GC_log_printf(const char * format, ...)
GC_ATTR_FORMAT_PRINTF(1, 2);
+#if defined(__cplusplus) && defined(SYMBIAN)
+ }
+#endif
+
+#ifndef GC_ANDROID_LOG
+# define GC_PRINT_STATS_FLAG (GC_print_stats != 0)
+# define GC_INFOLOG_PRINTF GC_COND_LOG_PRINTF
+ /* GC_verbose_log_printf is called only if GC_print_stats is VERBOSE. */
+# define GC_verbose_log_printf GC_log_printf
+#else
+ extern GC_bool GC_quiet;
+# define GC_PRINT_STATS_FLAG (!GC_quiet)
+ /* INFO/DBG loggers are enabled even if GC_print_stats is off. */
+# ifndef GC_INFOLOG_PRINTF
+# define GC_INFOLOG_PRINTF if (GC_quiet) {} else GC_info_log_printf
+# endif
+ GC_INNER void GC_info_log_printf(const char *format, ...)
+ GC_ATTR_FORMAT_PRINTF(1, 2);
+ GC_INNER void GC_verbose_log_printf(const char *format, ...)
+ GC_ATTR_FORMAT_PRINTF(1, 2);
+#endif /* GC_ANDROID_LOG */
+
+/* Convenient macros for GC_[verbose_]log_printf invocation. */
+#define GC_COND_LOG_PRINTF \
+ if (EXPECT(!GC_print_stats, TRUE)) {} else GC_log_printf
+#define GC_VERBOSE_LOG_PRINTF \
+ if (EXPECT(GC_print_stats != VERBOSE, TRUE)) {} else GC_verbose_log_printf
+#ifndef GC_DBGLOG_PRINTF
+# define GC_DBGLOG_PRINTF if (!GC_PRINT_STATS_FLAG) {} else GC_log_printf
+#endif
+
void GC_err_puts(const char *s);
/* Write s to stderr, don't buffer, don't add */
/* newlines, don't ... */
+/* Handy macro for logging size values (of word type) in KiB (rounding */
+/* to nearest value). */
+#define TO_KiB_UL(v) ((unsigned long)(((v) + ((1 << 9) - 1)) >> 10))
+
GC_EXTERN unsigned GC_fail_count;
/* How many consecutive GC/expansion failures? */
/* Reset by GC_allochblk(); defined in alloc.c. */
@@ -2027,6 +2140,12 @@ GC_EXTERN signed_word GC_bytes_found;
/* Number of reclaimed bytes after garbage collection; */
/* protected by GC lock; defined in reclaim.c. */
+#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+ GC_EXTERN word GC_reclaimed_bytes_before_gc;
+ /* Number of bytes reclaimed before this */
+ /* collection cycle; used for statistics only. */
+#endif
+
#ifdef USE_MUNMAP
GC_EXTERN int GC_unmap_threshold; /* defined in allchblk.c */
GC_EXTERN GC_bool GC_force_unmap_on_gcollect; /* defined in misc.c */
@@ -2107,7 +2226,7 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp);
/* from os_dep.c */
# endif
-#elif defined(MSWIN32) || defined(MSWINCE)
+#elif defined(USE_WINALLOC)
GC_INNER void GC_add_current_malloc_heap(void);
#endif /* !REDIRECT_MALLOC */
@@ -2176,14 +2295,18 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
#endif /* NEED_PROC_MAPS */
#ifdef GC_ASSERTIONS
-# define GC_ASSERT(expr) \
+# define GC_ASSERT(expr) \
+ do { \
if (!(expr)) { \
GC_err_printf("Assertion failure: %s:%d\n", \
__FILE__, __LINE__); \
ABORT("assertion failure"); \
- }
+ } \
+ } while (0)
+ GC_INNER word GC_compute_large_free_bytes(void);
+ GC_INNER word GC_compute_root_size(void);
#else
-# define GC_ASSERT(expr)
+# define GC_ASSERT(expr)
#endif
/* Check a compile time assertion at compile time. The error */
@@ -2197,15 +2320,30 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
# define GC_STATIC_ASSERT(expr) (void)sizeof(char[(expr)? 1 : -1])
#endif
+#define COND_DUMP_CHECKS \
+ do { \
+ GC_ASSERT(GC_compute_large_free_bytes() == GC_large_free_bytes); \
+ GC_ASSERT(GC_compute_root_size() == GC_root_size); \
+ } while (0)
+
+#ifndef NO_DEBUGGING
+ GC_EXTERN GC_bool GC_dump_regularly;
+ /* Generate regular debugging dumps. */
+# define COND_DUMP if (EXPECT(GC_dump_regularly, FALSE)) GC_dump(); \
+ else COND_DUMP_CHECKS
+#else
+# define COND_DUMP COND_DUMP_CHECKS
+#endif
+
#if defined(PARALLEL_MARK)
/* We need additional synchronization facilities from the thread */
/* support. We believe these are less performance critical */
/* than the main garbage collector lock; standard pthreads-based */
/* implementations should be sufficient. */
- GC_EXTERN long GC_markers; /* Number of mark threads we would like */
- /* to have. Includes the initiating */
- /* thread. Defined in mark.c. */
+# define GC_markers_m1 GC_parallel
+ /* Number of mark threads we would like to have */
+ /* excluding the initiating thread. */
/* The mark lock and condition variable. If the GC lock is also */
/* acquired, the GC lock must be acquired first. The mark lock is */
@@ -2236,7 +2374,8 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
/* some other reason. */
#endif /* PARALLEL_MARK */
-#if defined(GC_PTHREADS) && !defined(NACL) && !defined(SIG_SUSPEND)
+#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) && !defined(NACL) \
+ && !defined(SIG_SUSPEND)
/* We define the thread suspension signal here, so that we can refer */
/* to it in the dirty bit implementation, if necessary. Ideally we */
/* would allocate a (real-time?) signal using the standard mechanism. */
@@ -2252,7 +2391,11 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
/* Linuxthreads itself uses SIGUSR1 and SIGUSR2. */
# define SIG_SUSPEND SIGPWR
# endif
-# elif !defined(GC_OPENBSD_THREADS) && !defined(GC_DARWIN_THREADS)
+# elif defined(GC_OPENBSD_THREADS)
+# ifndef GC_OPENBSD_UTHREADS
+# define SIG_SUSPEND SIGXFSZ
+# endif
+# elif !defined(GC_DARWIN_THREADS)
# if defined(_SIGRTMIN)
# define SIG_SUSPEND _SIGRTMIN + 6
# else
@@ -2265,12 +2408,14 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
# define GC_SEM_INIT_PSHARED 0
#endif
+#include <setjmp.h>
+
/* Some macros for setjmp that works across signal handlers */
/* were possible, and a couple of routines to facilitate */
/* catching accesses to bad addresses when that's */
/* possible/needed. */
-#if defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32))
-# include <setjmp.h>
+#if (defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32))) \
+ && !defined(GC_NO_SIGSETJMP)
# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX)
# include <sys/siginfo.h>
# endif
@@ -2281,13 +2426,13 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
# define JMP_BUF sigjmp_buf
#else
# ifdef ECOS
-# define SETJMP(env) hal_setjmp(env)
+# define SETJMP(env) hal_setjmp(env)
# else
# define SETJMP(env) setjmp(env)
# endif
# define LONGJMP(env, val) longjmp(env, val)
# define JMP_BUF jmp_buf
-#endif /* !UNIX_LIKE */
+#endif /* !UNIX_LIKE || GC_NO_SIGSETJMP */
/* Do we need the GC_find_limit machinery to find the end of a */
/* data segment. */
@@ -2348,19 +2493,19 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str,
# else
# define INCR_CANCEL_DISABLE()
# define DECR_CANCEL_DISABLE()
-# define ASSERT_CANCEL_DISABLED()
+# define ASSERT_CANCEL_DISABLED() (void)0
# endif /* GC_ASSERTIONS & ... */
# define DISABLE_CANCEL(state) \
- { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \
- INCR_CANCEL_DISABLE(); }
+ do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \
+ INCR_CANCEL_DISABLE(); } while (0)
# define RESTORE_CANCEL(state) \
- { ASSERT_CANCEL_DISABLED(); \
+ do { ASSERT_CANCEL_DISABLED(); \
pthread_setcancelstate(state, NULL); \
- DECR_CANCEL_DISABLE(); }
+ DECR_CANCEL_DISABLE(); } while (0)
#else /* !CANCEL_SAFE */
-# define DISABLE_CANCEL(state)
-# define RESTORE_CANCEL(state)
-# define ASSERT_CANCEL_DISABLED()
+# define DISABLE_CANCEL(state) (void)0
+# define RESTORE_CANCEL(state) (void)0
+# define ASSERT_CANCEL_DISABLED() (void)0
#endif /* !CANCEL_SAFE */
#endif /* GC_PRIVATE_H */
diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h
index b57dcc93..0b4e02b4 100644
--- a/include/private/gcconfig.h
+++ b/include/private/gcconfig.h
@@ -42,6 +42,13 @@
# define PLATFORM_ANDROID 1
#endif
+#if defined(__SYMBIAN32__) && !defined(SYMBIAN)
+# define SYMBIAN
+# ifdef __WINS__
+# pragma data_seg(".data2")
+# endif
+#endif
+
/* First a unified test for Linux: */
# if (defined(linux) || defined(__linux__) || defined(PLATFORM_ANDROID)) \
&& !defined(LINUX) && !defined(__native_client__)
@@ -75,10 +82,18 @@
# define I386
# define mach_type_known
# endif
+# if defined(__aarch64__)
+# define AARCH64
+# if !defined(LINUX)
+# define NOSYS
+# define mach_type_known
+# endif
+# endif
# if defined(__arm) || defined(__arm__) || defined(__thumb__)
# define ARM32
-# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \
- && !defined(DARWIN) && !defined(_WIN32) && !defined(__CEGCC__)
+# if !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) \
+ && !defined(OPENBSD) && !defined(DARWIN) \
+ && !defined(_WIN32) && !defined(__CEGCC__) && !defined(SYMBIAN)
# define NOSYS
# define mach_type_known
# endif
@@ -255,6 +270,10 @@
# define IA64
# define mach_type_known
# endif
+# if defined(LINUX) && defined(__aarch64__)
+# define AARCH64
+# define mach_type_known
+# endif
# if defined(LINUX) && (defined(__arm) || defined(__arm__))
# define ARM32
# define mach_type_known
@@ -290,10 +309,6 @@
# define M32R
# define mach_type_known
# endif
-# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__))
-# define POWERPC
-# define mach_type_known
-# endif
# if defined(__alpha) || defined(__alpha__)
# define ALPHA
# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \
@@ -309,12 +324,14 @@
# define M68K
# define mach_type_known
# endif
-# if defined(THINK_C) || defined(__MWERKS__) && !defined(__powerc)
+# if defined(THINK_C) \
+ || (defined(__MWERKS__) && !defined(__powerc) && !defined(SYMBIAN))
# define M68K
# define MACOS
# define mach_type_known
# endif
-# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__)
+# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) \
+ && !defined(SYMBIAN)
# define POWERPC
# define MACOS
# define mach_type_known
@@ -360,25 +377,37 @@
# define OPENBSD
# define mach_type_known
# endif
+# if defined(__NetBSD__) && (defined(i386) || defined(__i386__))
+# define I386
+# define mach_type_known
+# endif
+# if defined(__NetBSD__) && defined(__x86_64__)
+# define X86_64
+# define mach_type_known
+# endif
# if defined(FREEBSD) && (defined(i386) || defined(__i386__))
# define I386
# define mach_type_known
# endif
-# if defined(FREEBSD) && defined(__x86_64__)
+# if defined(FREEBSD) && (defined(__amd64__) || defined(__x86_64__))
# define X86_64
# define mach_type_known
# endif
-# if defined(__NetBSD__) && (defined(i386) || defined(__i386__))
-# define I386
+# if defined(FREEBSD) && defined(__ia64__)
+# define IA64
# define mach_type_known
# endif
-# if defined(__NetBSD__) && defined(__x86_64__)
-# define X86_64
-# define mach_type_known
-# endif
# if defined(FREEBSD) && defined(__sparc__)
-# define SPARC
-# define mach_type_known
+# define SPARC
+# define mach_type_known
+# endif
+# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__))
+# define POWERPC
+# define mach_type_known
+# endif
+# if defined(FREEBSD) && defined(__arm__)
+# define ARM32
+# define mach_type_known
# endif
# if defined(bsdi) && (defined(i386) || defined(__i386__))
# define I386
@@ -414,8 +443,9 @@
# define MSWINCE
# define mach_type_known
# else
-# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \
- || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)
+# if ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300)) \
+ || (defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \
+ && !defined(SYMBIAN))
# if defined(__LP64__) || defined(_WIN64)
# define X86_64
# else
@@ -507,6 +537,10 @@
# define mach_type_known
# endif
+# if defined(SYMBIAN)
+# define mach_type_known
+# endif
+
/* Feel free to add more clauses here */
/* Or manually define the machine type here. A machine type is */
@@ -524,7 +558,7 @@
/* I386 ==> Intel 386 */
/* (SEQUENT, OS2, SCO, LINUX, NETBSD, */
/* FREEBSD, THREE86BSD, MSWIN32, */
- /* BSDI,SOLARIS, NEXT, other variants) */
+ /* BSDI, SOLARIS, NEXT and others) */
/* NS32K ==> Encore Multimax */
/* MIPS ==> R2000 through R14K */
/* (many variants) */
@@ -542,6 +576,7 @@
/* running Amdahl UTS4 */
/* S390 ==> 390-like machine */
/* running LINUX */
+ /* AARCH64 ==> ARM AArch64 */
/* ARM32 ==> Intel StrongARM */
/* IA64 ==> Intel IPF */
/* (e.g. Itanium) */
@@ -585,11 +620,11 @@
* On UNIX-like systems, the collector will scan the area between DATASTART
* and DATAEND for root pointers.
*
- * DATAEND, if not `end' where `end' is defined as ``extern int end[];''.
+ * DATAEND, if not "end", where "end" is defined as "extern int end[]".
* RTH suggests gaining access to linker script synth'd values with
- * this idiom instead of `&end' where `end' is defined as ``extern int end;'' .
- * Otherwise, ``GCC will assume these are in .sdata/.sbss'' and it will, e.g.,
- * cause failures on alpha*-*-* with ``-msmall-data or -fpic'' or mips-*-*
+ * this idiom instead of "&end", where "end" is defined as "extern int end".
+ * Otherwise, "GCC will assume these are in .sdata/.sbss" and it will, e.g.,
+ * cause failures on alpha*-*-* with -msmall-data or -fpic or mips-*-*
* without any special options.
*
* STACKBOTTOM is the cool end of the stack, which is usually the
@@ -649,7 +684,7 @@
* int argc;
* char **argv, **envp;
* {
- * int dummy;
+ * volatile int dummy;
*
* GC_stackbottom = (ptr_t)(&dummy);
* return(real_main(argc, argv, envp));
@@ -685,10 +720,22 @@
/* __builtin_unwind_init() to push the relevant registers onto the stack. */
# if defined(__GNUC__) && ((__GNUC__ >= 3) \
|| (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) \
- && !defined(__INTEL_COMPILER) && !defined(__PATHCC__)
+ && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) \
+ && !(defined(POWERPC) && defined(DARWIN)) /* for MacOS X 10.3.9 */ \
+ && !defined(RTEMS) \
+ && !defined(__clang__) /* since no-op in clang (3.0) */
# define HAVE_BUILTIN_UNWIND_INIT
# endif
+# ifdef SYMBIAN
+# define MACH_TYPE "SYMBIAN"
+# define OS_TYPE "SYMBIAN"
+# define CPP_WORDSZ 32
+# define ALIGNMENT 4
+# define DATASTART NULL
+# define DATAEND NULL
+# endif
+
# define STACK_GRAN 0x1000000
# ifdef M68K
# define MACH_TYPE "M68K"
@@ -846,12 +893,10 @@
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
# define ALIGNMENT 4
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 268
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -901,6 +946,9 @@
# define DATAEND (ptr_t)(_end)
# define DATASTART (ptr_t)(__bss_start)
# define STACKBOTTOM ((ptr_t)ps3_get_stack_bottom())
+# define NO_PTHREAD_TRYLOCK
+ /* Current GC LOCK() implementation for PS3 explicitly */
+ /* use pthread_mutex_lock for some reason. */
# endif
# ifdef AIX
# define OS_TYPE "AIX"
@@ -1031,7 +1079,7 @@
# ifdef __ELF__
# define DYNAMIC_LOADING
# else
- Linux Sparc/a.out not supported
+# error --> Linux SPARC a.out not supported
# endif
extern int _end[];
extern int _etext[];
@@ -1048,12 +1096,10 @@
# endif
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 232
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -1196,12 +1242,14 @@
# ifdef NACL
# define OS_TYPE "NACL"
extern int etext[];
-# define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
+/* #define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) */
+# define DATASTART ((ptr_t)0x10000000)
extern int _end[];
# define DATAEND (_end)
# undef STACK_GRAN
# define STACK_GRAN 0x10000
# define HEURISTIC1
+# define NO_PTHREAD_GETATTR_NP
# define GETPAGESIZE() 65536
# ifndef MAX_NACL_GC_THREADS
# define MAX_NACL_GC_THREADS 1024
@@ -1254,13 +1302,18 @@
# endif
extern int _end[];
# define DATAEND (ptr_t)(_end)
+# if defined(PLATFORM_ANDROID) && !defined(GC_NO_SIGSETJMP)
+ /* As of Android NDK r8b, _sigsetjmp is still missing */
+ /* for x86 (setjmp is used instead to find data_start). */
+# define GC_NO_SIGSETJMP
+# endif
# else
extern int etext[];
# define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff))
# endif
# ifdef USE_I686_PREFETCH
- /* FIXME: Thus should use __builtin_prefetch, but we'll leave that */
- /* for the next rtelease. */
+ /* FIXME: Thus should use __builtin_prefetch, but we'll leave */
+ /* that for the next release. */
# define PREFETCH(x) \
__asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x)))
/* Empirically prefetcht0 is much more effective at reducing */
@@ -1324,12 +1377,10 @@
# endif
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 176
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -1466,6 +1517,7 @@
# define DYNAMIC_LOADING
extern int _end[];
# define DATAEND (ptr_t)(_end)
+# pragma weak __data_start
extern int __data_start[];
# define DATASTART ((ptr_t)(__data_start))
# ifdef _MIPS_SZPTR
@@ -1560,12 +1612,10 @@
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
# define ALIGNMENT 4
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 808
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int _fdata[];
# define DATASTART ((ptr_t)_fdata)
@@ -1604,7 +1654,7 @@
# define DATASTART ((ptr_t)(__data_start))
# ifdef USE_HPUX_FIXED_STACKBOTTOM
/* The following appears to work for 7xx systems running HP/UX */
- /* 9.xx Furthermore, it might result in much faster */
+ /* 9.xx. Furthermore, it might result in much faster */
/* collections than HEURISTIC2, which may involve scanning */
/* segments that directly precede the stack. It is not the */
/* default, since it may not work on older machine/OS */
@@ -1622,10 +1672,10 @@
# include <unistd.h>
# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE)
# ifndef __GNUC__
-# define PREFETCH(x) { \
+# define PREFETCH(x) do { \
register long addr = (long)(x); \
(void) _asm ("LDW", 0, 0, addr, 0); \
- }
+ } while (0)
# endif
# endif /* HPUX */
# ifdef LINUX
@@ -1638,12 +1688,10 @@
# endif /* LINUX */
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 520
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -1670,12 +1718,10 @@
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
# define ELF_CLASS ELFCLASS64
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 816
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -1904,6 +1950,31 @@
# endif
# endif
+# ifdef AARCH64
+# define CPP_WORDSZ 64
+# define MACH_TYPE "AARCH64"
+# define ALIGNMENT 8
+# ifndef HBLKSIZE
+# define HBLKSIZE 4096
+# endif
+# ifdef LINUX
+# define OS_TYPE "LINUX"
+# define LINUX_STACKBOTTOM
+# define DYNAMIC_LOADING
+ extern int __data_start[];
+# define DATASTART ((ptr_t)__data_start)
+ extern char _end[];
+# define DATAEND ((ptr_t)(&_end))
+# endif
+# ifdef NOSYS
+ /* __data_start is usually defined in the target linker script. */
+ extern int __data_start[];
+# define DATASTART ((ptr_t)__data_start)
+ extern void *__stack_base__;
+# define STACKBOTTOM ((ptr_t)__stack_base__)
+# endif
+# endif
+
# ifdef ARM32
# define CPP_WORDSZ 32
# define MACH_TYPE "ARM32"
@@ -1954,6 +2025,17 @@
# define OS_TYPE "MSWINCE"
# define DATAEND /* not needed */
# endif
+# ifdef FREEBSD
+ /* FreeBSD/arm */
+# define ALIGNMENT 4
+# define OS_TYPE "FREEBSD"
+# ifdef __ELF__
+# define DYNAMIC_LOADING
+# endif
+# define HEURISTIC2
+ extern char etext[];
+# define SEARCH_FOR_DATA_START
+# endif
# ifdef DARWIN
/* iPhone */
# define OS_TYPE "DARWIN"
@@ -1973,17 +2055,17 @@
/* FIXME: There seems to be some issues with trylock hanging on */
/* darwin. This should be looked into some more. */
# define NO_PTHREAD_TRYLOCK
-# define NO_DYLD_BIND_FULLY_IMAGE
+# ifndef NO_DYLD_BIND_FULLY_IMAGE
+# define NO_DYLD_BIND_FULLY_IMAGE
+# endif
# endif
# ifdef OPENBSD
# define ALIGNMENT 4
# define OS_TYPE "OPENBSD"
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 176
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -2037,12 +2119,10 @@
# endif
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 332
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -2089,8 +2169,13 @@
# ifdef X86_64
# define MACH_TYPE "X86_64"
-# define ALIGNMENT 8
-# define CPP_WORDSZ 64
+# ifdef __ILP32__
+# define ALIGNMENT 4
+# define CPP_WORDSZ 32
+# else
+# define ALIGNMENT 8
+# define CPP_WORDSZ 64
+# endif
# ifndef HBLKSIZE
# define HBLKSIZE 4096
# endif
@@ -2098,12 +2183,10 @@
# ifdef OPENBSD
# define OS_TYPE "OPENBSD"
# define ELF_CLASS ELFCLASS64
-# ifdef GC_OPENBSD_THREADS
-# define UTHREAD_SP_OFFSET 400
-# else
+# ifndef GC_OPENBSD_THREADS
# include <sys/param.h>
# include <uvm/uvm_extern.h>
-# define STACKBOTTOM USRSTACK
+# define STACKBOTTOM ((ptr_t) USRSTACK)
# endif
extern int __data_start[];
# define DATASTART ((ptr_t)__data_start)
@@ -2287,6 +2370,11 @@
# endif
# endif
+#if defined(__GLIBC__) && !defined(DONT_USE_LIBC_PRIVATES)
+ /* Use glibc's stack-end marker. */
+# define USE_LIBC_PRIVATES
+#endif
+
#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \
&& !defined(USE_LIBC_PRIVATES)
/* This combination will fail, since we have no way to get */
@@ -2347,6 +2435,14 @@
# define DATAEND (ptr_t)(end)
#endif
+#if defined(PLATFORM_ANDROID) && !defined(THREADS) \
+ && !defined(USE_GET_STACKBASE_FOR_MAIN)
+ /* Always use pthread_attr_getstack on Android ("-lpthread" option is */
+ /* not needed to be specified manually) since GC_linux_main_stack_base */
+ /* causes app crash if invoked inside Dalvik VM. */
+# define USE_GET_STACKBASE_FOR_MAIN
+#endif
+
#if (defined(SVR4) || defined(PLATFORM_ANDROID)) && !defined(GETPAGESIZE)
# include <unistd.h>
# define GETPAGESIZE() sysconf(_SC_PAGESIZE)
@@ -2393,6 +2489,15 @@
# define SIGRTMAX 63
#endif
+#ifdef GC_OPENBSD_THREADS
+# include <sys/param.h>
+ /* Prior to 5.2 release, OpenBSD had user threads and required */
+ /* special handling. */
+# if OpenBSD < 201211
+# define GC_OPENBSD_UTHREADS 1
+# endif
+#endif /* GC_OPENBSD_THREADS */
+
#if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \
|| defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \
|| defined(DGUX) || defined(BSD) || defined(HURD) \
@@ -2431,6 +2536,15 @@
# define GC_DISABLE_INCREMENTAL
#endif
+#if (defined(MSWIN32) || defined(MSWINCE)) && !defined(USE_WINALLOC)
+ /* USE_WINALLOC is only an option for Cygwin. */
+# define USE_WINALLOC
+#endif
+
+#ifdef USE_WINALLOC
+# undef USE_MMAP
+#endif
+
#if defined(GC_DISABLE_INCREMENTAL) || defined(MANUAL_VDB)
# undef GWW_VDB
# undef MPROTECT_VDB
@@ -2476,17 +2590,18 @@
|| defined(OPENBSD) || defined(ARM32) \
|| defined(MIPS) || defined(AVR32))) \
|| (defined(LINUX) && (defined(SPARC) || defined(M68K))) \
- || (defined(RTEMS) && defined(I386))) && !defined(NO_GETCONTEXT)
+ || ((defined(RTEMS) || defined(PLATFORM_ANDROID)) && defined(I386))) \
+ && !defined(NO_GETCONTEXT)
# define NO_GETCONTEXT
#endif
#ifndef PREFETCH
-# define PREFETCH(x)
+# define PREFETCH(x) (void)0
# define NO_PREFETCH
#endif
#ifndef PREFETCH_FOR_WRITE
-# define PREFETCH_FOR_WRITE(x)
+# define PREFETCH_FOR_WRITE(x) (void)0
# define NO_PREFETCH_FOR_WRITE
#endif
@@ -2566,6 +2681,10 @@
# define THREADS
#endif
+#if defined(PARALLEL_MARK) && !defined(THREADS)
+# error "invalid config - PARALLEL_MARK requires GC_THREADS"
+#endif
+
#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \
&& !defined(PLATFORM_ANDROID)
/* Make the code cancellation-safe. This basically means that we */
@@ -2589,6 +2708,28 @@
# define IF_CANCEL(x) /* empty */
#endif
+#if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \
+ && !defined(HAVE_NO_FORK) \
+ && ((defined(GC_PTHREADS) && !defined(NACL) \
+ && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \
+ || (defined(DARWIN) && defined(MPROTECT_VDB)) || defined(HANDLE_FORK))
+ /* Attempts (where supported and requested) to make GC_malloc work in */
+ /* a child process fork'ed from a multi-threaded parent. */
+# define CAN_HANDLE_FORK
+#endif
+
+#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \
+ && !defined(HURD) && !defined(PLATFORM_ANDROID)
+ /* Have working pthread_atfork(). */
+# define CAN_CALL_ATFORK
+#endif
+
+#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \
+ && (defined(MSWIN32) || defined(MSWINCE) || defined(DOS4GW) \
+ || defined(OS2) || defined(SYMBIAN) /* and probably others ... */)
+# define HAVE_NO_FORK
+#endif
+
#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \
&& defined(PARALLEL_MARK)
/* Minimize compare-and-swap usage. */
@@ -2673,6 +2814,13 @@
# define NEED_CALLINFO
#endif
+#if (defined(FREEBSD) || (defined(DARWIN) && !defined(_POSIX_C_SOURCE)) \
+ || (defined(SOLARIS) && (!defined(_XOPEN_SOURCE) \
+ || defined(__EXTENSIONS__))) \
+ || defined(LINUX)) && !defined(HAVE_DLADDR)
+# define HAVE_DLADDR
+#endif
+
#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL)
# define DBG_HDRS_ALL
#endif
diff --git a/include/private/pthread_stop_world.h b/include/private/pthread_stop_world.h
index cb67d230..48374286 100644
--- a/include/private/pthread_stop_world.h
+++ b/include/private/pthread_stop_world.h
@@ -19,7 +19,7 @@
#define GC_PTHREAD_STOP_WORLD_H
struct thread_stop_info {
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
word last_stop_count; /* GC_last_stop_count value when thread */
/* last successfully handled a suspend */
/* signal. */
diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h
index ba0f6936..525a9aac 100644
--- a/include/private/pthread_support.h
+++ b/include/private/pthread_support.h
@@ -56,7 +56,7 @@ typedef struct GC_Thread_Rep {
# define FINISHED 1 /* Thread has exited. */
# define DETACHED 2 /* Thread is treated as detached. */
/* Thread may really be detached, or */
- /* it may have have been explicitly */
+ /* it may have been explicitly */
/* registered, in which case we can */
/* deallocate its GC_Thread_Rep once */
/* it unregisters itself, since it */
@@ -136,10 +136,17 @@ GC_EXTERN GC_bool GC_in_thread_creation;
GC_INNER void GC_unblock_gc_signals(void);
#endif
-GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *),
+#ifdef GC_PTHREAD_START_STANDALONE
+# define GC_INNER_PTHRSTART /* empty */
+#else
+# define GC_INNER_PTHRSTART GC_INNER
+#endif
+
+GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread(
+ void *(**pstart)(void *),
void **pstart_arg,
struct GC_stack_base *sb, void *arg);
-GC_INNER void GC_thread_exit_proc(void *);
+GC_INNER_PTHRSTART void GC_thread_exit_proc(void *);
#endif /* GC_PTHREADS && !GC_WIN32_THREADS */
diff --git a/include/private/specific.h b/include/private/specific.h
index 3a73a0fb..7302fe32 100644
--- a/include/private/specific.h
+++ b/include/private/specific.h
@@ -23,9 +23,11 @@
#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
#define TS_CACHE_SIZE 1024
-#define CACHE_HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_CACHE_SIZE - 1))
+#define CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1))
+
#define TS_HASH_SIZE 1024
-#define HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_HASH_SIZE - 1))
+#define HASH(p) \
+ ((unsigned)((((word)(p)) >> 8) ^ (word)(p)) & (TS_HASH_SIZE - 1))
/* An entry describing a thread-specific value for a given thread. */
/* All such accessible structures preserve the invariant that if either */
@@ -52,15 +54,20 @@ typedef struct thread_specific_entry {
/* or at least thread stack separation, is at least 4K. */
/* Must be defined so that it never returns 0. (Page 0 can't really be */
/* part of any stack, since that would make 0 a valid stack pointer.) */
-#define quick_thread_id() (((unsigned long)GC_approx_sp()) >> 12)
+#define quick_thread_id() (((word)GC_approx_sp()) >> 12)
-#define INVALID_QTID ((unsigned long)0)
+#define INVALID_QTID ((word)0)
#define INVALID_THREADID ((pthread_t)0)
+union ptse_ao_u {
+ tse *p;
+ volatile AO_t ao;
+};
+
typedef struct thread_specific_data {
tse * volatile cache[TS_CACHE_SIZE];
/* A faster index to the hash table */
- tse * hash[TS_HASH_SIZE];
+ union ptse_ao_u hash[TS_HASH_SIZE];
pthread_mutex_t lock;
} tsd;
@@ -72,16 +79,17 @@ GC_INNER int GC_setspecific(tsd * key, void * value);
GC_INNER void GC_remove_specific(tsd * key);
/* An internal version of getspecific that assumes a cache miss. */
-GC_INNER void * GC_slow_getspecific(tsd * key, unsigned long qtid,
+GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
tse * volatile * cache_entry);
/* GC_INLINE is defined in gc_priv.h. */
GC_INLINE void * GC_getspecific(tsd * key)
{
- unsigned long qtid = quick_thread_id();
- unsigned hash_val = CACHE_HASH(qtid);
- tse * volatile * entry_ptr = key -> cache + hash_val;
+ word qtid = quick_thread_id();
+ tse * volatile * entry_ptr = &key->cache[CACHE_HASH(qtid)];
tse * entry = *entry_ptr; /* Must be loaded only once. */
+
+ GC_ASSERT(qtid != INVALID_QTID);
if (EXPECT(entry -> qtid == qtid, TRUE)) {
GC_ASSERT(entry -> thread == pthread_self());
return entry -> value;
diff --git a/include/private/thread_local_alloc.h b/include/private/thread_local_alloc.h
index 3ebb922c..82fe2987 100644
--- a/include/private/thread_local_alloc.h
+++ b/include/private/thread_local_alloc.h
@@ -36,28 +36,39 @@
&& !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \
&& !defined(USE_CUSTOM_SPECIFIC)
# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
-# if defined(__GNUC__) /* Fixed for versions past 2.95? */ \
- || defined(MSWINCE)
+# if defined(CYGWIN32) && (__GNUC__ >= 4)
+# if defined(__clang__)
+ /* As of Cygwin clang3.1, thread-local storage is unsupported. */
+# define USE_PTHREAD_SPECIFIC
+# else
+# define USE_COMPILER_TLS
+# endif
+# elif defined(__GNUC__) || defined(MSWINCE)
# define USE_WIN32_SPECIFIC
# else
# define USE_WIN32_COMPILER_TLS
# endif /* !GNU */
-# elif defined(LINUX) && !defined(ARM32) && !defined(AVR32) \
- && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >=3))
+# elif (defined(LINUX) && !defined(ARM32) && !defined(AVR32) \
+ && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) \
+ && !(defined(__clang__) && defined(PLATFORM_ANDROID))) \
+ || (defined(PLATFORM_ANDROID) && defined(ARM32) \
+ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
+ /* As of Android NDK r8e, Clang cannot find __tls_get_addr. */
# define USE_COMPILER_TLS
# elif defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) \
- || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) \
- || defined(GC_NETBSD_THREADS) || defined(GC_RTEMS_PTHREADS)
+ || defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \
+ || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) \
+ || defined(GC_LINUX_THREADS) || defined(GC_RTEMS_PTHREADS)
# define USE_PTHREAD_SPECIFIC
# elif defined(GC_HPUX_THREADS)
# ifdef __GNUC__
-# define USE_PTHREAD_SPECIFIC
- /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */
+# define USE_PTHREAD_SPECIFIC
+ /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */
# else
-# define USE_COMPILER_TLS
+# define USE_COMPILER_TLS
# endif
# else
-# define USE_CUSTOM_SPECIFIC /* Use our own. */
+# define USE_CUSTOM_SPECIFIC /* Use our own. */
# endif
#endif
diff --git a/mach_dep.c b/mach_dep.c
index 28ad562b..9c95e698 100644
--- a/mach_dep.c
+++ b/mach_dep.c
@@ -15,12 +15,6 @@
#include "private/gc_priv.h"
#include <stdio.h>
-#include <setjmp.h>
-
-#if defined(OS2) || defined(CX_UX) || defined(__CC_ARM)
-# define _setjmp(b) setjmp(b)
-# define _longjmp(b,v) longjmp(b,v)
-#endif
#ifdef AMIGA
# ifndef __GNUC__
@@ -111,7 +105,7 @@
# if defined(M68K) && defined(AMIGA)
/* This function is not static because it could also be */
- /* errorneously defined in .S file, so this error would be caught */
+ /* erroneously defined in .S file, so this error would be caught */
/* by the linker. */
void GC_push_regs(void)
{
@@ -219,7 +213,7 @@
GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
ptr_t arg)
{
- word dummy;
+ volatile int dummy;
void * context = 0;
# if defined(HAVE_PUSH_REGS)
@@ -266,31 +260,28 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
/* subsumed by the getcontext() call. */
GC_save_regs_ret_val = GC_save_regs_in_stack();
# endif /* register windows. */
-# elif defined(HAVE_BUILTIN_UNWIND_INIT) \
- && !(defined(POWERPC) && defined(DARWIN)) \
- && !(defined(I386) && defined(RTEMS))
+# elif defined(HAVE_BUILTIN_UNWIND_INIT)
/* This was suggested by Richard Henderson as the way to */
/* force callee-save registers and register windows onto */
/* the stack. */
- /* Mark Sibly points out that this doesn't seem to work */
- /* on MacOS 10.3.9/PowerPC. */
__builtin_unwind_init();
# else /* !HAVE_BUILTIN_UNWIND_INIT && !UNIX_LIKE */
/* && !HAVE_PUSH_REGS */
/* Generic code */
/* The idea is due to Parag Patel at HP. */
/* We're not sure whether he would like */
- /* to be he acknowledged for it or not. */
+ /* to be acknowledged for it or not. */
jmp_buf regs;
register word * i = (word *) regs;
register ptr_t lim = (ptr_t)(regs) + (sizeof regs);
/* Setjmp doesn't always clear all of the buffer. */
/* That tends to preserve garbage. Clear it. */
- for (; (char *)i < lim; i++) {
+ for (; (word)i < (word)lim; i++) {
*i = 0;
}
# if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \
+ || defined(OS2) || defined(CX_UX) || defined(__CC_ARM) \
|| defined(LINUX) || defined(EWS4800) || defined(RTEMS)
(void) setjmp(regs);
# else
diff --git a/malloc.c b/malloc.c
index 1400fc32..4983cdfe 100644
--- a/malloc.c
+++ b/malloc.c
@@ -73,6 +73,7 @@ GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags)
if (GC_large_allocd_bytes > GC_max_large_allocd_bytes)
GC_max_large_allocd_bytes = GC_large_allocd_bytes;
}
+ /* FIXME: Do we need some way to reset GC_max_large_allocd_bytes? */
result = h -> hb_body;
}
return result;
@@ -111,16 +112,28 @@ GC_INNER void * GC_generic_malloc_inner(size_t lb, int k)
op = *opp;
if (EXPECT(0 == op, FALSE)) {
- if (GC_size_map[lb] == 0) {
- if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
- if (GC_size_map[lb] == 0) GC_extend_size_map(lb);
- return(GC_generic_malloc_inner(lb, k));
+ if (lg == 0) {
+ if (!EXPECT(GC_is_initialized, TRUE)) {
+ GC_init();
+ lg = GC_size_map[lb];
}
- if (kind -> ok_reclaim_list == 0) {
- if (!GC_alloc_reclaim_list(kind)) goto out;
+ if (0 == lg) {
+ GC_extend_size_map(lb);
+ lg = GC_size_map[lb];
+ GC_ASSERT(lg != 0);
}
+ /* Retry */
+ opp = &(kind -> ok_freelist[lg]);
+ op = *opp;
+ }
+ if (0 == op) {
+ if (0 == kind -> ok_reclaim_list &&
+ !GC_alloc_reclaim_list(kind))
+ return NULL;
op = GC_allocobj(lg, k);
- if (op == 0) goto out;
+ if (0 == op)
+ return NULL;
+ }
}
*opp = obj_link(op);
obj_link(op) = 0;
@@ -130,7 +143,6 @@ GC_INNER void * GC_generic_malloc_inner(size_t lb, int k)
GC_bytes_allocd += lb;
}
-out:
return op;
}
@@ -150,6 +162,12 @@ GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k)
return op;
}
+#ifdef GC_COLLECT_AT_MALLOC
+ /* Parameter to force GC at every malloc of size greater or equal to */
+ /* the given value. This might be handy during debugging. */
+ size_t GC_dbg_collect_at_malloc_min_lb = (GC_COLLECT_AT_MALLOC);
+#endif
+
GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
{
void * result;
@@ -158,6 +176,7 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
if (EXPECT(GC_have_errors, FALSE))
GC_print_all_errors();
GC_INVOKE_FINALIZERS();
+ GC_DBG_COLLECT_AT_MALLOC(lb);
if (SMALL_OBJ(lb)) {
LOCK();
result = GC_generic_malloc_inner((word)lb, k);
@@ -167,8 +186,11 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
size_t lb_rounded;
word n_blocks;
GC_bool init;
+
lg = ROUNDED_UP_GRANULES(lb);
lb_rounded = GRANULES_TO_BYTES(lg);
+ if (lb_rounded < lb)
+ return((*GC_get_oom_fn())(lb));
n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded);
init = GC_obj_kinds[k].ok_init;
LOCK();
@@ -200,7 +222,7 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
}
}
-/* Allocate lb bytes of atomic (pointerfree) data */
+/* Allocate lb bytes of atomic (pointer-free) data. */
#ifdef THREAD_LOCAL_ALLOC
GC_INNER void * GC_core_malloc_atomic(size_t lb)
#else
@@ -213,6 +235,7 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
DCL_LOCK_STATE;
if(SMALL_OBJ(lb)) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
lg = GC_size_map[lb];
opp = &(GC_aobjfreelist[lg]);
LOCK();
@@ -242,6 +265,7 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
DCL_LOCK_STATE;
if(SMALL_OBJ(lb)) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
lg = GC_size_map[lb];
opp = (void **)&(GC_objfreelist[lg]);
LOCK();
@@ -264,7 +288,7 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k)
}
}
-/* Allocate lb bytes of pointerful, traced, but not collectable data */
+/* Allocate lb bytes of pointerful, traced, but not collectible data. */
GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb)
{
void *op;
@@ -273,6 +297,7 @@ GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb)
DCL_LOCK_STATE;
if( SMALL_OBJ(lb) ) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
if (EXTRA_BYTES != 0 && lb != 0) lb--;
/* We don't need the extra byte, since this won't be */
/* collected anyway. */
@@ -309,7 +334,11 @@ GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb)
/* mark bits. */
LOCK();
set_mark_bit_from_hdr(hhdr, 0); /* Only object. */
- GC_ASSERT(hhdr -> hb_n_marks == 0);
+# ifndef THREADS
+ GC_ASSERT(hhdr -> hb_n_marks == 0);
+ /* This is not guaranteed in the multi-threaded case */
+ /* because the counter could be updated before locking. */
+# endif
hhdr -> hb_n_marks = 1;
UNLOCK();
return((void *) op);
@@ -326,9 +355,7 @@ GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb)
/* malloc replacements. Otherwise we end up saving a */
/* meaningless return address in the object. It also speeds things up, */
/* but it is admittedly quite ugly. */
-
-# define GC_debug_malloc_replacement(lb) \
- GC_debug_malloc(lb, GC_DBG_RA "unknown", 0)
+# define GC_debug_malloc_replacement(lb) GC_debug_malloc(lb, GC_DBG_EXTRAS)
void * malloc(size_t lb)
{
@@ -336,13 +363,10 @@ void * malloc(size_t lb)
/* But any decent compiler should reduce the extra procedure call */
/* to at most a jump instruction in this case. */
# if defined(I386) && defined(GC_SOLARIS_THREADS)
- /*
- * Thread initialisation can call malloc before
- * we're ready for it.
- * It's not clear that this is enough to help matters.
- * The thread implementation may well call malloc at other
- * inopportune times.
- */
+ /* Thread initialization can call malloc before we're ready for. */
+ /* It's not clear that this is enough to help matters. */
+ /* The thread implementation may well call malloc at other */
+ /* inopportune times. */
if (!EXPECT(GC_is_initialized, TRUE)) return sbrk(lb);
# endif /* I386 && GC_SOLARIS_THREADS */
return((void *)REDIRECT_MALLOC(lb));
@@ -372,11 +396,23 @@ void * malloc(size_t lb)
}
#endif /* GC_LINUX_THREADS */
+#include <limits.h>
+#ifdef SIZE_MAX
+# define GC_SIZE_MAX SIZE_MAX
+#else
+# define GC_SIZE_MAX (~(size_t)0)
+#endif
+
+#define GC_SQRT_SIZE_MAX ((1U << (WORDSZ / 2)) - 1)
+
void * calloc(size_t n, size_t lb)
{
+ if ((lb | n) > GC_SQRT_SIZE_MAX /* fast initial test */
+ && lb && n > GC_SIZE_MAX / lb)
+ return NULL;
# 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. */
+ /* mmapped thread stacks. Make sure it is not collectible. */
{
static GC_bool lib_bounds_set = FALSE;
ptr_t caller = (ptr_t)__builtin_return_address(0);
@@ -386,8 +422,10 @@ void * calloc(size_t n, size_t lb)
GC_init_lib_bounds();
lib_bounds_set = TRUE;
}
- if ((caller >= GC_libpthread_start && caller < GC_libpthread_end)
- || (caller >= GC_libld_start && caller < GC_libld_end))
+ if (((word)caller >= (word)GC_libpthread_start
+ && (word)caller < (word)GC_libpthread_end)
+ || ((word)caller >= (word)GC_libld_start
+ && (word)caller < (word)GC_libld_end))
return GC_malloc_uncollectable(n*lb);
/* The two ranges are actually usually adjacent, so there may */
/* be a way to speed this up. */
@@ -451,7 +489,8 @@ GC_API void GC_CALL GC_free(void * p)
if (p == 0) return;
/* Required by ANSI. It's not my fault ... */
# ifdef LOG_ALLOCS
- GC_err_printf("GC_free(%p): %lu\n", p, (unsigned long)GC_gc_no);
+ GC_log_printf("GC_free(%p) after GC #%lu\n",
+ p, (unsigned long)GC_gc_no);
# endif
h = HBLKPTR(p);
hhdr = HDR(h);
@@ -509,7 +548,6 @@ GC_API void GC_CALL GC_free(void * p)
void ** flh;
int knd;
struct obj_kind * ok;
- DCL_LOCK_STATE;
h = HBLKPTR(p);
hhdr = HDR(h);
@@ -549,12 +587,14 @@ GC_API void GC_CALL GC_free(void * p)
{
/* Don't bother with initialization checks. If nothing */
/* has been initialized, the check fails, and that's safe, */
- /* since we haven't allocated uncollectable objects either. */
+ /* since we have not allocated uncollectible objects neither. */
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
- || (caller >= GC_libld_start && caller < GC_libld_end)) {
+ if (((word)caller >= (word)GC_libpthread_start
+ && (word)caller < (word)GC_libpthread_end)
+ || ((word)caller >= (word)GC_libld_start
+ && (word)caller < (word)GC_libld_end)) {
GC_free(p);
return;
}
diff --git a/mallocx.c b/mallocx.c
index a907df00..7fcaa5c2 100644
--- a/mallocx.c
+++ b/mallocx.c
@@ -153,9 +153,8 @@ GC_API void * GC_CALL GC_realloc(void * p, size_t lb)
# ifdef REDIRECT_REALLOC
/* As with malloc, avoid two levels of extra calls here. */
-
# define GC_debug_realloc_replacement(p, lb) \
- GC_debug_realloc(p, lb, GC_DBG_RA "unknown", 0)
+ GC_debug_realloc(p, lb, GC_DBG_EXTRAS)
void * realloc(void * p, size_t lb)
{
@@ -165,11 +164,10 @@ void * realloc(void * p, size_t lb)
# undef GC_debug_realloc_replacement
# endif /* REDIRECT_REALLOC */
-
/* Allocate memory such that only pointers to near the */
/* beginning of the object are considered. */
-/* We avoid holding allocation lock while we clear memory. */
-GC_INNER void * GC_generic_malloc_ignore_off_page(size_t lb, int k)
+/* We avoid holding allocation lock while we clear the memory. */
+GC_API void * GC_CALL GC_generic_malloc_ignore_off_page(size_t lb, int k)
{
void *result;
size_t lg;
@@ -182,11 +180,14 @@ GC_INNER void * GC_generic_malloc_ignore_off_page(size_t lb, int k)
return(GC_generic_malloc((word)lb, k));
lg = ROUNDED_UP_GRANULES(lb);
lb_rounded = GRANULES_TO_BYTES(lg);
+ if (lb_rounded < lb)
+ return((*GC_get_oom_fn())(lb));
n_blocks = OBJ_SZ_TO_BLOCKS(lb_rounded);
init = GC_obj_kinds[k].ok_init;
if (EXPECT(GC_have_errors, FALSE))
GC_print_all_errors();
GC_INVOKE_FINALIZERS();
+ GC_DBG_COLLECT_AT_MALLOC(lb);
LOCK();
result = (ptr_t)GC_alloc_large(ADD_SLOP(lb), k, IGNORE_OFF_PAGE);
if (0 != result) {
@@ -241,7 +242,7 @@ GC_API void GC_CALL GC_incr_bytes_freed(size_t n)
}
# ifdef PARALLEL_MARK
- STATIC volatile signed_word GC_bytes_allocd_tmp = 0;
+ STATIC volatile AO_t GC_bytes_allocd_tmp = 0;
/* Number of bytes of memory allocated since */
/* we released the GC lock. Instead of */
/* reacquiring the GC lock just to add this in, */
@@ -291,6 +292,7 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result)
if (EXPECT(GC_have_errors, FALSE))
GC_print_all_errors();
GC_INVOKE_FINALIZERS();
+ GC_DBG_COLLECT_AT_MALLOC(lb);
LOCK();
if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
/* Do our share of marking work */
@@ -314,16 +316,16 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result)
hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no;
# ifdef PARALLEL_MARK
if (GC_parallel) {
- signed_word my_bytes_allocd_tmp = GC_bytes_allocd_tmp;
-
+ signed_word my_bytes_allocd_tmp =
+ (signed_word)AO_load(&GC_bytes_allocd_tmp);
GC_ASSERT(my_bytes_allocd_tmp >= 0);
/* We only decrement it while holding the GC lock. */
/* Thus we can't accidentally adjust it down in more */
/* than one thread simultaneously. */
+
if (my_bytes_allocd_tmp != 0) {
- (void)AO_fetch_and_add(
- (volatile void *)(&GC_bytes_allocd_tmp),
- (AO_t)(-my_bytes_allocd_tmp));
+ (void)AO_fetch_and_add(&GC_bytes_allocd_tmp,
+ (AO_t)(-my_bytes_allocd_tmp));
GC_bytes_allocd += my_bytes_allocd_tmp;
}
GC_acquire_mark_lock();
@@ -343,9 +345,8 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result)
# ifdef PARALLEL_MARK
if (GC_parallel) {
*result = op;
- (void)AO_fetch_and_add(
- (volatile AO_t *)(&GC_bytes_allocd_tmp),
- (AO_t)(my_bytes_allocd));
+ (void)AO_fetch_and_add(&GC_bytes_allocd_tmp,
+ (AO_t)my_bytes_allocd);
GC_acquire_mark_lock();
-- GC_fl_builder_count;
if (GC_fl_builder_count == 0) GC_notify_all_builder();
@@ -365,8 +366,8 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result)
GC_release_mark_lock();
LOCK();
/* GC lock is needed for reclaim list access. We */
- /* must decrement fl_builder_count before reaquiring GC */
- /* lock. Hopefully this path is rare. */
+ /* must decrement fl_builder_count before reacquiring */
+ /* the lock. Hopefully this path is rare. */
}
# endif
}
@@ -463,6 +464,8 @@ GC_API void * GC_CALL GC_memalign(size_t align, size_t lb)
/* is a multiple of align. That would be correct up to HBLKSIZE. */
new_lb = lb + align - 1;
result = GC_malloc(new_lb);
+ /* It is OK not to check result for NULL as in that case */
+ /* GC_memalign returns NULL too since (0 + 0 % align) is 0. */
offset = (word)result % align;
if (offset != 0) {
offset = align - offset;
@@ -476,7 +479,7 @@ GC_API void * GC_CALL GC_memalign(size_t align, size_t lb)
return result;
}
-/* This one exists largerly to redirect posix_memalign for leaks finding. */
+/* This one exists largely to redirect posix_memalign for leaks finding. */
GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb)
{
/* Check alignment properly. */
@@ -499,7 +502,7 @@ GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb)
}
#ifdef ATOMIC_UNCOLLECTABLE
- /* Allocate lb bytes of pointerfree, untraced, uncollectable data */
+ /* Allocate lb bytes of pointer-free, untraced, uncollectible data */
/* This is normally roughly equivalent to the system malloc. */
/* But it may be useful if malloc is redefined. */
GC_API void * GC_CALL GC_malloc_atomic_uncollectable(size_t lb)
@@ -510,6 +513,7 @@ GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb)
DCL_LOCK_STATE;
if( SMALL_OBJ(lb) ) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
if (EXTRA_BYTES != 0 && lb != 0) lb--;
/* We don't need the extra byte, since this won't be */
/* collected anyway. */
@@ -541,7 +545,9 @@ GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb)
LOCK();
set_mark_bit_from_hdr(hhdr, 0); /* Only object. */
- GC_ASSERT(hhdr -> hb_n_marks == 0);
+# ifndef THREADS
+ GC_ASSERT(hhdr -> hb_n_marks == 0);
+# endif
hhdr -> hb_n_marks = 1;
UNLOCK();
return((void *) op);
@@ -563,12 +569,7 @@ GC_API char * GC_CALL GC_strdup(const char *s)
# endif
return NULL;
}
-# ifndef MSWINCE
- strcpy(copy, s);
-# else
- /* strcpy() is deprecated in WinCE */
- memcpy(copy, s, lb);
-# endif
+ BCOPY(s, copy, lb);
return copy;
}
diff --git a/mark.c b/mark.c
index 1e584249..60a1a1e4 100644
--- a/mark.c
+++ b/mark.c
@@ -22,17 +22,14 @@
# include <excpt.h>
#endif
-/* We put this here to minimize the risk of inlining. */
-/*VARARGS*/
-#if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__CC_ARM)
- void GC_noop(void * p GC_ATTR_UNUSED, ...) {}
-#else
-# ifdef __DMC__
- void GC_noop(...) {}
-# else
- void GC_noop() {}
-# endif
-#endif
+/* Make arguments appear live to compiler. Put here to minimize the */
+/* risk of inlining. Used to minimize junk left in registers. */
+void GC_noop6(word arg1 GC_ATTR_UNUSED, word arg2 GC_ATTR_UNUSED,
+ word arg3 GC_ATTR_UNUSED, word arg4 GC_ATTR_UNUSED,
+ word arg5 GC_ATTR_UNUSED, word arg6 GC_ATTR_UNUSED)
+{
+ /* Empty */
+}
/* Single argument version, robust against whole program analysis. */
volatile word GC_noop_sink;
@@ -100,30 +97,18 @@ GC_INNER unsigned GC_n_kinds = GC_N_KINDS_INITIAL_VALUE;
/* grow dynamically. */
# endif
-/*
- * Limits of stack for GC_mark routine.
- * All ranges between GC_mark_stack(incl.) and GC_mark_stack_top(incl.) still
- * need to be marked from.
- */
-
STATIC word GC_n_rescuing_pages = 0;
/* Number of dirty pages we marked from */
/* excludes ptrfree pages, etc. */
-GC_INNER mse * GC_mark_stack = NULL;
-GC_INNER mse * GC_mark_stack_limit = NULL;
GC_INNER size_t GC_mark_stack_size = 0;
#ifdef PARALLEL_MARK
- GC_INNER mse * volatile GC_mark_stack_top = NULL;
- /* Updated only with mark lock held, but read asynchronously. */
STATIC volatile AO_t GC_first_nonempty = 0;
/* Lowest entry on mark stack */
/* that may be nonempty. */
/* Updated only by initiating */
/* thread. */
-#else
- GC_INNER mse * GC_mark_stack_top = NULL;
#endif
GC_INNER mark_state_t GC_mark_state = MS_NONE;
@@ -133,7 +118,7 @@ GC_INNER GC_bool GC_mark_stack_too_small = FALSE;
static struct hblk * scan_ptr;
STATIC GC_bool GC_objects_are_marked = FALSE;
- /* Are there collectable marked objects in the heap? */
+ /* Are there collectible marked objects in the heap? */
/* Is a collection in progress? Note that this can return true in the */
/* nonincremental case, if a collection has been abandoned and the */
@@ -152,7 +137,7 @@ GC_INNER void GC_clear_hdr_marks(hdr *hhdr)
hhdr -> hb_n_marks = 0;
}
-/* Set all mark bits in the header. Used for uncollectable blocks. */
+/* Set all mark bits in the header. Used for uncollectible blocks. */
GC_INNER void GC_set_hdr_marks(hdr *hhdr)
{
unsigned i;
@@ -230,7 +215,7 @@ GC_API int GC_CALL GC_is_marked(const void *p)
hdr * hhdr = HDR(h);
word bit_no = MARK_BIT_NO((ptr_t)p - (ptr_t)h, hhdr -> hb_sz);
- return (int)mark_bit_from_hdr(hhdr, bit_no); // 0 or 1
+ return (int)mark_bit_from_hdr(hhdr, bit_no); /* 0 or 1 */
}
/*
@@ -287,13 +272,14 @@ GC_INNER void GC_initiate_gc(void)
STATIC struct hblk * GC_push_next_marked(struct hblk *h);
/* Ditto, but also mark from clean pages. */
STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h);
- /* Ditto, but mark only from uncollectable pages. */
+ /* Ditto, but mark only from uncollectible pages. */
static void alloc_mark_stack(size_t);
-# if (defined(MSWIN32) || defined(MSWINCE)) && !defined(__GNUC__) \
- || defined(MSWIN32) && defined(I386) /* for Win98 */ \
- || defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)
+# if (((defined(MSWIN32) || defined(MSWINCE)) && !defined(__GNUC__)) \
+ || (defined(MSWIN32) && defined(I386)) /* for Win98 */ \
+ || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))) \
+ && !defined(NO_WRAP_MARK_SOME)
/* Under rare conditions, we may end up marking from nonexistent memory. */
/* Hence we need to be prepared to recover by running GC_mark_some */
/* with a suitable handler in place. */
@@ -328,8 +314,8 @@ static void alloc_mark_stack(size_t);
break;
case MS_PUSH_RESCUERS:
- if (GC_mark_stack_top
- >= GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/2) {
+ if ((word)GC_mark_stack_top
+ >= (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/2)) {
/* Go ahead and mark, even though that might cause us to */
/* see more marked dirty objects later on. Avoid this */
/* in the future. */
@@ -339,10 +325,8 @@ static void alloc_mark_stack(size_t);
} else {
scan_ptr = GC_push_next_marked_dirty(scan_ptr);
if (scan_ptr == 0) {
- if (GC_print_stats) {
- GC_log_printf("Marked from %lu dirty pages\n",
- (unsigned long)GC_n_rescuing_pages);
- }
+ GC_COND_LOG_PRINTF("Marked from %lu dirty pages\n",
+ (unsigned long)GC_n_rescuing_pages);
GC_push_roots(FALSE, cold_gc_frame);
GC_objects_are_marked = TRUE;
if (GC_mark_state != MS_INVALID) {
@@ -353,8 +337,8 @@ static void alloc_mark_stack(size_t);
break;
case MS_PUSH_UNCOLLECTABLE:
- if (GC_mark_stack_top
- >= GC_mark_stack + GC_mark_stack_size/4) {
+ if ((word)GC_mark_stack_top
+ >= (word)(GC_mark_stack + GC_mark_stack_size/4)) {
# ifdef PARALLEL_MARK
/* Avoid this, since we don't parallelize the marker */
/* here. */
@@ -386,7 +370,7 @@ static void alloc_mark_stack(size_t);
/* the allocation lock. */
if (GC_parallel) {
GC_do_parallel_mark();
- GC_ASSERT(GC_mark_stack_top < (mse *)GC_first_nonempty);
+ GC_ASSERT((word)GC_mark_stack_top < (word)GC_first_nonempty);
GC_mark_stack_top = GC_mark_stack - 1;
if (GC_mark_stack_too_small) {
alloc_mark_stack(2*GC_mark_stack_size);
@@ -398,7 +382,7 @@ static void alloc_mark_stack(size_t);
break;
}
# endif
- if (GC_mark_stack_top >= GC_mark_stack) {
+ if ((word)GC_mark_stack_top >= (word)GC_mark_stack) {
MARK_FROM_MARK_STACK();
break;
} else {
@@ -415,7 +399,7 @@ static void alloc_mark_stack(size_t);
GC_mark_state = MS_PUSH_UNCOLLECTABLE;
break;
}
- if (GC_mark_stack_top >= GC_mark_stack) {
+ if ((word)GC_mark_stack_top >= (word)GC_mark_stack) {
MARK_FROM_MARK_STACK();
break;
}
@@ -482,7 +466,7 @@ static void alloc_mark_stack(size_t);
# endif /* __GNUC__ && MSWIN32 */
#if defined(GC_WIN32_THREADS) && !defined(__GNUC__)
- GC_bool GC_started_thread_while_stopped(void);
+ GC_INNER GC_bool GC_started_thread_while_stopped(void);
/* In win32_threads.c. Did we invalidate mark phase with an */
/* unexpected thread start? */
#endif
@@ -570,10 +554,8 @@ static void alloc_mark_stack(size_t);
handle_ex:
/* Exception handler starts here for all cases. */
- if (GC_print_stats) {
- GC_log_printf(
- "Caught ACCESS_VIOLATION in marker; memory mapping disappeared\n");
- }
+ WARN("Caught ACCESS_VIOLATION in marker;"
+ " memory mapping disappeared\n", 0);
/* We have bad roots on the stack. Discard mark stack. */
/* Rescan from marked objects. Redetermine roots. */
@@ -585,11 +567,6 @@ handle_ex:
}
#endif /* WRAP_MARK_SOME */
-GC_INNER GC_bool GC_mark_stack_empty(void)
-{
- return(GC_mark_stack_top < GC_mark_stack);
-}
-
GC_INNER void GC_invalidate_mark_state(void)
{
GC_mark_state = MS_INVALID;
@@ -600,10 +577,8 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp)
{
GC_mark_state = MS_INVALID;
GC_mark_stack_too_small = TRUE;
- if (GC_print_stats) {
- GC_log_printf("Mark stack overflow; current size = %lu entries\n",
- (unsigned long)GC_mark_stack_size);
- }
+ GC_COND_LOG_PRINTF("Mark stack overflow; current size = %lu entries\n",
+ (unsigned long)GC_mark_stack_size);
return(msp - GC_MARK_STACK_DISCARDS);
}
@@ -626,9 +601,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
{
signed_word credit = HBLKSIZE; /* Remaining credit for marking work */
ptr_t current_p; /* Pointer to current candidate ptr. */
- word current; /* Candidate pointer. */
- ptr_t limit; /* (Incl) limit of current candidate */
- /* range */
+ word current; /* Candidate pointer. */
+ ptr_t limit; /* (Incl) limit of current candidate range. */
word descr;
ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
ptr_t least_ha = GC_least_plausible_heap_addr;
@@ -639,13 +613,13 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
GC_objects_are_marked = TRUE;
INIT_HDR_CACHE;
# ifdef OS2 /* Use untweaked version to circumvent compiler problem */
- while (mark_stack_top >= mark_stack && credit >= 0)
+ while ((word)mark_stack_top >= (word)mark_stack && credit >= 0)
# else
while ((((ptr_t)mark_stack_top - (ptr_t)mark_stack) | credit) >= 0)
# endif
{
current_p = mark_stack_top -> mse_start;
- descr = mark_stack_top -> mse_descr;
+ descr = mark_stack_top -> mse_descr.w;
retry:
/* current_p and descr describe the current object. */
/* *mark_stack_top is vacant. */
@@ -663,26 +637,26 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr
- (word)GC_least_plausible_heap_addr);
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
- && GC_trace_addr < current_p + descr) {
- GC_log_printf("GC:%u Large section; start %p len %lu\n",
+ if ((word)GC_trace_addr >= (word)current_p
+ && (word)GC_trace_addr < (word)(current_p + descr)) {
+ GC_log_printf("GC #%u: large section; start %p, len %lu\n",
(unsigned)GC_gc_no, current_p, (unsigned long)descr);
}
# endif /* ENABLE_TRACE */
# ifdef PARALLEL_MARK
# define SHARE_BYTES 2048
if (descr > SHARE_BYTES && GC_parallel
- && mark_stack_top < mark_stack_limit - 1) {
+ && (word)mark_stack_top < (word)(mark_stack_limit - 1)) {
int new_size = (descr/2) & ~(sizeof(word)-1);
mark_stack_top -> mse_start = current_p;
- mark_stack_top -> mse_descr = new_size + sizeof(word);
+ mark_stack_top -> mse_descr.w = new_size + sizeof(word);
/* makes sure we handle */
/* misaligned pointers. */
mark_stack_top++;
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
- && GC_trace_addr < current_p + descr) {
- GC_log_printf("GC:%u Splitting (parallel) %p at %p\n",
+ if ((word)GC_trace_addr >= (word)current_p
+ && (word)GC_trace_addr < (word)(current_p + descr)) {
+ GC_log_printf("GC #%u: splitting (parallel) %p at %p\n",
(unsigned)GC_gc_no, current_p, current_p + new_size);
}
# endif /* ENABLE_TRACE */
@@ -693,12 +667,12 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
# endif /* PARALLEL_MARK */
mark_stack_top -> mse_start =
limit = current_p + WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1);
- mark_stack_top -> mse_descr =
- descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1);
+ mark_stack_top -> mse_descr.w =
+ descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1);
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
- && GC_trace_addr < current_p + descr) {
- GC_log_printf("GC:%u Splitting %p at %p\n",
+ if ((word)GC_trace_addr >= (word)current_p
+ && (word)GC_trace_addr < (word)(current_p + descr)) {
+ GC_log_printf("GC #%u: splitting %p at %p\n",
(unsigned)GC_gc_no, current_p, limit);
}
# endif /* ENABLE_TRACE */
@@ -709,10 +683,12 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
case GC_DS_BITMAP:
mark_stack_top--;
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
- && GC_trace_addr < current_p + WORDS_TO_BYTES(WORDSZ-2)) {
- GC_log_printf("GC:%u Tracing from %p bitmap descr %lu\n",
- (unsigned)GC_gc_no, current_p, (unsigned long)descr);
+ if ((word)GC_trace_addr >= (word)current_p
+ && (word)GC_trace_addr < (word)(current_p
+ + WORDS_TO_BYTES(WORDSZ-2))) {
+ GC_log_printf("GC #%u: tracing from %p bitmap descr %lu\n",
+ (unsigned)GC_gc_no, current_p,
+ (unsigned long)descr);
}
# endif /* ENABLE_TRACE */
descr &= ~GC_DS_TAGS;
@@ -721,12 +697,13 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
if ((signed_word)descr < 0) {
current = *(word *)current_p;
FIXUP_POINTER(current);
- if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) {
+ if (current >= (word)least_ha && current < (word)greatest_ha) {
PREFETCH((ptr_t)current);
# ifdef ENABLE_TRACE
if (GC_trace_addr == current_p) {
- GC_log_printf("GC:%u Considering(3) %p -> %p\n",
- (unsigned)GC_gc_no, current_p, (ptr_t)current);
+ GC_log_printf("GC #%u: considering(3) %p -> %p\n",
+ (unsigned)GC_gc_no, current_p,
+ (ptr_t)current);
}
# endif /* ENABLE_TRACE */
PUSH_CONTENTS((ptr_t)current, mark_stack_top,
@@ -740,18 +717,17 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
case GC_DS_PROC:
mark_stack_top--;
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
+ if ((word)GC_trace_addr >= (word)current_p
&& GC_base(current_p) != 0
&& GC_base(current_p) == GC_base(GC_trace_addr)) {
- GC_log_printf("GC:%u Tracing from %p proc descr %lu\n",
- (unsigned)GC_gc_no, current_p, (unsigned long)descr);
+ GC_log_printf("GC #%u: tracing from %p, proc descr %lu\n",
+ (unsigned)GC_gc_no, current_p,
+ (unsigned long)descr);
}
# endif /* ENABLE_TRACE */
credit -= GC_PROC_BYTES;
- mark_stack_top =
- (*PROC(descr))
- ((word *)current_p, mark_stack_top,
- mark_stack_limit, ENV(descr));
+ mark_stack_top = (*PROC(descr))((word *)current_p, mark_stack_top,
+ mark_stack_limit, ENV(descr));
continue;
case GC_DS_PER_OBJECT:
if ((signed_word)descr >= 0) {
@@ -790,13 +766,17 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
}
} else /* Small object with length descriptor */ {
mark_stack_top--;
+# ifndef SMALL_CONFIG
+ if (descr < sizeof(word))
+ continue;
+# endif
limit = current_p + (word)descr;
}
# ifdef ENABLE_TRACE
- if (GC_trace_addr >= current_p
- && GC_trace_addr < limit) {
- GC_log_printf("GC:%u Tracing from %p len %lu\n",
- (int)GC_gc_no, current_p, (unsigned long)descr);
+ if ((word)GC_trace_addr >= (word)current_p
+ && (word)GC_trace_addr < (word)limit) {
+ GC_log_printf("GC #%u: Tracing from %p, length is %lu\n",
+ (unsigned)GC_gc_no, current_p, (unsigned long)descr);
}
# endif /* ENABLE_TRACE */
/* The simple case in which we're scanning a range. */
@@ -809,7 +789,7 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
# ifndef SMALL_CONFIG
word deferred;
- /* Try to prefetch the next pointer to be examined asap. */
+ /* Try to prefetch the next pointer to be examined ASAP. */
/* Empirically, this also seems to help slightly without */
/* prefetches, at least on linux/X86. Presumably this loop */
/* ends up with less register pressure, and gcc thus ends up */
@@ -817,47 +797,47 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
/* for this loop is still not great. */
for(;;) {
PREFETCH(limit - PREF_DIST*CACHE_LINE_SIZE);
- GC_ASSERT(limit >= current_p);
+ GC_ASSERT((word)limit >= (word)current_p);
deferred = *(word *)limit;
FIXUP_POINTER(deferred);
limit -= ALIGNMENT;
- if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) {
+ if (deferred >= (word)least_ha && deferred < (word)greatest_ha) {
PREFETCH((ptr_t)deferred);
break;
}
- if (current_p > limit) goto next_object;
+ if ((word)current_p > (word)limit) goto next_object;
/* Unroll once, so we don't do too many of the prefetches */
/* based on limit. */
deferred = *(word *)limit;
FIXUP_POINTER(deferred);
limit -= ALIGNMENT;
- if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) {
+ if (deferred >= (word)least_ha && deferred < (word)greatest_ha) {
PREFETCH((ptr_t)deferred);
break;
}
- if (current_p > limit) goto next_object;
+ if ((word)current_p > (word)limit) goto next_object;
}
# endif
- while (current_p <= limit) {
+ while ((word)current_p <= (word)limit) {
/* Empirically, unrolling this loop doesn't help a lot. */
/* Since PUSH_CONTENTS expands to a lot of code, */
/* we don't. */
current = *(word *)current_p;
FIXUP_POINTER(current);
PREFETCH(current_p + PREF_DIST*CACHE_LINE_SIZE);
- if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) {
+ if (current >= (word)least_ha && current < (word)greatest_ha) {
/* Prefetch the contents of the object we just pushed. It's */
/* likely we will need them soon. */
PREFETCH((ptr_t)current);
# ifdef ENABLE_TRACE
if (GC_trace_addr == current_p) {
- GC_log_printf("GC:%u Considering(1) %p -> %p\n",
+ GC_log_printf("GC #%u: considering(1) %p -> %p\n",
(unsigned)GC_gc_no, current_p, (ptr_t)current);
}
# endif /* ENABLE_TRACE */
PUSH_CONTENTS((ptr_t)current, mark_stack_top,
- mark_stack_limit, current_p, exit2);
+ mark_stack_limit, current_p, exit2);
}
current_p += ALIGNMENT;
}
@@ -868,12 +848,12 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
/* validity test. */
# ifdef ENABLE_TRACE
if (GC_trace_addr == current_p) {
- GC_log_printf("GC:%u Considering(2) %p -> %p\n",
+ GC_log_printf("GC #%u: considering(2) %p -> %p\n",
(unsigned)GC_gc_no, current_p, (ptr_t)deferred);
}
# endif /* ENABLE_TRACE */
PUSH_CONTENTS((ptr_t)deferred, mark_stack_top,
- mark_stack_limit, current_p, exit4);
+ mark_stack_limit, current_p, exit4);
next_object:;
# endif
}
@@ -897,7 +877,7 @@ GC_INNER word GC_mark_no = 0;
#define LOCAL_MARK_STACK_SIZE HBLKSIZE
/* Under normal circumstances, this is big enough to guarantee */
- /* We don't overflow half of it in a single call to */
+ /* we don't overflow half of it in a single call to */
/* GC_mark_from. */
@@ -913,19 +893,20 @@ STATIC mse * GC_steal_mark_stack(mse * low, mse * high, mse * local,
mse *top = local - 1;
unsigned i = 0;
- GC_ASSERT(high >= low-1 && (word)(high - low + 1) <= GC_mark_stack_size);
- for (p = low; p <= high && i <= max; ++p) {
- word descr = AO_load((volatile AO_t *) &(p -> mse_descr));
+ GC_ASSERT((word)high >= (word)(low - 1)
+ && (word)(high - low + 1) <= GC_mark_stack_size);
+ for (p = low; (word)p <= (word)high && i <= max; ++p) {
+ word descr = (word)AO_load(&p->mse_descr.ao);
if (descr != 0) {
/* Must be ordered after read of descr: */
- AO_store_release_write((volatile AO_t *) &(p -> mse_descr), 0);
+ AO_store_release_write(&p->mse_descr.ao, 0);
/* More than one thread may get this entry, but that's only */
/* a minor performance problem. */
++top;
- top -> mse_descr = descr;
+ top -> mse_descr.w = descr;
top -> mse_start = p -> mse_start;
- GC_ASSERT((top -> mse_descr & GC_DS_TAGS) != GC_DS_LENGTH ||
- top -> mse_descr < (word)GC_greatest_plausible_heap_addr
+ GC_ASSERT((top->mse_descr.w & GC_DS_TAGS) != GC_DS_LENGTH ||
+ top->mse_descr.w < (word)GC_greatest_plausible_heap_addr
- (word)GC_least_plausible_heap_addr);
/* If this is a big object, count it as */
/* size/256 + 1 objects. */
@@ -945,15 +926,14 @@ STATIC void GC_return_mark_stack(mse * low, mse * high)
mse * my_start;
size_t stack_size;
- if (high < low) return;
+ if ((word)high < (word)low) return;
stack_size = high - low + 1;
GC_acquire_mark_lock();
my_top = GC_mark_stack_top; /* Concurrent modification impossible. */
my_start = my_top + 1;
- if (my_start - GC_mark_stack + stack_size > GC_mark_stack_size) {
- if (GC_print_stats) {
- GC_log_printf("No room to copy back mark stack\n");
- }
+ if ((word)(my_start - GC_mark_stack + stack_size)
+ > (word)GC_mark_stack_size) {
+ GC_COND_LOG_PRINTF("No room to copy back mark stack\n");
GC_mark_state = MS_INVALID;
GC_mark_stack_too_small = TRUE;
/* We drop the local mark stack. We'll fix things later. */
@@ -987,26 +967,26 @@ STATIC void GC_do_local_mark(mse *local_mark_stack, mse *local_top)
for (n = 0; n < N_LOCAL_ITERS; ++n) {
local_top = GC_mark_from(local_top, local_mark_stack,
local_mark_stack + LOCAL_MARK_STACK_SIZE);
- if (local_top < local_mark_stack) return;
+ if ((word)local_top < (word)local_mark_stack) return;
if ((word)(local_top - local_mark_stack)
>= LOCAL_MARK_STACK_SIZE / 2) {
GC_return_mark_stack(local_mark_stack, local_top);
return;
}
}
- if ((mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top))
- < (mse *)AO_load(&GC_first_nonempty)
+ if ((word)AO_load((volatile AO_t *)&GC_mark_stack_top)
+ < (word)AO_load(&GC_first_nonempty)
&& GC_active_count < GC_helper_count
- && local_top > local_mark_stack + 1) {
+ && (word)local_top > (word)(local_mark_stack + 1)) {
/* Try to share the load, since the main stack is empty, */
/* and helper threads are waiting for a refill. */
/* The entries near the bottom of the stack are likely */
- /* to require more work. Thus we return those, eventhough */
+ /* to require more work. Thus we return those, even though */
/* it's harder. */
mse * new_bottom = local_mark_stack
+ (local_top - local_mark_stack)/2;
- GC_ASSERT(new_bottom > local_mark_stack
- && new_bottom < local_top);
+ GC_ASSERT((word)new_bottom > (word)local_mark_stack
+ && (word)new_bottom < (word)local_top);
GC_return_mark_stack(local_mark_stack, new_bottom - 1);
memmove(local_mark_stack, new_bottom,
(local_top - new_bottom + 1) * sizeof(mse));
@@ -1017,9 +997,6 @@ STATIC void GC_do_local_mark(mse *local_mark_stack, mse *local_top)
#define ENTRIES_TO_GET 5
-GC_INNER long GC_markers = 2; /* Normally changed by thread-library- */
- /* -specific code. */
-
/* Mark using the local mark stack until the global mark stack is empty */
/* and there are no active workers. Update GC_first_nonempty to reflect */
/* progress. */
@@ -1033,11 +1010,10 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
GC_acquire_mark_lock();
GC_active_count++;
my_first_nonempty = (mse *)AO_load(&GC_first_nonempty);
- GC_ASSERT((mse *)AO_load(&GC_first_nonempty) >= GC_mark_stack &&
- (mse *)AO_load(&GC_first_nonempty) <=
- (mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + 1);
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Starting mark helper %lu\n", (unsigned long)id);
+ GC_ASSERT((word)GC_mark_stack <= (word)my_first_nonempty);
+ GC_ASSERT((word)my_first_nonempty
+ <= (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + sizeof(mse));
+ GC_VERBOSE_LOG_PRINTF("Starting mark helper %lu\n", (unsigned long)id);
GC_release_mark_lock();
for (;;) {
size_t n_on_stack;
@@ -1046,15 +1022,17 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
mse * local_top;
mse * global_first_nonempty = (mse *)AO_load(&GC_first_nonempty);
- GC_ASSERT(my_first_nonempty >= GC_mark_stack &&
- my_first_nonempty <=
- (mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + 1);
- GC_ASSERT(global_first_nonempty >= GC_mark_stack &&
- global_first_nonempty <=
- (mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + 1);
- if (my_first_nonempty < global_first_nonempty) {
+ GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack &&
+ (word)my_first_nonempty <=
+ (word)AO_load((volatile AO_t *)&GC_mark_stack_top)
+ + sizeof(mse));
+ GC_ASSERT((word)global_first_nonempty >= (word)GC_mark_stack &&
+ (word)global_first_nonempty <=
+ (word)AO_load((volatile AO_t *)&GC_mark_stack_top)
+ + sizeof(mse));
+ if ((word)my_first_nonempty < (word)global_first_nonempty) {
my_first_nonempty = global_first_nonempty;
- } else if (global_first_nonempty < my_first_nonempty) {
+ } else if ((word)global_first_nonempty < (word)my_first_nonempty) {
AO_compare_and_swap(&GC_first_nonempty,
(AO_t) global_first_nonempty,
(AO_t) my_first_nonempty);
@@ -1078,15 +1056,16 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
/* on the stack. */
if (0 == GC_active_count) GC_notify_all_marker();
while (GC_active_count > 0
- && (mse *)AO_load(&GC_first_nonempty)
- > GC_mark_stack_top) {
+ && (word)AO_load(&GC_first_nonempty)
+ > (word)GC_mark_stack_top) {
/* We will be notified if either GC_active_count */
/* reaches zero, or if more objects are pushed on */
/* the global mark stack. */
GC_wait_marker();
}
- if (GC_active_count == 0 &&
- (mse *)AO_load(&GC_first_nonempty) > GC_mark_stack_top) {
+ if (GC_active_count == 0
+ && (word)AO_load(&GC_first_nonempty)
+ > (word)GC_mark_stack_top) {
GC_bool need_to_notify = FALSE;
/* The above conditions can't be falsified while we */
/* hold the mark lock, since neither */
@@ -1096,9 +1075,8 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
/* both conditions actually held simultaneously. */
GC_helper_count--;
if (0 == GC_helper_count) need_to_notify = TRUE;
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Finished mark helper %lu\n",
- (unsigned long)id);
+ GC_VERBOSE_LOG_PRINTF("Finished mark helper %lu\n",
+ (unsigned long)id);
GC_release_mark_lock();
if (need_to_notify) GC_notify_all_marker();
return;
@@ -1118,9 +1096,10 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
local_top = GC_steal_mark_stack(my_first_nonempty, my_top,
local_mark_stack, n_to_get,
&my_first_nonempty);
- GC_ASSERT(my_first_nonempty >= GC_mark_stack &&
- my_first_nonempty <=
- (mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + 1);
+ GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack &&
+ (word)my_first_nonempty <=
+ (word)AO_load((volatile AO_t *)&GC_mark_stack_top)
+ + sizeof(mse));
GC_do_local_mark(local_mark_stack, local_top);
}
}
@@ -1132,6 +1111,7 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id)
STATIC void GC_do_parallel_mark(void)
{
mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
+ /* Note: local_mark_stack is quite big (up to 128 KiB). */
GC_acquire_mark_lock();
GC_ASSERT(I_HOLD_LOCK());
@@ -1139,9 +1119,8 @@ STATIC void GC_do_parallel_mark(void)
/* all the time, especially since it's cheap. */
if (GC_help_wanted || GC_active_count != 0 || GC_helper_count != 0)
ABORT("Tried to start parallel mark in bad state");
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Starting marking for mark phase number %lu\n",
- (unsigned long)GC_mark_no);
+ GC_VERBOSE_LOG_PRINTF("Starting marking for mark phase number %lu\n",
+ (unsigned long)GC_mark_no);
GC_first_nonempty = (AO_t)GC_mark_stack;
GC_active_count = 0;
GC_helper_count = 1;
@@ -1153,11 +1132,12 @@ STATIC void GC_do_parallel_mark(void)
GC_acquire_mark_lock();
GC_help_wanted = FALSE;
/* Done; clean up. */
- while (GC_helper_count > 0) GC_wait_marker();
+ while (GC_helper_count > 0) {
+ GC_wait_marker();
+ }
/* GC_helper_count cannot be incremented while GC_help_wanted == FALSE */
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Finished marking for mark phase number %lu\n",
- (unsigned long)GC_mark_no);
+ GC_VERBOSE_LOG_PRINTF("Finished marking for mark phase number %lu\n",
+ (unsigned long)GC_mark_no);
GC_mark_no++;
GC_release_mark_lock();
GC_notify_all_marker();
@@ -1168,17 +1148,19 @@ STATIC void GC_do_parallel_mark(void)
/* We do not hold the GC lock, but the requestor does. */
GC_INNER void GC_help_marker(word my_mark_no)
{
- mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
unsigned my_id;
+ mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
+ /* Note: local_mark_stack is quite big (up to 128 KiB). */
if (!GC_parallel) return;
+
GC_acquire_mark_lock();
while (GC_mark_no < my_mark_no
|| (!GC_help_wanted && GC_mark_no == my_mark_no)) {
GC_wait_marker();
}
my_id = GC_helper_count;
- if (GC_mark_no != my_mark_no || my_id >= (unsigned)GC_markers) {
+ if (GC_mark_no != my_mark_no || my_id > (unsigned)GC_markers_m1) {
/* Second test is useful only if original threads can also */
/* act as helpers. Under Linux they can't. */
GC_release_mark_lock();
@@ -1226,16 +1208,12 @@ static void alloc_mark_stack(size_t n)
}
GC_mark_stack = new_stack;
GC_mark_stack_size = n;
+ /* FIXME: Do we need some way to reset GC_mark_stack_size? */
GC_mark_stack_limit = new_stack + n;
- if (GC_print_stats) {
- GC_log_printf("Grew mark stack to %lu frames\n",
- (unsigned long) GC_mark_stack_size);
- }
+ GC_COND_LOG_PRINTF("Grew mark stack to %lu frames\n",
+ (unsigned long)GC_mark_stack_size);
} else {
- if (GC_print_stats) {
- GC_log_printf("Failed to grow mark stack to %lu frames\n",
- (unsigned long) n);
- }
+ WARN("Failed to grow mark stack to %" WARN_PRIdPTR " frames\n", n);
}
} else {
if (new_stack == 0) {
@@ -1261,15 +1239,16 @@ GC_INNER void GC_mark_init(void)
* Should only be used if there is no possibility of mark stack
* overflow.
*/
-GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
+GC_API void GC_CALL GC_push_all(char *bottom, char *top)
{
register word length;
- bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
- top = (ptr_t)(((word) top) & ~(ALIGNMENT-1));
- if (top == 0 || bottom == top) return;
+ bottom = (char *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
+ top = (char *)(((word) top) & ~(ALIGNMENT-1));
+ if ((word)bottom >= (word)top) return;
+
GC_mark_stack_top++;
- if (GC_mark_stack_top >= GC_mark_stack_limit) {
+ if ((word)GC_mark_stack_top >= (word)GC_mark_stack_limit) {
ABORT("Unexpected mark stack overflow");
}
length = top - bottom;
@@ -1278,7 +1257,7 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
length &= ~GC_DS_TAGS;
# endif
GC_mark_stack_top -> mse_start = bottom;
- GC_mark_stack_top -> mse_descr = length;
+ GC_mark_stack_top -> mse_descr.w = length;
}
#ifndef GC_DISABLE_INCREMENTAL
@@ -1298,10 +1277,10 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
top = (ptr_t)(((word) top) & ~(ALIGNMENT-1));
- if (top == 0 || bottom == top) return;
+ if ((word)bottom >= (word)top) return;
h = HBLKPTR(bottom + HBLKSIZE);
- if (top <= (ptr_t) h) {
+ if ((word)top <= (word)h) {
if ((*dirty_fn)(h-1)) {
GC_push_all(bottom, top);
}
@@ -1311,7 +1290,7 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
GC_push_all(bottom, (ptr_t)h);
}
- while ((ptr_t)(h+1) <= top) {
+ while ((word)(h+1) <= (word)top) {
if ((*dirty_fn)(h)) {
if ((word)(GC_mark_stack_top - GC_mark_stack)
> 3 * GC_mark_stack_size / 4) {
@@ -1328,20 +1307,20 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
if ((ptr_t)h != top && (*dirty_fn)(h)) {
GC_push_all((ptr_t)h, top);
}
- if (GC_mark_stack_top >= GC_mark_stack_limit) {
+ if ((word)GC_mark_stack_top >= (word)GC_mark_stack_limit) {
ABORT("Unexpected mark stack overflow");
}
}
- GC_INNER void GC_push_conditional(ptr_t bottom, ptr_t top, GC_bool all)
+ GC_API void GC_CALL GC_push_conditional(char *bottom, char *top, int all)
{
if (!all) {
- GC_push_selected(bottom, top, GC_page_was_dirty);
+ GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_dirty);
} else {
# ifdef PROC_VDB
if (GC_dirty_maintained) {
/* Pages that were never dirtied cannot contain pointers. */
- GC_push_selected(bottom, top, GC_page_was_ever_dirty);
+ GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_ever_dirty);
} else
# endif
/* else */ {
@@ -1349,7 +1328,13 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top)
}
}
}
-#endif /* !GC_DISABLE_INCREMENTAL */
+#else
+ GC_API void GC_CALL GC_push_conditional(char *bottom, char *top,
+ int all GC_ATTR_UNUSED)
+ {
+ GC_push_all(bottom, top);
+ }
+#endif /* GC_DISABLE_INCREMENTAL */
#if defined(MSWIN32) || defined(MSWINCE)
void __cdecl GC_push_one(word p)
@@ -1369,21 +1354,12 @@ GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj,
PREFETCH(obj);
GET_HDR(obj, hhdr);
- if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE)) {
- if (GC_all_interior_pointers) {
- hhdr = GC_find_header(GC_base(obj));
- if (hhdr == 0) {
- GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src);
- return mark_stack_ptr;
- }
- } else {
- GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src);
- return mark_stack_ptr;
- }
- }
- if (EXPECT(HBLK_IS_FREE(hhdr), FALSE)) {
- GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src);
- return mark_stack_ptr;
+ if ((EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE)
+ && (!GC_all_interior_pointers
+ || NULL == (hhdr = GC_find_header(GC_base(obj)))))
+ || EXPECT(HBLK_IS_FREE(hhdr), FALSE)) {
+ GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src);
+ return mark_stack_ptr;
}
PUSH_CONTENTS_HDR(obj, mark_stack_ptr /* modified */, mark_stack_limit,
@@ -1469,24 +1445,32 @@ void GC_add_trace_entry(char *kind, word arg1, word arg2)
if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0;
}
-void GC_print_trace(word gc_no, GC_bool lock)
+void GC_print_trace_inner(word gc_no)
{
int i;
struct trace_entry *p;
- DCL_LOCK_STATE;
- if (lock) LOCK();
for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) {
if (i < 0) i = TRACE_ENTRIES-1;
p = GC_trace_buf + i;
- if (p -> gc_no < gc_no || p -> kind == 0) return;
- printf("Trace:%s (gc:%u,bytes:%lu) 0x%X, 0x%X\n",
- p -> kind, (unsigned)p -> gc_no,
- (unsigned long)p -> bytes_allocd,
- (p -> arg1) ^ 0x80000000, (p -> arg2) ^ 0x80000000);
+ if (p -> gc_no < gc_no || p -> kind == 0) {
+ return;
+ }
+ GC_printf("Trace:%s (gc:%u, bytes:%lu) 0x%lX, 0x%lX\n",
+ p -> kind, (unsigned)p -> gc_no,
+ (unsigned long)p -> bytes_allocd,
+ (long)p->arg1 ^ 0x80000000L, (long)p->arg2 ^ 0x80000000L);
}
- printf("Trace incomplete\n");
- if (lock) UNLOCK();
+ GC_printf("Trace incomplete\n");
+}
+
+void GC_print_trace(word gc_no)
+{
+ DCL_LOCK_STATE;
+
+ LOCK();
+ GC_print_trace_inner(gc_no);
+ UNLOCK();
}
# endif /* TRACE_BUF */
@@ -1512,7 +1496,8 @@ GC_INNER void GC_push_all_eager(ptr_t bottom, ptr_t top)
/* check all pointers in range and push if they appear */
/* to be valid. */
lim = t - 1 /* longword */;
- for (p = b; p <= lim; p = (word *)(((ptr_t)p) + ALIGNMENT)) {
+ for (p = b; (word)p <= (word)lim;
+ p = (word *)(((ptr_t)p) + ALIGNMENT)) {
q = *p;
GC_PUSH_ONE_STACK(q, p);
}
@@ -1538,26 +1523,32 @@ GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top)
# if GC_GRANULE_WORDS == 1
# define USE_PUSH_MARKED_ACCELERATORS
# define PUSH_GRANULE(q) \
- { word qcontents = (q)[0]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)); }
+ do { \
+ word qcontents = (q)[0]; \
+ GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \
+ } while (0)
# elif GC_GRANULE_WORDS == 2
# define USE_PUSH_MARKED_ACCELERATORS
# define PUSH_GRANULE(q) \
- { word qcontents = (q)[0]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)); \
+ do { \
+ word qcontents = (q)[0]; \
+ GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \
qcontents = (q)[1]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)+1); }
+ GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \
+ } while (0)
# elif GC_GRANULE_WORDS == 4
# define USE_PUSH_MARKED_ACCELERATORS
# define PUSH_GRANULE(q) \
- { word qcontents = (q)[0]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)); \
+ do { \
+ word qcontents = (q)[0]; \
+ GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \
qcontents = (q)[1]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)+1); \
+ GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \
qcontents = (q)[2]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)+2); \
+ GC_PUSH_ONE_HEAP(qcontents, (q)+2, GC_mark_stack_top); \
qcontents = (q)[3]; \
- GC_PUSH_ONE_HEAP(qcontents, (q)+3); }
+ GC_PUSH_ONE_HEAP(qcontents, (q)+3, GC_mark_stack_top); \
+ } while (0)
# endif
#endif /* !USE_MARK_BYTES && MARK_BIT_PER_GRANULE */
@@ -1572,13 +1563,16 @@ STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr)
word *q;
word mark_word;
- /* Allow registers to be used for some frequently acccessed */
+ /* Allow registers to be used for some frequently accessed */
/* global variables. Otherwise aliasing issues are likely */
/* to prevent that. */
ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
ptr_t least_ha = GC_least_plausible_heap_addr;
mse * mark_stack_top = GC_mark_stack_top;
mse * mark_stack_limit = GC_mark_stack_limit;
+
+# undef GC_mark_stack_top
+# undef GC_mark_stack_limit
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
@@ -1588,7 +1582,7 @@ STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr)
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
- while (p < plim) {
+ while ((word)p < (word)plim) {
mark_word = *mark_word_addr++;
q = p;
while(mark_word != 0) {
@@ -1605,7 +1599,8 @@ STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr)
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
-
+# define GC_mark_stack_limit GC_arrays._mark_stack_limit
+# define GC_mark_stack_top GC_arrays._mark_stack_top
GC_mark_stack_top = mark_stack_top;
}
@@ -1627,6 +1622,8 @@ STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr)
mse * mark_stack_top = GC_mark_stack_top;
mse * mark_stack_limit = GC_mark_stack_limit;
+# undef GC_mark_stack_top
+# undef GC_mark_stack_limit
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
@@ -1636,7 +1633,7 @@ STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr)
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
- while (p < plim) {
+ while ((word)p < (word)plim) {
mark_word = *mark_word_addr++;
q = p;
while(mark_word != 0) {
@@ -1654,7 +1651,8 @@ STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr)
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
-
+# define GC_mark_stack_limit GC_arrays._mark_stack_limit
+# define GC_mark_stack_top GC_arrays._mark_stack_top
GC_mark_stack_top = mark_stack_top;
}
@@ -1675,6 +1673,9 @@ STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr)
ptr_t least_ha = GC_least_plausible_heap_addr;
mse * mark_stack_top = GC_mark_stack_top;
mse * mark_stack_limit = GC_mark_stack_limit;
+
+# undef GC_mark_stack_top
+# undef GC_mark_stack_limit
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
@@ -1684,7 +1685,7 @@ STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr)
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
- while (p < plim) {
+ while ((word)p < (word)plim) {
mark_word = *mark_word_addr++;
q = p;
while(mark_word != 0) {
@@ -1703,6 +1704,8 @@ STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr)
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
+# define GC_mark_stack_limit GC_arrays._mark_stack_limit
+# define GC_mark_stack_top GC_arrays._mark_stack_top
GC_mark_stack_top = mark_stack_top;
}
@@ -1752,7 +1755,7 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr)
# endif
default:
GC_mark_stack_top_reg = GC_mark_stack_top;
- for (p = h -> hb_body, bit_no = 0; p <= lim;
+ for (p = h -> hb_body, bit_no = 0; (word)p <= (word)lim;
p += sz, bit_no += MARK_BIT_OFFSET(sz)) {
if (mark_bit_from_hdr(hhdr, bit_no)) {
/* Mark from fields inside the object */
@@ -1765,7 +1768,7 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr)
#ifdef ENABLE_DISCLAIM
/* Unconditionally mark from all objects which have not been reclaimed. */
-/* This is useful in order to retain pointes which are reachable from */
+/* This is useful in order to retain pointers which are reachable from */
/* the disclaim notifiers. */
/* */
/* To determine whether an object has been reclaimed, we require that */
@@ -1793,7 +1796,7 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr)
lim = (h + 1)->hb_body - sz;
GC_mark_stack_top_reg = GC_mark_stack_top;
- for (p = h -> hb_body; p <= lim; p += sz)
+ for (p = h -> hb_body; (word)p <= (word)lim; p += sz)
if ((*(GC_word *)p & 0x3) != 0)
PUSH_OBJ(p, hhdr, GC_mark_stack_top_reg, mark_stack_limit);
GC_mark_stack_top = GC_mark_stack_top_reg;
@@ -1810,7 +1813,7 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr)
return(GC_page_was_dirty(h));
} else {
ptr_t p = (ptr_t)h;
- while (p < (ptr_t)h + sz) {
+ while ((word)p < (word)h + sz) {
if (GC_page_was_dirty((struct hblk *)p)) return(TRUE);
p += HBLKSIZE;
}
@@ -1867,7 +1870,7 @@ STATIC struct hblk * GC_push_next_marked(struct hblk *h)
}
#endif /* !GC_DISABLE_INCREMENTAL */
-/* Similar to above, but for uncollectable pages. Needed since we */
+/* Similar to above, but for uncollectible pages. Needed since we */
/* do not clear marks for such pages, even for full collections. */
STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h)
{
diff --git a/mark_rts.c b/mark_rts.c
index b7b81320..a8dc04a2 100644
--- a/mark_rts.c
+++ b/mark_rts.c
@@ -39,25 +39,37 @@ int GC_no_dls = 0; /* Register dynamic library data segments. */
static int n_root_sets = 0;
/* GC_static_roots[0..n_root_sets) contains the valid root sets. */
+#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS)
+ /* Should return the same value as GC_root_size. */
+ GC_INNER word GC_compute_root_size(void)
+ {
+ int i;
+ word size = 0;
+
+ for (i = 0; i < n_root_sets; i++) {
+ size += GC_static_roots[i].r_end - GC_static_roots[i].r_start;
+ }
+ return size;
+ }
+#endif /* !NO_DEBUGGING || GC_ASSERTIONS */
+
#if !defined(NO_DEBUGGING)
/* For debugging: */
void GC_print_static_roots(void)
{
int i;
- size_t total = 0;
+ word size;
for (i = 0; i < n_root_sets; i++) {
GC_printf("From %p to %p%s\n",
- GC_static_roots[i].r_start,
- GC_static_roots[i].r_end,
+ GC_static_roots[i].r_start, GC_static_roots[i].r_end,
GC_static_roots[i].r_tmp ? " (temporary)" : "");
- total += GC_static_roots[i].r_end - GC_static_roots[i].r_start;
- }
- GC_printf("Total size: %ld\n", (unsigned long) total);
- if (GC_root_size != total) {
- GC_err_printf("GC_root_size incorrect: %ld!!\n",
- (long) GC_root_size);
}
+ GC_printf("GC_root_size: %lu\n", (unsigned long)GC_root_size);
+
+ if ((size = GC_compute_root_size()) != GC_root_size)
+ GC_err_printf("GC_root_size incorrect!! Should be: %lu\n",
+ (unsigned long)size);
}
#endif /* !NO_DEBUGGING */
@@ -70,13 +82,14 @@ static int n_root_sets = 0;
int i;
if (last_root_set < n_root_sets
- && p >= GC_static_roots[last_root_set].r_start
- && p < GC_static_roots[last_root_set].r_end) return(TRUE);
+ && (word)p >= (word)GC_static_roots[last_root_set].r_start
+ && (word)p < (word)GC_static_roots[last_root_set].r_end)
+ return(TRUE);
for (i = 0; i < n_root_sets; i++) {
- if (p >= GC_static_roots[i].r_start
- && p < GC_static_roots[i].r_end) {
- last_root_set = i;
- return(TRUE);
+ if ((word)p >= (word)GC_static_roots[i].r_start
+ && (word)p < (word)GC_static_roots[i].r_end) {
+ last_root_set = i;
+ return(TRUE);
}
}
return(FALSE);
@@ -156,11 +169,12 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp)
{
struct roots * old;
- /* Adjust and check range boundaries for safety */
- GC_ASSERT((word)b % sizeof(word) == 0);
+ GC_ASSERT((word)b <= (word)e);
+ b = (ptr_t)(((word)b + (sizeof(word) - 1)) & ~(sizeof(word) - 1));
+ /* round b up to word boundary */
e = (ptr_t)((word)e & ~(sizeof(word) - 1));
- GC_ASSERT(b <= e);
- if (b == e) return; /* nothing to do? */
+ /* round e down to word boundary */
+ if ((word)b >= (word)e) return; /* nothing to do */
# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
/* Spend the time to ensure that there are no overlapping */
@@ -174,14 +188,15 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp)
old = 0; /* initialized to prevent warning. */
for (i = 0; i < n_root_sets; i++) {
old = GC_static_roots + i;
- if (b <= old -> r_end && e >= old -> r_start) {
- if (b < old -> r_start) {
+ if ((word)b <= (word)old->r_end
+ && (word)e >= (word)old->r_start) {
+ if ((word)b < (word)old->r_start) {
+ GC_root_size += old->r_start - b;
old -> r_start = b;
- GC_root_size += (old -> r_start - b);
}
- if (e > old -> r_end) {
+ if ((word)e > (word)old->r_end) {
+ GC_root_size += e - old->r_end;
old -> r_end = e;
- GC_root_size += (e - old -> r_end);
}
old -> r_tmp &= tmp;
break;
@@ -195,14 +210,15 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp)
other = GC_static_roots + i;
b = other -> r_start;
e = other -> r_end;
- if (b <= old -> r_end && e >= old -> r_start) {
- if (b < old -> r_start) {
+ if ((word)b <= (word)old->r_end
+ && (word)e >= (word)old->r_start) {
+ if ((word)b < (word)old->r_start) {
+ GC_root_size += old->r_start - b;
old -> r_start = b;
- GC_root_size += (old -> r_start - b);
}
- if (e > old -> r_end) {
+ if ((word)e > (word)old->r_end) {
+ GC_root_size += e - old->r_end;
old -> r_end = e;
- GC_root_size += (e - old -> r_end);
}
old -> r_tmp &= other -> r_tmp;
/* Delete this entry. */
@@ -218,7 +234,7 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp)
# else
old = (struct roots *)GC_roots_present(b);
if (old != 0) {
- if (e <= old -> r_end) /* already there */ return;
+ if ((word)e <= (word)old->r_end) /* already there */ return;
/* else extend */
GC_root_size += e - old -> r_end;
old -> r_end = e;
@@ -228,6 +244,11 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp)
if (n_root_sets == MAX_ROOT_SETS) {
ABORT("Too many root sets");
}
+
+# ifdef DEBUG_ADD_DEL_ROOTS
+ GC_log_printf("Adding data root section %d: %p .. %p%s\n",
+ n_root_sets, b, e, tmp ? " (temporary)" : "");
+# endif
GC_static_roots[n_root_sets].r_start = (ptr_t)b;
GC_static_roots[n_root_sets].r_end = (ptr_t)e;
GC_static_roots[n_root_sets].r_tmp = tmp;
@@ -253,12 +274,20 @@ GC_API void GC_CALL GC_clear_roots(void)
# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32)
BZERO(GC_root_index, RT_SIZE * sizeof(void *));
# endif
+# ifdef DEBUG_ADD_DEL_ROOTS
+ GC_log_printf("Clear all data root sections\n");
+# endif
UNLOCK();
}
/* Internal use only; lock held. */
STATIC void GC_remove_root_at_pos(int i)
{
+# ifdef DEBUG_ADD_DEL_ROOTS
+ GC_log_printf("Remove data root section at %d: %p .. %p%s\n",
+ i, GC_static_roots[i].r_start, GC_static_roots[i].r_end,
+ GC_static_roots[i].r_tmp ? " (temporary)" : "");
+# endif
GC_root_size -= (GC_static_roots[i].r_end - GC_static_roots[i].r_start);
GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start;
GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end;
@@ -318,8 +347,8 @@ STATIC void GC_remove_tmp_roots(void)
{
int i;
for (i = 0; i < n_root_sets; ) {
- if (GC_static_roots[i].r_start >= b
- && GC_static_roots[i].r_end <= e) {
+ if ((word)GC_static_roots[i].r_start >= (word)b
+ && (word)GC_static_roots[i].r_end <= (word)e) {
GC_remove_root_at_pos(i);
} else {
i++;
@@ -340,12 +369,12 @@ STATIC void GC_remove_tmp_roots(void)
register int i;
if (last_root_set < n_root_sets
- && p >= GC_static_roots[last_root_set].r_start
- && p < GC_static_roots[last_root_set].r_end)
+ && (word)p >= (word)GC_static_roots[last_root_set].r_start
+ && (word)p < (word)GC_static_roots[last_root_set].r_end)
return GC_static_roots[last_root_set].r_tmp;
for (i = 0; i < n_root_sets; i++) {
- if (p >= GC_static_roots[i].r_start
- && p < GC_static_roots[i].r_end) {
+ if ((word)p >= (word)GC_static_roots[i].r_start
+ && (word)p < (word)GC_static_roots[i].r_end) {
last_root_set = i;
return GC_static_roots[i].r_tmp;
}
@@ -412,7 +441,7 @@ GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish)
size_t next_index, i;
GC_ASSERT((word)start % sizeof(word) == 0);
- GC_ASSERT(start < finish);
+ GC_ASSERT((word)start < (word)finish);
if (0 == GC_excl_table_entries) {
next = 0;
@@ -446,11 +475,13 @@ GC_API void GC_CALL GC_exclude_static_roots(void *b, void *e)
{
DCL_LOCK_STATE;
- /* Adjust the upper boundary for safety (round down) */
- e = (void *)((word)e & ~(sizeof(word) - 1));
-
if (b == e) return; /* nothing to exclude? */
+ /* Round boundaries (in direction reverse to that of GC_add_roots). */
+ b = (void *)((word)b & ~(sizeof(word) - 1));
+ e = (void *)(((word)e + (sizeof(word) - 1)) & ~(sizeof(word) - 1));
+ if (0 == e) e = (void *)(word)(~(sizeof(word) - 1)); /* handle overflow */
+
LOCK();
GC_exclude_static_roots_inner(b, e);
UNLOCK();
@@ -463,13 +494,14 @@ STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top,
struct exclusion * next;
ptr_t excl_start;
- while (bottom < top) {
+ while ((word)bottom < (word)top) {
next = GC_next_exclusion(bottom);
- if (0 == next || (excl_start = next -> e_start) >= top) {
- GC_push_conditional(bottom, top, all);
+ if (0 == next || (word)(excl_start = next -> e_start) >= (word)top) {
+ GC_PUSH_CONDITIONAL(bottom, top, all);
return;
}
- if (excl_start > bottom) GC_push_conditional(bottom, excl_start, all);
+ if ((word)excl_start > (word)bottom)
+ GC_PUSH_CONDITIONAL(bottom, excl_start, all);
bottom = next -> e_end;
}
}
@@ -481,7 +513,7 @@ STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top,
{
while (traced_stack_sect != NULL) {
ptr_t frame_bs_lo = traced_stack_sect -> backing_store_end;
- GC_ASSERT(frame_bs_lo <= bs_hi);
+ GC_ASSERT((word)frame_bs_lo <= (word)bs_hi);
if (eager) {
GC_push_all_eager(frame_bs_lo, bs_hi);
} else {
@@ -490,7 +522,7 @@ STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top,
bs_hi = traced_stack_sect -> saved_backing_store_ptr;
traced_stack_sect = traced_stack_sect -> prev;
}
- GC_ASSERT(bs_lo <= bs_hi);
+ GC_ASSERT((word)bs_lo <= (word)bs_hi);
if (eager) {
GC_push_all_eager(bs_lo, bs_hi);
} else {
@@ -505,7 +537,7 @@ GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi,
struct GC_traced_stack_sect_s *traced_stack_sect)
{
while (traced_stack_sect != NULL) {
- GC_ASSERT(lo HOTTER_THAN (ptr_t)traced_stack_sect);
+ GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect);
# ifdef STACK_GROWS_UP
GC_push_all_stack((ptr_t)traced_stack_sect, lo);
# else /* STACK_GROWS_DOWN */
@@ -515,7 +547,7 @@ GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi,
GC_ASSERT(lo != NULL);
traced_stack_sect = traced_stack_sect -> prev;
}
- GC_ASSERT(!(hi HOTTER_THAN lo));
+ GC_ASSERT(!((word)hi HOTTER_THAN (word)lo));
# ifdef STACK_GROWS_UP
/* We got them backwards! */
GC_push_all_stack(hi, lo);
@@ -557,7 +589,8 @@ STATIC void GC_push_all_stack_partially_eager(ptr_t bottom, ptr_t top,
GC_push_all_stack(bottom, top);
return;
}
- GC_ASSERT(bottom <= cold_gc_frame && cold_gc_frame <= top);
+ GC_ASSERT((word)bottom <= (word)cold_gc_frame
+ && (word)cold_gc_frame <= (word)top);
# ifdef STACK_GROWS_DOWN
GC_push_all(cold_gc_frame - sizeof(ptr_t), top);
GC_push_all_eager(bottom, cold_gc_frame);
@@ -578,10 +611,10 @@ STATIC void GC_push_all_stack_part_eager_sections(ptr_t lo, ptr_t hi,
ptr_t cold_gc_frame, struct GC_traced_stack_sect_s *traced_stack_sect)
{
GC_ASSERT(traced_stack_sect == NULL || cold_gc_frame == NULL ||
- cold_gc_frame HOTTER_THAN (ptr_t)traced_stack_sect);
+ (word)cold_gc_frame HOTTER_THAN (word)traced_stack_sect);
while (traced_stack_sect != NULL) {
- GC_ASSERT(lo HOTTER_THAN (ptr_t)traced_stack_sect);
+ GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect);
# ifdef STACK_GROWS_UP
GC_push_all_stack_partially_eager((ptr_t)traced_stack_sect, lo,
cold_gc_frame);
@@ -595,7 +628,7 @@ STATIC void GC_push_all_stack_part_eager_sections(ptr_t lo, ptr_t hi,
cold_gc_frame = NULL; /* Use at most once. */
}
- GC_ASSERT(!(hi HOTTER_THAN lo));
+ GC_ASSERT(!((word)hi HOTTER_THAN (word)lo));
# ifdef STACK_GROWS_UP
/* We got them backwards! */
GC_push_all_stack_partially_eager(hi, lo, cold_gc_frame);
@@ -646,12 +679,13 @@ STATIC void GC_push_current_stack(ptr_t cold_gc_frame,
{
ptr_t bsp = GC_save_regs_ret_val;
ptr_t cold_gc_bs_pointer = bsp - 2048;
- if (GC_all_interior_pointers &&
- cold_gc_bs_pointer > BACKING_STORE_BASE) {
+ if (GC_all_interior_pointers
+ && (word)cold_gc_bs_pointer > (word)BACKING_STORE_BASE) {
/* Adjust cold_gc_bs_pointer if below our innermost */
/* "traced stack section" in backing store. */
- if (GC_traced_stack_sect != NULL && cold_gc_bs_pointer <
- GC_traced_stack_sect->backing_store_end)
+ if (GC_traced_stack_sect != NULL
+ && (word)cold_gc_bs_pointer
+ < (word)GC_traced_stack_sect->backing_store_end)
cold_gc_bs_pointer =
GC_traced_stack_sect->backing_store_end;
GC_push_all_register_sections(BACKING_STORE_BASE,
@@ -681,7 +715,9 @@ GC_INNER void (*GC_push_typed_structures)(void) = 0;
*/
STATIC void GC_push_gc_structures(void)
{
- GC_push_finalizer_structures();
+# ifndef GC_NO_FINALIZATION
+ GC_push_finalizer_structures();
+# endif
# if defined(THREADS)
GC_push_thread_structures();
# endif
@@ -706,8 +742,9 @@ STATIC void GC_push_regs_and_stack(ptr_t cold_gc_frame)
}
/*
- * Call the mark routines (GC_tl_push for a single pointer, GC_push_conditional
- * on groups of pointers) on every top level accessible pointer.
+ * Call the mark routines (GC_tl_push for a single pointer,
+ * GC_push_conditional on groups of pointers) on every top level
+ * accessible pointer.
* If all is FALSE, arrange to push only possibly altered values.
* Cold_gc_frame is an address inside a GC frame that
* remains valid until all marking is complete.
diff --git a/misc.c b/misc.c
index 56153dab..8e2da686 100644
--- a/misc.c
+++ b/misc.c
@@ -26,7 +26,9 @@
#ifdef GC_SOLARIS_THREADS
# include <sys/syscall.h>
#endif
-#if defined(MSWIN32) || defined(MSWINCE)
+
+#if defined(MSWIN32) || defined(MSWINCE) \
+ || (defined(CYGWIN32) && defined(GC_READ_ENV_FILE))
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN 1
# endif
@@ -34,7 +36,7 @@
# include <windows.h>
#endif
-#if defined(UNIX_LIKE) || defined(CYGWIN32)
+#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN)
# include <fcntl.h>
# include <sys/types.h>
# include <sys/stat.h>
@@ -83,14 +85,14 @@ ptr_t GC_stackbottom = 0;
ptr_t GC_register_stackbottom = 0;
#endif
-GC_bool GC_dont_gc = 0;
+int GC_dont_gc = FALSE;
-GC_bool GC_dont_precollect = 0;
+int GC_dont_precollect = FALSE;
GC_bool GC_quiet = 0; /* used also in pcr_interface.c */
#ifndef SMALL_CONFIG
- GC_bool GC_print_stats = 0;
+ int GC_print_stats = 0;
#endif
#ifdef GC_PRINT_BACK_HEIGHT
@@ -129,6 +131,22 @@ GC_bool GC_quiet = 0; /* used also in pcr_interface.c */
int GC_all_interior_pointers = 0;
#endif
+#ifdef FINALIZE_ON_DEMAND
+ int GC_finalize_on_demand = 1;
+#else
+ int GC_finalize_on_demand = 0;
+#endif
+
+#ifdef JAVA_FINALIZATION
+ int GC_java_finalization = 1;
+#else
+ int GC_java_finalization = 0;
+#endif
+
+/* All accesses to it should be synchronized to avoid data races. */
+GC_finalizer_notifier_proc GC_finalizer_notifier =
+ (GC_finalizer_notifier_proc)0;
+
#ifdef GC_FORCE_UNMAP_ON_GCOLLECT
/* Has no effect unless USE_MUNMAP. */
/* Has no effect on implicitly-initiated garbage collections. */
@@ -152,7 +170,56 @@ STATIC void * GC_CALLBACK GC_default_oom_fn(
/* All accesses to it should be synchronized to avoid data races. */
GC_oom_func GC_oom_fn = GC_default_oom_fn;
-/* Set things up so that GC_size_map[i] >= granules(i), */
+#ifdef CAN_HANDLE_FORK
+# ifdef HANDLE_FORK
+ GC_INNER int GC_handle_fork = 1;
+ /* The value is examined by GC_thr_init. */
+# else
+ GC_INNER int GC_handle_fork = FALSE;
+# endif
+
+#elif !defined(HAVE_NO_FORK)
+
+ /* Same as above but with GC_CALL calling conventions. */
+ GC_API void GC_CALL GC_atfork_prepare(void)
+ {
+# ifdef THREADS
+ ABORT("fork() handling unsupported");
+# endif
+ }
+
+ GC_API void GC_CALL GC_atfork_parent(void)
+ {
+ /* empty */
+ }
+
+ GC_API void GC_CALL GC_atfork_child(void)
+ {
+ /* empty */
+ }
+#endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */
+
+/* Overrides the default automatic handle-fork mode. Has effect only */
+/* if called before GC_INIT. */
+GC_API void GC_CALL GC_set_handle_fork(int value GC_ATTR_UNUSED)
+{
+# ifdef CAN_HANDLE_FORK
+ if (!GC_is_initialized)
+ GC_handle_fork = value >= -1 ? value : 1;
+ /* Map all negative values except for -1 to a positive one. */
+# elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB))
+ if (!GC_is_initialized && value) {
+# ifndef SMALL_CONFIG
+ GC_init(); /* just to initialize GC_stderr */
+# endif
+ ABORT("fork() handling unsupported");
+ }
+# else
+ /* No at-fork handler is needed in the single-threaded mode. */
+# endif
+}
+
+/* Set things up so that GC_size_map[i] >= granules(i), */
/* but not too much bigger */
/* and so that size_map contains relatively few distinct entries */
/* This was originally stolen from Russ Atkinson's Cedar */
@@ -174,9 +241,9 @@ STATIC void GC_init_size_map(void)
/* We leave the rest of the array to be filled in on demand. */
}
-/* Fill in additional entries in GC_size_map, including the ith one */
+/* Fill in additional entries in GC_size_map, including the ith one */
/* We assume the ith entry is currently 0. */
-/* Note that a filled in section of the array ending at n always */
+/* Note that a filled in section of the array ending at n always */
/* has length at least n/4. */
GC_INNER void GC_extend_size_map(size_t i)
{
@@ -211,9 +278,10 @@ GC_INNER void GC_extend_size_map(size_t i)
granule_sz = MAXOBJGRANULES;
}
/* If we can fit the same number of larger objects in a block, */
- /* do so. */
+ /* do so. */
{
size_t number_of_objs = HBLK_GRANULES/granule_sz;
+ GC_ASSERT(number_of_objs != 0);
granule_sz = HBLK_GRANULES/number_of_objs;
granule_sz &= ~1;
}
@@ -255,14 +323,14 @@ GC_INNER void GC_extend_size_map(size_t i)
void *GC_clear_stack_inner(void *, ptr_t);
#else
/* Clear the stack up to about limit. Return arg. This function is */
- /* not static because it could also be errorneously defined in .S */
+ /* not static because it could also be erroneously defined in .S */
/* file, so this error would be caught by the linker. */
void * GC_clear_stack_inner(void *arg, ptr_t limit)
{
- word dummy[CLEAR_SIZE];
+ volatile word dummy[CLEAR_SIZE];
- BZERO(dummy, CLEAR_SIZE*sizeof(word));
- if ((ptr_t)(dummy) COOLER_THAN limit) {
+ BZERO((/* no volatile */ void *)dummy, sizeof(dummy));
+ if ((word)GC_approx_sp() COOLER_THAN (word)limit) {
(void) GC_clear_stack_inner(arg, limit);
}
/* Make sure the recursive call is not a tail call, and the bzero */
@@ -275,11 +343,11 @@ GC_INNER void GC_extend_size_map(size_t i)
/* Clear some of the inaccessible part of the stack. Returns its */
/* argument, so it can be used in a tail call position, hence clearing */
/* another frame. */
-GC_INNER void * GC_clear_stack(void *arg)
+GC_API void * GC_CALL GC_clear_stack(void *arg)
{
ptr_t sp = GC_approx_sp(); /* Hotter than actual sp */
# ifdef THREADS
- word dummy[SMALL_CLEAR_SIZE];
+ word volatile dummy[SMALL_CLEAR_SIZE];
static unsigned random_no = 0;
/* Should be more random than it is ... */
/* Used to occasionally clear a bigger */
@@ -311,7 +379,7 @@ GC_INNER void * GC_clear_stack(void *arg)
/* implementations of GC_clear_stack_inner. */
return GC_clear_stack_inner(arg, limit);
} else {
- BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word));
+ BZERO((void *)dummy, SMALL_CLEAR_SIZE*sizeof(word));
return arg;
}
# else
@@ -324,13 +392,13 @@ GC_INNER void * GC_clear_stack(void *arg)
}
/* Adjust GC_high_water */
MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP);
- if (sp HOTTER_THAN GC_high_water) {
+ if ((word)sp HOTTER_THAN (word)GC_high_water) {
GC_high_water = sp;
}
MAKE_HOTTER(GC_high_water, GC_SLOP);
limit = GC_min_sp;
MAKE_HOTTER(limit, SLOP);
- if (sp COOLER_THAN limit) {
+ if ((word)sp COOLER_THAN (word)limit) {
limit = (ptr_t)((word)limit & ~0xf);
/* Make it sufficiently aligned for assembly */
/* implementations of GC_clear_stack_inner. */
@@ -340,7 +408,8 @@ GC_INNER void * GC_clear_stack(void *arg)
/* Restart clearing process, but limit how much clearing we do. */
GC_min_sp = sp;
MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4);
- if (GC_min_sp HOTTER_THAN GC_high_water) GC_min_sp = GC_high_water;
+ if ((word)GC_min_sp HOTTER_THAN (word)GC_high_water)
+ GC_min_sp = GC_high_water;
GC_bytes_allocd_at_reset = GC_bytes_allocd;
}
return(arg);
@@ -381,10 +450,10 @@ GC_API void * GC_CALL GC_base(void * p)
r -= obj_displ;
limit = r + sz;
- if (limit > (ptr_t)(h + 1) && sz <= HBLKSIZE) {
+ if ((word)limit > (word)(h + 1) && sz <= HBLKSIZE) {
return(0);
}
- if ((ptr_t)p >= limit) return(0);
+ if ((word)p >= (word)limit) return(0);
}
return((void *)r);
}
@@ -441,6 +510,8 @@ GC_API size_t GC_CALL GC_get_total_bytes(void)
return (size_t)(GC_bytes_allocd + GC_bytes_allocd_before_gc);
}
+#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+
/* Return the heap usage information. This is a thread-safe (atomic) */
/* alternative for the five above getters. NULL pointer is allowed for */
/* any argument. Returned (filled in) values are of word type. */
@@ -464,24 +535,104 @@ GC_API void GC_CALL GC_get_heap_usage_safe(GC_word *pheap_size,
UNLOCK();
}
+ GC_INNER word GC_reclaimed_bytes_before_gc = 0;
-#ifdef THREADS
- GC_API int GC_CALL GC_get_suspend_signal(void)
+ /* Fill in GC statistics provided the destination is of enough size. */
+ static void fill_prof_stats(struct GC_prof_stats_s *pstats)
{
-# ifdef SIG_SUSPEND
- return SIG_SUSPEND;
+ pstats->heapsize_full = GC_heapsize;
+ pstats->free_bytes_full = GC_large_free_bytes;
+ pstats->unmapped_bytes = GC_unmapped_bytes;
+ pstats->bytes_allocd_since_gc = GC_bytes_allocd;
+ pstats->allocd_bytes_before_gc = GC_bytes_allocd_before_gc;
+ pstats->non_gc_bytes = GC_non_gc_bytes;
+ pstats->gc_no = GC_gc_no; /* could be -1 */
+# ifdef PARALLEL_MARK
+ pstats->markers_m1 = (word)GC_markers_m1;
# else
- return -1;
+ pstats->markers_m1 = 0; /* one marker */
# endif
+ pstats->bytes_reclaimed_since_gc = GC_bytes_found > 0 ?
+ (word)GC_bytes_found : 0;
+ pstats->reclaimed_bytes_before_gc = GC_reclaimed_bytes_before_gc;
}
-# if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS)
- GC_API int GC_CALL GC_get_thr_restart_signal(void)
+# include <string.h> /* for memset() */
+
+ GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *pstats,
+ size_t stats_sz)
+ {
+ struct GC_prof_stats_s stats;
+ DCL_LOCK_STATE;
+
+ LOCK();
+ fill_prof_stats(stats_sz >= sizeof(stats) ? pstats : &stats);
+ UNLOCK();
+
+ if (stats_sz == sizeof(stats)) {
+ return sizeof(stats);
+ } else if (stats_sz > sizeof(stats)) {
+ /* Fill in the remaining part with -1. */
+ memset((char *)pstats + sizeof(stats), 0xff, stats_sz - sizeof(stats));
+ return sizeof(stats);
+ } else {
+ BCOPY(&stats, pstats, stats_sz);
+ return stats_sz;
+ }
+ }
+
+# ifdef THREADS
+ /* The _unsafe version assumes the caller holds the allocation lock. */
+ GC_API size_t GC_CALL GC_get_prof_stats_unsafe(
+ struct GC_prof_stats_s *pstats,
+ size_t stats_sz)
{
- return -1; /* GC does not use signals to restart threads. */
+ struct GC_prof_stats_s stats;
+
+ if (stats_sz >= sizeof(stats)) {
+ fill_prof_stats(pstats);
+ if (stats_sz > sizeof(stats))
+ memset((char *)pstats + sizeof(stats), 0xff,
+ stats_sz - sizeof(stats));
+ return sizeof(stats);
+ } else {
+ fill_prof_stats(&stats);
+ BCOPY(&stats, pstats, stats_sz);
+ return stats_sz;
+ }
}
-# endif
-#endif /* THREADS */
+# endif /* THREADS */
+
+#endif /* !GC_GET_HEAP_USAGE_NOT_NEEDED */
+
+#if defined(GC_DARWIN_THREADS) || defined(GC_OPENBSD_UTHREADS) \
+ || defined(GC_WIN32_THREADS) || (defined(NACL) && defined(THREADS))
+ /* GC does not use signals to suspend and restart threads. */
+ GC_API void GC_CALL GC_set_suspend_signal(int sig GC_ATTR_UNUSED)
+ {
+ /* empty */
+ }
+
+ GC_API void GC_CALL GC_set_thr_restart_signal(int sig GC_ATTR_UNUSED)
+ {
+ /* empty */
+ }
+
+ GC_API int GC_CALL GC_get_suspend_signal(void)
+ {
+ return -1;
+ }
+
+ GC_API int GC_CALL GC_get_thr_restart_signal(void)
+ {
+ return -1;
+ }
+#endif /* GC_DARWIN_THREADS || GC_WIN32_THREADS || ... */
+
+#if !defined(_MAX_PATH) && (defined(MSWIN32) || defined(MSWINCE) \
+ || defined(CYGWIN32))
+# define _MAX_PATH MAX_PATH
+#endif
#ifdef GC_READ_ENV_FILE
/* This works for Win32/WinCE for now. Really useful only for WinCE. */
@@ -513,7 +664,7 @@ GC_API void GC_CALL GC_get_heap_usage_safe(GC_word *pheap_size,
if (len > 4 && path[len - 4] == (TCHAR)'.') {
len -= 4; /* strip executable file extension */
}
- memcpy(&path[len], TEXT(".gc.env"), sizeof(TEXT(".gc.env")));
+ BCOPY(TEXT(".gc.env"), &path[len], sizeof(TEXT(".gc.env")));
hFile = CreateFile(path, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL /* lpSecurityAttributes */, OPEN_EXISTING,
@@ -589,16 +740,22 @@ GC_INNER GC_bool GC_is_initialized = FALSE;
GC_INNER CRITICAL_SECTION GC_write_cs;
#endif
-STATIC void GC_exit_check(void)
-{
- GC_gcollect();
-}
+#ifndef DONT_USE_ATEXIT
+ STATIC void GC_exit_check(void)
+ {
+ if (GC_find_leak) {
+ GC_gcollect();
+ }
+ }
+#endif
-#ifdef UNIX_LIKE
+#if defined(UNIX_LIKE) && !defined(NO_DEBUGGING)
static void looping_handler(int sig)
{
GC_err_printf("Caught signal %d: looping in handler\n", sig);
- for (;;) {}
+ for (;;) {
+ /* empty */
+ }
}
static GC_bool installed_looping_handler = FALSE;
@@ -617,10 +774,14 @@ STATIC void GC_exit_check(void)
# define maybe_install_looping_handler()
#endif
-#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE)
- STATIC int GC_stdout = 1;
- STATIC int GC_stderr = 2;
- STATIC int GC_log = 2; /* stderr */
+#define GC_DEFAULT_STDOUT_FD 1
+#define GC_DEFAULT_STDERR_FD 2
+
+#if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \
+ && !defined(MSWIN32) && !defined(MSWINCE)
+ STATIC int GC_stdout = GC_DEFAULT_STDOUT_FD;
+ STATIC int GC_stderr = GC_DEFAULT_STDERR_FD;
+ STATIC int GC_log = GC_DEFAULT_STDERR_FD;
#endif
STATIC word GC_parse_mem_size_arg(const char *str)
@@ -657,12 +818,11 @@ STATIC word GC_parse_mem_size_arg(const char *str)
return result;
}
+#define GC_LOG_STD_NAME "gc.log"
+
GC_API void GC_CALL GC_init(void)
{
/* LOCK(); -- no longer does anything this early. */
-# if !defined(THREADS) && defined(GC_ASSERTIONS)
- word dummy;
-# endif
word initial_heap_sz;
IF_CANCEL(int cancel_state;)
@@ -737,12 +897,13 @@ GC_API void GC_CALL GC_init(void)
GC_print_stats = 1;
}
# endif
-# if defined(UNIX_LIKE) || defined(CYGWIN32)
+# if (defined(UNIX_LIKE) && !defined(GC_ANDROID_LOG)) \
+ || defined(CYGWIN32) || defined(SYMBIAN)
{
char * file_name = GETENV("GC_LOG_FILE");
# ifdef GC_LOG_TO_FILE_ALWAYS
if (NULL == file_name)
- file_name = "gc.log";
+ file_name = GC_LOG_STD_NAME;
# else
if (0 != file_name)
# endif
@@ -820,6 +981,16 @@ GC_API void GC_CALL GC_init(void)
# endif
}
}
+# ifdef GC_COLLECT_AT_MALLOC
+ {
+ char * string = GETENV("GC_COLLECT_AT_MALLOC");
+ if (0 != string) {
+ size_t min_lb = (size_t)STRTOULL(string, NULL, 10);
+ if (min_lb > 0)
+ GC_dbg_collect_at_malloc_min_lb = min_lb;
+ }
+ }
+# endif
# ifndef GC_DISABLE_INCREMENTAL
{
char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET");
@@ -947,16 +1118,14 @@ GC_API void GC_CALL GC_init(void)
GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word));
GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE);
# ifndef THREADS
- GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)(&dummy)));
+ GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)GC_approx_sp()));
# endif
# if !defined(_AUX_SOURCE) || defined(__GNUC__)
GC_STATIC_ASSERT((word)(-1) > (word)0);
/* word should be unsigned */
# endif
-# if !defined(__BORLANDC__) && !defined(__CC_ARM) /* Workaround */
- GC_STATIC_ASSERT((ptr_t)(word)(-1) > (ptr_t)0);
- /* Ptr_t comparisons should behave as unsigned comparisons. */
-# endif
+ /* We no longer check for ((void*)(-1) > NULL) since all pointers */
+ /* are explicitly cast to word in every less-greater comparison. */
GC_STATIC_ASSERT((signed_word)(-1) < (signed_word)0);
# ifndef GC_DISABLE_INCREMENTAL
if (GC_incremental || 0 != GETENV("GC_ENABLE_INCREMENTAL")) {
@@ -998,6 +1167,8 @@ GC_API void GC_CALL GC_init(void)
if (!GC_expand_hp_inner(initial_heap_sz)) {
GC_err_printf("Can't start up: not enough memory\n");
EXIT();
+ } else {
+ GC_requested_heapsize += initial_heap_sz;
}
if (GC_all_interior_pointers)
GC_initialize_offsets();
@@ -1029,25 +1200,14 @@ GC_API void GC_CALL GC_init(void)
# ifdef STUBBORN_ALLOC
GC_stubborn_init();
# endif
- /* Convince lint that some things are used */
-# ifdef LINT
- {
- extern char * const GC_copyright[];
- GC_noop(GC_copyright, GC_find_header, GC_push_one,
- GC_call_with_alloc_lock, GC_dont_expand,
-# ifndef NO_DEBUGGING
- GC_dump,
-# endif
- GC_register_finalizer_no_order);
+# ifndef DONT_USE_ATEXIT
+ if (GC_find_leak) {
+ /* This is to give us at least one chance to detect leaks. */
+ /* This may report some very benign leaks, but ... */
+ atexit(GC_exit_check);
}
# endif
- if (GC_find_leak) {
- /* This is to give us at least one chance to detect leaks. */
- /* This may report some very benign leaks, but ... */
- atexit(GC_exit_check);
- }
-
/* The rest of this again assumes we don't really hold */
/* the allocation lock. */
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
@@ -1103,6 +1263,14 @@ GC_API void GC_CALL GC_enable_incremental(void)
GC_init();
}
+#if defined(THREADS) && (!defined(PARALLEL_MARK) || !defined(CAN_HANDLE_FORK))
+ GC_API void GC_CALL GC_start_mark_threads(void)
+ {
+ /* No action since parallel markers are disabled (or no POSIX fork). */
+ GC_ASSERT(I_DONT_HOLD_LOCK());
+ }
+#endif
+
#if defined(MSWIN32) || defined(MSWINCE)
# if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE)
@@ -1130,72 +1298,91 @@ GC_API void GC_CALL GC_enable_incremental(void)
# define IF_NEED_TO_LOCK(x)
# endif /* !THREADS */
-# ifndef _MAX_PATH
-# define _MAX_PATH MAX_PATH
-# endif
-
STATIC HANDLE GC_CreateLogFile(void)
{
+ HANDLE hFile;
+ TCHAR *logPath;
+ BOOL appendToFile = FALSE;
# if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE)
- TCHAR logPath[_MAX_PATH + 0x10]; /* buffer for path + ext */
+ TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */
+
+ logPath = pathBuf;
# endif
+
/* Use GetEnvironmentVariable instead of GETENV() for unicode support. */
# ifndef NO_GETENV_WIN32
- if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), logPath,
- _MAX_PATH + 1) - 1U >= (DWORD)_MAX_PATH)
+ if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), pathBuf,
+ _MAX_PATH + 1) - 1U < (DWORD)_MAX_PATH) {
+ appendToFile = TRUE;
+ } else
# endif
- {
+ /* else */ {
/* Env var not found or its value too long. */
# ifdef OLD_WIN32_LOG_FILE
- return CreateFile(TEXT("gc.log"), GENERIC_WRITE, FILE_SHARE_READ,
- NULL /* lpSecurityAttributes */, CREATE_ALWAYS,
- FILE_FLAG_WRITE_THROUGH, NULL /* hTemplateFile */);
+ logPath = TEXT(GC_LOG_STD_NAME);
# else
- int len = (int)GetModuleFileName(NULL /* hModule */, logPath,
+ int len = (int)GetModuleFileName(NULL /* hModule */, pathBuf,
_MAX_PATH + 1);
/* If GetModuleFileName() has failed then len is 0. */
- if (len > 4 && logPath[len - 4] == (TCHAR)'.') {
+ if (len > 4 && pathBuf[len - 4] == (TCHAR)'.') {
len -= 4; /* strip executable file extension */
}
- /* strcat/wcscat() are deprecated on WinCE, so use memcpy() */
- memcpy(&logPath[len], TEXT(".gc.log"), sizeof(TEXT(".gc.log")));
+ BCOPY(TEXT("." GC_LOG_STD_NAME), &pathBuf[len],
+ sizeof(TEXT("." GC_LOG_STD_NAME)));
# endif
}
-# if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE)
- return CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ,
- NULL /* lpSecurityAttributes */, CREATE_ALWAYS,
- GC_print_stats == VERBOSE ? FILE_ATTRIBUTE_NORMAL :
+
+ hFile = CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ,
+ NULL /* lpSecurityAttributes */,
+ appendToFile ? OPEN_ALWAYS : CREATE_ALWAYS,
+ GC_print_stats == VERBOSE ? FILE_ATTRIBUTE_NORMAL :
/* immediately flush writes unless very verbose */
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
- NULL /* hTemplateFile */);
+ NULL /* hTemplateFile */);
+# ifndef NO_GETENV_WIN32
+ if (appendToFile && hFile != INVALID_HANDLE_VALUE) {
+ LONG posHigh = 0;
+ (void)SetFilePointer(hFile, 0, &posHigh, FILE_END);
+ /* Seek to file end (ignoring any error) */
+ }
# endif
+ return hFile;
}
STATIC int GC_write(const char *buf, size_t len)
{
- BOOL tmp;
+ BOOL res;
DWORD written;
+# if defined(THREADS) && defined(GC_ASSERTIONS)
+ static GC_bool inside_write = FALSE;
+ /* to prevent infinite recursion at abort. */
+ if (inside_write)
+ return -1;
+# endif
+
if (len == 0)
return 0;
IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs));
-# ifdef THREADS
- GC_ASSERT(!GC_write_disabled);
+# if defined(THREADS) && defined(GC_ASSERTIONS)
+ if (GC_write_disabled) {
+ inside_write = TRUE;
+ ABORT("Assertion failure: GC_write called with write_disabled");
+ }
# endif
+ if (GC_log == 0) {
+ GC_log = GC_CreateLogFile();
+ }
if (GC_log == INVALID_HANDLE_VALUE) {
- IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
+ IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
+# ifdef NO_DEBUGGING
+ /* Ignore open log failure (e.g., it might be caused by */
+ /* read-only folder of the client application). */
+ return 0;
+# else
return -1;
- } else if (GC_log == 0) {
- GC_log = GC_CreateLogFile();
- /* Ignore open log failure if the collector is built with */
- /* print_stats always set on. */
-# ifndef GC_PRINT_VERBOSE_STATS
- if (GC_log == INVALID_HANDLE_VALUE)
- ABORT("Open of log file failed");
# endif
}
- tmp = WriteFile(GC_log, buf, (DWORD)len, &written, NULL);
- if (!tmp)
- DebugBreak();
+ res = WriteFile(GC_log, buf, (DWORD)len, &written, NULL);
# if defined(_MSC_VER) && defined(_DEBUG)
# ifdef MSWINCE
/* There is no CrtDbgReport() in WinCE */
@@ -1212,7 +1399,7 @@ GC_API void GC_CALL GC_enable_incremental(void)
# endif
# endif
IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs));
- return tmp ? (int)written : -1;
+ return res ? (int)written : -1;
}
/* FIXME: This is pretty ugly ... */
@@ -1246,6 +1433,21 @@ GC_API void GC_CALL GC_enable_incremental(void)
# define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len))
+#elif defined(GC_ANDROID_LOG)
+
+# include <android/log.h>
+
+# ifndef GC_ANDROID_LOG_TAG
+# define GC_ANDROID_LOG_TAG "BDWGC"
+# endif
+
+# define GC_stdout ANDROID_LOG_DEBUG
+# define GC_stderr ANDROID_LOG_ERROR
+# define GC_log GC_stdout
+
+# define WRITE(level, buf, unused_len) \
+ __android_log_write(level, GC_ANDROID_LOG_TAG, buf)
+
#else
# if !defined(AMIGA) && !defined(__CC_ARM)
# include <unistd.h>
@@ -1286,78 +1488,109 @@ GC_API void GC_CALL GC_enable_incremental(void)
}
# define WRITE(f, buf, len) GC_write(f, buf, len)
-#endif /* !MSWIN32 && !OS2 && !MACOS */
+#endif /* !MSWIN32 && !OS2 && !MACOS && !GC_ANDROID_LOG */
#define BUFSZ 1024
#ifdef NO_VSNPRINTF
/* In case this function is missing (eg., in DJGPP v2.0.3). */
-# define vsnprintf(buf, bufsz, format, args) vsprintf(buf, format, args)
+# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args)
#elif defined(_MSC_VER)
# ifdef MSWINCE
/* _vsnprintf is deprecated in WinCE */
-# define vsnprintf StringCchVPrintfA
+# define GC_VSNPRINTF StringCchVPrintfA
# else
-# define vsnprintf _vsnprintf
+# define GC_VSNPRINTF _vsnprintf
# endif
+#else
+# define GC_VSNPRINTF vsnprintf
#endif
+
/* A version of printf that is unlikely to call malloc, and is thus safer */
/* to call from the collector in case malloc has been bound to GC_malloc. */
-/* Floating point arguments and formats should be avoided, since fp */
-/* conversion is more likely to allocate. */
+/* Floating point arguments and formats should be avoided, since FP */
+/* conversion is more likely to allocate memory. */
/* Assumes that no more than BUFSZ-1 characters are written at once. */
+#define GC_PRINTF_FILLBUF(buf, format) \
+ do { \
+ va_list args; \
+ va_start(args, format); \
+ (buf)[sizeof(buf) - 1] = 0x15; /* guard */ \
+ (void)GC_VSNPRINTF(buf, sizeof(buf) - 1, format, args); \
+ va_end(args); \
+ if ((buf)[sizeof(buf) - 1] != 0x15) \
+ ABORT("GC_printf clobbered stack"); \
+ } while (0)
+
void GC_printf(const char *format, ...)
{
- va_list args;
- char buf[BUFSZ+1];
+ char buf[BUFSZ + 1];
- if (GC_quiet) return;
- va_start(args, format);
- buf[BUFSZ] = 0x15;
- (void) vsnprintf(buf, BUFSZ, format, args);
- va_end(args);
- if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
- if (WRITE(GC_stdout, buf, strlen(buf)) < 0)
- ABORT("write to stdout failed");
+ if (!GC_quiet) {
+ GC_PRINTF_FILLBUF(buf, format);
+ if (WRITE(GC_stdout, buf, strlen(buf)) < 0)
+ ABORT("write to stdout failed");
+ }
}
void GC_err_printf(const char *format, ...)
{
- va_list args;
- char buf[BUFSZ+1];
+ char buf[BUFSZ + 1];
- va_start(args, format);
- buf[BUFSZ] = 0x15;
- (void) vsnprintf(buf, BUFSZ, format, args);
- va_end(args);
- if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
- if (WRITE(GC_stderr, buf, strlen(buf)) < 0)
- ABORT("write to stderr failed");
+ GC_PRINTF_FILLBUF(buf, format);
+ GC_err_puts(buf);
}
void GC_log_printf(const char *format, ...)
{
- va_list args;
- char buf[BUFSZ+1];
+ char buf[BUFSZ + 1];
- va_start(args, format);
- buf[BUFSZ] = 0x15;
- (void) vsnprintf(buf, BUFSZ, format, args);
- va_end(args);
- if (buf[BUFSZ] != 0x15) ABORT("GC_printf clobbered stack");
+ GC_PRINTF_FILLBUF(buf, format);
if (WRITE(GC_log, buf, strlen(buf)) < 0)
- ABORT("write to log failed");
+ ABORT("write to GC log failed");
}
-/* This is equivalent to GC_err_printf("%s",s). */
+#ifndef GC_ANDROID_LOG
+
+# define GC_warn_printf GC_err_printf
+
+#else
+
+ GC_INNER void GC_info_log_printf(const char *format, ...)
+ {
+ char buf[BUFSZ + 1];
+
+ GC_PRINTF_FILLBUF(buf, format);
+ (void)WRITE(ANDROID_LOG_INFO, buf, 0 /* unused */);
+ }
+
+ GC_INNER void GC_verbose_log_printf(const char *format, ...)
+ {
+ char buf[BUFSZ + 1];
+
+ GC_PRINTF_FILLBUF(buf, format);
+ (void)WRITE(ANDROID_LOG_VERBOSE, buf, 0); /* ignore write errors */
+ }
+
+ STATIC void GC_warn_printf(const char *format, ...)
+ {
+ char buf[BUFSZ + 1];
+
+ GC_PRINTF_FILLBUF(buf, format);
+ (void)WRITE(ANDROID_LOG_WARN, buf, 0);
+ }
+
+#endif /* GC_ANDROID_LOG */
+
void GC_err_puts(const char *s)
{
- if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed");
+ (void)WRITE(GC_stderr, s, strlen(s)); /* ignore errors */
}
STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg)
{
- GC_err_printf(msg, arg);
+ /* TODO: Add assertion on arg comply with msg (format). */
+ GC_warn_printf(msg, arg);
}
GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;
@@ -1399,34 +1632,53 @@ GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void)
}
#if !defined(PCR) && !defined(SMALL_CONFIG)
- /* Print (or display) a message before abort. msg must not be NULL. */
- void GC_on_abort(const char *msg)
+ /* Print (or display) a message before abnormal exit (including */
+ /* abort). Invoked from ABORT(msg) macro (there msg is non-NULL) */
+ /* and from EXIT() macro (msg is NULL in that case). */
+ STATIC void GC_CALLBACK GC_default_on_abort(const char *msg)
{
-# if defined(MSWIN32)
-# ifndef DONT_USE_USER32_DLL
- /* Use static binding to "user32.dll". */
- (void)MessageBoxA(NULL, msg, "Fatal error in GC", MB_ICONERROR|MB_OK);
-# else
- /* This simplifies linking - resolve "MessageBoxA" at run-time. */
- HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll"));
- if (hU32) {
- FARPROC pfn = GetProcAddress(hU32, "MessageBoxA");
- if (pfn)
- (void)(*(int (WINAPI *)(HWND, LPCSTR, LPCSTR, UINT))pfn)(
- NULL /* hWnd */, msg, "Fatal error in GC",
- MB_ICONERROR | MB_OK);
- (void)FreeLibrary(hU32);
- }
+ GC_find_leak = FALSE; /* disable at-exit GC_gcollect() */
+
+ if (msg != NULL) {
+# if defined(MSWIN32)
+# ifndef DONT_USE_USER32_DLL
+ /* Use static binding to "user32.dll". */
+ (void)MessageBoxA(NULL, msg, "Fatal error in GC",
+ MB_ICONERROR | MB_OK);
+# else
+ /* This simplifies linking - resolve "MessageBoxA" at run-time. */
+ HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll"));
+ if (hU32) {
+ FARPROC pfn = GetProcAddress(hU32, "MessageBoxA");
+ if (pfn)
+ (void)(*(int (WINAPI *)(HWND, LPCSTR, LPCSTR, UINT))pfn)(
+ NULL /* hWnd */, msg, "Fatal error in GC",
+ MB_ICONERROR | MB_OK);
+ (void)FreeLibrary(hU32);
+ }
+# endif
+ /* Also duplicate msg to GC log file. */
# endif
- /* Also duplicate msg to GC log file. */
-# endif
+
+# ifndef GC_ANDROID_LOG
/* Avoid calling GC_err_printf() here, as GC_on_abort() could be */
/* called from it. Note 1: this is not an atomic output. */
/* Note 2: possible write errors are ignored. */
- if (WRITE(GC_stderr, (void *)msg, strlen(msg)) >= 0)
- (void)WRITE(GC_stderr, (void *)("\n"), 1);
+# if defined(THREADS) && defined(GC_ASSERTIONS) \
+ && (defined(MSWIN32) || defined(MSWINCE))
+ if (!GC_write_disabled)
+# endif
+ {
+ if (WRITE(GC_stderr, (void *)msg, strlen(msg)) >= 0)
+ (void)WRITE(GC_stderr, (void *)("\n"), 1);
+ }
+# else
+ __android_log_assert("*" /* cond */, GC_ANDROID_LOG_TAG, "%s\n", msg);
+# endif
+ }
- if (GETENV("GC_LOOP_ON_ABORT") != NULL) {
+# if !defined(NO_DEBUGGING) && !defined(GC_ANDROID_LOG)
+ if (GETENV("GC_LOOP_ON_ABORT") != NULL) {
/* In many cases it's easier to debug a running process. */
/* It's arguably nicer to sleep, but that makes it harder */
/* to look at the thread if the debugger doesn't know much */
@@ -1434,14 +1686,38 @@ GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void)
for(;;) {
/* Empty */
}
- }
+ }
+# endif
+ }
+
+ GC_abort_func GC_on_abort = GC_default_on_abort;
+
+ GC_API void GC_CALL GC_set_abort_func(GC_abort_func fn)
+ {
+ DCL_LOCK_STATE;
+ GC_ASSERT(fn != 0);
+ LOCK();
+ GC_on_abort = fn;
+ UNLOCK();
+ }
+
+ GC_API GC_abort_func GC_CALL GC_get_abort_func(void)
+ {
+ GC_abort_func fn;
+ DCL_LOCK_STATE;
+ LOCK();
+ fn = GC_on_abort;
+ UNLOCK();
+ return fn;
}
#endif /* !SMALL_CONFIG */
GC_API void GC_CALL GC_enable(void)
{
DCL_LOCK_STATE;
+
LOCK();
+ GC_ASSERT(GC_dont_gc != 0); /* ensure no counter underflow */
GC_dont_gc--;
UNLOCK();
}
@@ -1454,6 +1730,11 @@ GC_API void GC_CALL GC_disable(void)
UNLOCK();
}
+GC_API int GC_CALL GC_is_disabled(void)
+{
+ return GC_dont_gc != 0;
+}
+
/* Helper procedures for new kind creation. */
GC_API void ** GC_CALL GC_new_free_list_inner(void)
{
@@ -1522,18 +1803,37 @@ GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc)
return result;
}
+GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, void *client_data)
+{
+ void * result;
+ DCL_LOCK_STATE;
+
+# ifdef THREADS
+ LOCK();
+# endif
+ result = (*fn)(client_data);
+# ifdef THREADS
+ UNLOCK();
+# endif
+ return(result);
+}
+
GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg)
{
- int dummy;
struct GC_stack_base base;
+ void *result;
- base.mem_base = (void *)&dummy;
+ base.mem_base = (void *)&base;
# ifdef IA64
base.reg_base = (void *)GC_save_regs_in_stack();
/* Unnecessarily flushes register stack, */
/* but that probably doesn't hurt. */
# endif
- return fn(&base, arg);
+ result = fn(&base, arg);
+ /* Strongly discourage the compiler from treating the above */
+ /* as a tail call. */
+ GC_noop1((word)(&base));
+ return result;
}
#ifndef THREADS
@@ -1556,12 +1856,15 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
/* Adjust our stack base value (this could happen if */
/* GC_get_main_stack_base() is unimplemented or broken for */
/* the platform). */
- if (GC_stackbottom HOTTER_THAN (ptr_t)(&stacksect))
+ if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect))
GC_stackbottom = (ptr_t)(&stacksect);
if (GC_blocked_sp == NULL) {
/* We are not inside GC_do_blocking() - do nothing more. */
- return fn(client_data);
+ client_data = fn(client_data);
+ /* Prevent treating the above as a tail call. */
+ GC_noop1((word)(&stacksect));
+ return client_data; /* result */
}
/* Setup new "stack section". */
diff --git a/new_hblk.c b/new_hblk.c
index 1366b131..05c4abff 100644
--- a/new_hblk.c
+++ b/new_hblk.c
@@ -37,7 +37,7 @@
p[2] = (word)p;
p[3] = 0;
p += 4;
- for (; p < lim; p += 4) {
+ for (; (word)p < (word)lim; p += 4) {
p[0] = (word)(p-2);
p[1] = 0;
p[2] = (word)p;
@@ -57,7 +57,7 @@
p[2] = 0;
p[3] = 0;
p += 4;
- for (; p < lim; p += 4) {
+ for (; (word)p < (word)lim; p += 4) {
PREFETCH_FOR_WRITE((ptr_t)(p+64));
p[0] = (word)(p-4);
p[1] = 0;
@@ -75,7 +75,7 @@
p[0] = (word)ofl;
p[2] = (word)p;
p += 4;
- for (; p < lim; p += 4) {
+ for (; (word)p < (word)lim; p += 4) {
p[0] = (word)(p-2);
p[2] = (word)p;
};
@@ -91,7 +91,7 @@
p[0] = (word)ofl;
p[4] = (word)p;
p += 8;
- for (; p < lim; p += 8) {
+ for (; (word)p < (word)lim; p += 8) {
PREFETCH_FOR_WRITE((ptr_t)(p+64));
p[0] = (word)(p-4);
p[4] = (word)p;
@@ -150,7 +150,7 @@ GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear,
/* Last place for last object to start */
/* make a list of all objects in *h with head as last object */
- while (p <= last_object) {
+ while ((word)p <= (word)last_object) {
/* current object's link points to last object */
obj_link(p) = (ptr_t)prev;
prev = p;
@@ -164,16 +164,13 @@ GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear,
return ((ptr_t)p);
}
-/*
- * Allocate a new heapblock for small objects of size gran granules.
- * Add all of the heapblock's objects to the free list for objects
- * of that size.
- * Set all mark bits if objects are uncollectable.
- * Will fail to do anything if we are out of memory.
- */
+/* Allocate a new heapblock for small objects of size gran granules. */
+/* Add all of the heapblock's objects to the free list for objects */
+/* of that size. Set all mark bits if objects are uncollectible. */
+/* Will fail to do anything if we are out of memory. */
GC_INNER void GC_new_hblk(size_t gran, int kind)
{
- struct hblk *h; /* the new heap block */
+ struct hblk *h; /* the new heap block */
GC_bool clear = GC_obj_kinds[kind].ok_init;
GC_STATIC_ASSERT((sizeof (struct hblk)) == HBLKSIZE);
diff --git a/obj_map.c b/obj_map.c
index ed75ef12..c935bf3f 100644
--- a/obj_map.c
+++ b/obj_map.c
@@ -59,9 +59,9 @@ GC_INNER void GC_register_displacement_inner(size_t offset)
}
new_map = (short *)GC_scratch_alloc(MAP_LEN * sizeof(short));
if (new_map == 0) return(FALSE);
- if (GC_print_stats)
- GC_log_printf("Adding block map for size of %u granules (%u bytes)\n",
- (unsigned)granules, (unsigned)(GRANULES_TO_BYTES(granules)));
+ GC_COND_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++) {
new_map[displ] = 1; /* Nonzero to get us out of marker fast path. */
@@ -74,7 +74,7 @@ GC_INNER void GC_register_displacement_inner(size_t offset)
GC_obj_map[granules] = new_map;
return(TRUE);
}
-#endif
+#endif /* MARK_BIT_PER_GRANULE */
GC_INNER void GC_initialize_offsets(void)
{
diff --git a/os_dep.c b/os_dep.c
index c495ff0d..2730205d 100644
--- a/os_dep.c
+++ b/os_dep.c
@@ -16,7 +16,7 @@
#include "private/gc_priv.h"
-#if defined(LINUX) && !defined(POWERPC)
+#if defined(LINUX) && !defined(POWERPC) && !defined(NO_SIGCONTEXT_H)
# include <linux/version.h>
# if (LINUX_VERSION_CODE <= 0x10400)
/* Ugly hack to get struct sigcontext_struct definition. Required */
@@ -38,13 +38,13 @@
/* has the right declaration for glibc 2.1. */
# include <sigcontext.h>
# endif /* 0 == __GLIBC_MINOR__ */
-# else /* not 2 <= __GLIBC__ */
+# else /* __GLIBC__ < 2 */
/* libc5 doesn't have <sigcontext.h>: go directly with the kernel */
/* one. Check LINUX_VERSION_CODE to see which we should reference. */
# include <asm/sigcontext.h>
-# endif /* 2 <= __GLIBC__ */
+# endif /* __GLIBC__ < 2 */
# endif
-#endif
+#endif /* LINUX && !POWERPC */
#if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \
&& !defined(MSWINCE) && !defined(__CC_ARM)
@@ -61,7 +61,8 @@
# include <signal.h>
#endif
-#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL)
+#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL) \
+ || defined(SYMBIAN)
# include <fcntl.h>
#endif
@@ -98,8 +99,7 @@
#endif
#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \
- || ((defined(USE_MMAP) || defined(USE_MUNMAP)) \
- && !defined(MSWIN32) && !defined(MSWINCE))
+ || ((defined(USE_MMAP) || defined(USE_MUNMAP)) && !defined(USE_WINALLOC))
# define MMAP_SUPPORTED
#endif
@@ -200,8 +200,7 @@ GC_INNER char * GC_get_maps(void)
{
int f;
ssize_t result;
- static char init_buf[1];
- static char *maps_buf = init_buf;
+ static char *maps_buf = NULL;
static size_t maps_buf_sz = 1;
size_t maps_size, old_maps_size = 0;
@@ -266,13 +265,10 @@ GC_INNER char * GC_get_maps(void)
return 0;
# ifdef THREADS
if (maps_size > old_maps_size) {
- if (GC_print_stats)
- GC_log_printf(
- "Unexpected maps size growth from %lu to %lu\n",
- (unsigned long)old_maps_size,
- (unsigned long)maps_size);
- ABORT("Unexpected asynchronous /proc/self/maps growth: "
- "unregistered thread?");
+ ABORT_ARG2("Unexpected asynchronous /proc/self/maps growth "
+ "(unregistered thread?)", " from %lu to %lu",
+ (unsigned long)old_maps_size,
+ (unsigned long)maps_size);
}
# endif
} while (maps_size >= maps_buf_sz || maps_size < old_maps_size);
@@ -373,7 +369,7 @@ GC_INNER char * GC_get_maps(void)
if (buf_ptr == NULL) return FALSE;
if (prot[1] == 'w' && maj_dev == 0) {
- if (my_end > addr && my_start <= addr) {
+ if ((word)my_end > (word)addr && (word)my_start <= (word)addr) {
*startp = my_start;
*endp = my_end;
return TRUE;
@@ -407,7 +403,7 @@ GC_INNER char * GC_get_maps(void)
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;
+ while (*p != '/' && (word)p >= (word)map_path) --p;
++p;
if (strncmp(nm, p, nm_len) == 0) {
*startp = my_start;
@@ -425,9 +421,7 @@ GC_INNER char * GC_get_maps(void)
{
ptr_t my_start, my_end;
if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) {
- if (GC_print_stats) {
- GC_log_printf("Failed to find backing store base from /proc\n");
- }
+ GC_COND_LOG_PRINTF("Failed to find backing store base from /proc\n");
return 0;
}
return my_start;
@@ -461,23 +455,30 @@ GC_INNER char * GC_get_maps(void)
GC_INNER void GC_init_linux_data_start(void)
{
- if (GC_no_dls) {
- /* Not needed, avoids the SIGSEGV caused by */
- /* GC_find_limit which complicates debugging. */
- return;
- }
-
-# if defined(LINUX) || defined(HURD)
+# if (defined(LINUX) || defined(HURD)) && !defined(IGNORE_PROG_DATA_START)
/* Try the easy approaches first: */
if ((ptr_t)__data_start != 0) {
GC_data_start = (ptr_t)(__data_start);
+ GC_ASSERT((word)GC_data_start <= (word)_end);
return;
}
if ((ptr_t)data_start != 0) {
GC_data_start = (ptr_t)(data_start);
+ GC_ASSERT((word)GC_data_start <= (word)_end);
return;
}
+# ifdef DEBUG_ADD_DEL_ROOTS
+ GC_log_printf("__data_start not provided\n");
+# endif
# endif /* LINUX */
+
+ if (GC_no_dls) {
+ /* Not needed, avoids the SIGSEGV caused by */
+ /* GC_find_limit which complicates debugging. */
+ GC_data_start = (ptr_t)_end; /* set data root size to 0 */
+ return;
+ }
+
GC_data_start = GC_find_limit((ptr_t)(_end), FALSE);
}
#endif /* SEARCH_FOR_DATA_START */
@@ -499,7 +500,7 @@ GC_INNER char * GC_get_maps(void)
{
void *p = ecos_gc_brk;
ecos_gc_brk += increment;
- if (ecos_gc_brk > ecos_gc_memory + sizeof(ecos_gc_memory)) {
+ if ((word)ecos_gc_brk > (word)(ecos_gc_memory + sizeof(ecos_gc_memory))) {
ecos_gc_brk -= increment;
return NULL;
}
@@ -541,7 +542,7 @@ GC_INNER char * GC_get_maps(void)
siglongjmp(GC_jmp_buf_openbsd, 1);
}
- /* Return the first non-addressible location > p or bound. */
+ /* Return the first non-addressable location > p or bound. */
/* Requires the allocation lock. */
STATIC ptr_t GC_find_limit_openbsd(ptr_t p, ptr_t bound)
{
@@ -558,13 +559,14 @@ GC_INNER char * GC_get_maps(void)
act.sa_handler = GC_fault_handler_openbsd;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_RESTART;
+ /* act.sa_restorer is deprecated and should not be initialized. */
sigaction(SIGSEGV, &act, &old_segv_act);
if (sigsetjmp(GC_jmp_buf_openbsd, 1) == 0) {
result = (ptr_t)((word)p & ~(pgsz-1));
for (;;) {
result += pgsz;
- if (result >= bound) {
+ if ((word)result >= (word)bound) {
result = bound;
break;
}
@@ -595,6 +597,7 @@ GC_INNER char * GC_get_maps(void)
act.sa_handler = GC_fault_handler_openbsd;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_RESTART;
+ /* act.sa_restorer is deprecated and should not be initialized. */
sigaction(SIGSEGV, &act, &old_segv_act);
firstpass = 1;
@@ -602,7 +605,7 @@ GC_INNER char * GC_get_maps(void)
if (sigsetjmp(GC_jmp_buf_openbsd, 1) != 0 || firstpass) {
firstpass = 0;
result += pgsz;
- if (result >= bound) {
+ if ((word)result >= (word)bound) {
result = bound;
} else {
GC_noop1((word)(*result));
@@ -709,6 +712,8 @@ GC_INNER word GC_page_size = 0;
GC_INNER GC_bool GC_dont_query_stack_min = FALSE;
# endif
+ GC_INNER SYSTEM_INFO GC_sysinfo;
+
GC_INNER void GC_setpagesize(void)
{
GetSystemInfo(&GC_sysinfo);
@@ -765,9 +770,7 @@ GC_INNER word GC_page_size = 0;
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
{
- int dummy;
- ptr_t sp = (ptr_t)(&dummy);
- ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1));
+ ptr_t trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(GC_page_size - 1));
/* FIXME: This won't work if called from a deeply recursive */
/* client code (and the committed stack space has grown). */
word size = GC_get_writable_length(trunc_sp, 0);
@@ -780,7 +783,10 @@ GC_INNER word GC_page_size = 0;
/* gcc version of boehm-gc). */
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
{
- extern void * _tlsbase __asm__ ("%fs:4");
+ void * _tlsbase;
+
+ __asm__ ("movl %%fs:4, %0"
+ : "=r" (_tlsbase));
sb -> mem_base = _tlsbase;
return GC_SUCCESS;
}
@@ -792,7 +798,7 @@ GC_INNER word GC_page_size = 0;
{
# if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP)
GC_page_size = GETPAGESIZE();
- if (!GC_page_size) ABORT("getpagesize() failed");
+ if (!GC_page_size) ABORT("getpagesize failed");
# else
/* It's acceptable to fake it. */
GC_page_size = HBLKSIZE;
@@ -838,20 +844,23 @@ GC_INNER word GC_page_size = 0;
typedef void (*GC_fault_handler_t)(int);
# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
- || defined(HURD) || defined(NETBSD)
+ || defined(HURD) || defined(FREEBSD) || defined(NETBSD)
static struct sigaction old_segv_act;
-# if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \
+# if defined(_sigargs) /* !Irix6.x */ \
|| defined(HURD) || defined(NETBSD) || defined(FREEBSD)
static struct sigaction old_bus_act;
# endif
# else
- static GC_fault_handler_t old_segv_handler, old_bus_handler;
+ static GC_fault_handler_t old_segv_handler;
+# ifdef SIGBUS
+ static GC_fault_handler_t old_bus_handler;
+# endif
# endif
GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h)
{
-# if defined(SUNOS5SIGS) || defined(IRIX5) \
- || defined(OSF1) || defined(HURD) || defined(NETBSD)
+# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
+ || defined(HURD) || defined(FREEBSD) || defined(NETBSD)
struct sigaction act;
act.sa_handler = h;
@@ -864,6 +873,7 @@ GC_INNER word GC_page_size = 0;
# endif
(void) sigemptyset(&act.sa_mask);
+ /* act.sa_restorer is deprecated and should not be initialized. */
# ifdef GC_IRIX_THREADS
/* Older versions have a bug related to retrieving and */
/* and setting a handler at the same time. */
@@ -872,14 +882,13 @@ GC_INNER word GC_page_size = 0;
# else
(void) sigaction(SIGSEGV, &act, &old_segv_act);
# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
- || defined(HPUX) || defined(HURD) || defined(NETBSD) \
- || defined(FREEBSD)
+ || defined(HURD) || defined(NETBSD) || defined(FREEBSD)
/* Under Irix 5.x or HP/UX, we may get SIGBUS. */
/* Pthreads doesn't exist under Irix 5.x, so we */
/* don't have to worry in the threads case. */
(void) sigaction(SIGBUS, &act, &old_bus_act);
# endif
-# endif /* GC_IRIX_THREADS */
+# endif /* !GC_IRIX_THREADS */
# else
old_segv_handler = signal(SIGSEGV, h);
# ifdef SIGBUS
@@ -909,12 +918,11 @@ GC_INNER word GC_page_size = 0;
GC_INNER void GC_reset_fault_handler(void)
{
-# if defined(SUNOS5SIGS) || defined(IRIX5) \
- || defined(OSF1) || defined(HURD) || defined(NETBSD)
+# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \
+ || defined(HURD) || defined(FREEBSD) || defined(NETBSD)
(void) sigaction(SIGSEGV, &old_segv_act, 0);
# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \
- || defined(HPUX) || defined(HURD) || defined(NETBSD) \
- || defined(FREEBSD)
+ || defined(HURD) || defined(NETBSD)
(void) sigaction(SIGBUS, &old_bus_act, 0);
# endif
# else
@@ -945,13 +953,13 @@ GC_INNER word GC_page_size = 0;
for (;;) {
if (up) {
result += MIN_PAGE_SIZE;
- if (result >= bound) {
+ if ((word)result >= (word)bound) {
result = bound;
break;
}
} else {
result -= MIN_PAGE_SIZE;
- if (result <= bound) {
+ if ((word)result <= (word)bound) {
result = bound - MIN_PAGE_SIZE;
/* This is to compensate */
/* further result increment (we */
@@ -1075,10 +1083,8 @@ GC_INNER word GC_page_size = 0;
} /* Otherwise it's not safe to add 16 bytes and we fall */
/* back to using /proc. */
# elif defined(SPARC)
- /* Older versions of glibc for 64-bit Sparc do not set
- * this variable correctly, it gets set to either zero
- * or one.
- */
+ /* Older versions of glibc for 64-bit SPARC do not set this */
+ /* variable correctly, it gets set to either zero or one. */
if (__libc_stack_end != (ptr_t) (unsigned long)0x1)
return __libc_stack_end;
# else
@@ -1145,6 +1151,13 @@ GC_INNER word GC_page_size = 0;
return STACKBOTTOM;
}
# define GET_MAIN_STACKBASE_SPECIAL
+#elif defined(SYMBIAN)
+ extern int GC_get_main_symbian_stack_base(void);
+ ptr_t GC_get_main_stack_base(void)
+ {
+ return (ptr_t)GC_get_main_symbian_stack_base();
+ }
+# define GET_MAIN_STACKBASE_SPECIAL
#elif !defined(BEOS) && !defined(AMIGA) && !defined(OS2) \
&& !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) \
&& !defined(GC_OPENBSD_THREADS) \
@@ -1162,13 +1175,14 @@ GC_INNER word GC_page_size = 0;
ptr_t GC_get_main_stack_base(void)
{
- ptr_t result; /* also used as "dummy" to get the approx. sp value */
-# if defined(LINUX) && !defined(NACL) \
+ ptr_t result;
+# if defined(LINUX) && !defined(NO_PTHREAD_GETATTR_NP) \
&& (defined(USE_GET_STACKBASE_FOR_MAIN) \
|| (defined(THREADS) && !defined(REDIRECT_MALLOC)))
pthread_attr_t attr;
void *stackaddr;
size_t size;
+
if (pthread_getattr_np(pthread_self(), &attr) == 0) {
if (pthread_attr_getstack(&attr, &stackaddr, &size) == 0
&& stackaddr != NULL) {
@@ -1189,10 +1203,10 @@ GC_INNER word GC_page_size = 0;
# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1)
# ifdef HEURISTIC1
# ifdef STACK_GROWS_DOWN
- result = (ptr_t)((((word)(&result)) + STACKBOTTOM_ALIGNMENT_M1)
+ result = (ptr_t)(((word)GC_approx_sp() + STACKBOTTOM_ALIGNMENT_M1)
& ~STACKBOTTOM_ALIGNMENT_M1);
# else
- result = (ptr_t)(((word)(&result)) & ~STACKBOTTOM_ALIGNMENT_M1);
+ result = (ptr_t)((word)GC_approx_sp() & ~STACKBOTTOM_ALIGNMENT_M1);
# endif
# endif /* HEURISTIC1 */
# ifdef LINUX_STACKBOTTOM
@@ -1202,36 +1216,40 @@ GC_INNER word GC_page_size = 0;
result = GC_freebsd_main_stack_base();
# endif
# ifdef HEURISTIC2
-# ifdef STACK_GROWS_DOWN
- result = GC_find_limit((ptr_t)(&result), TRUE);
-# ifdef HEURISTIC2_LIMIT
- if (result > HEURISTIC2_LIMIT
- && (ptr_t)(&result) < HEURISTIC2_LIMIT) {
- result = HEURISTIC2_LIMIT;
- }
-# endif
-# else
- result = GC_find_limit((ptr_t)(&result), FALSE);
-# ifdef HEURISTIC2_LIMIT
- if (result < HEURISTIC2_LIMIT
- && (ptr_t)(&result) > HEURISTIC2_LIMIT) {
- result = HEURISTIC2_LIMIT;
- }
+ {
+ ptr_t sp = GC_approx_sp();
+# ifdef STACK_GROWS_DOWN
+ result = GC_find_limit(sp, TRUE);
+# ifdef HEURISTIC2_LIMIT
+ if ((word)result > (word)HEURISTIC2_LIMIT
+ && (word)sp < (word)HEURISTIC2_LIMIT) {
+ result = HEURISTIC2_LIMIT;
+ }
+# endif
+# else
+ result = GC_find_limit(sp, FALSE);
+# ifdef HEURISTIC2_LIMIT
+ if ((word)result < (word)HEURISTIC2_LIMIT
+ && (word)sp > (word)HEURISTIC2_LIMIT) {
+ result = HEURISTIC2_LIMIT;
+ }
+# endif
# endif
-# endif
+ }
# endif /* HEURISTIC2 */
# ifdef STACK_GROWS_DOWN
if (result == 0)
result = (ptr_t)(signed_word)(-sizeof(ptr_t));
# endif
# endif
- GC_ASSERT((ptr_t)(&result) HOTTER_THAN result);
+ GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)result);
return(result);
}
# define GET_MAIN_STACKBASE_SPECIAL
#endif /* !AMIGA, !BEOS, !OPENBSD, !OS2, !Windows */
-#if (defined(GC_LINUX_THREADS) || defined(PLATFORM_ANDROID)) && !defined(NACL)
+#if (defined(GC_LINUX_THREADS) || defined(PLATFORM_ANDROID)) \
+ && !defined(NO_PTHREAD_GETATTR_NP)
# include <pthread.h>
/* extern int pthread_getattr_np(pthread_t, pthread_attr_t *); */
@@ -1290,13 +1308,10 @@ GC_INNER word GC_page_size = 0;
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
{
-# ifdef GC_ASSERTIONS
- int dummy;
-# endif
/* pthread_get_stackaddr_np() should return stack bottom (highest */
/* stack address plus 1). */
b->mem_base = pthread_get_stackaddr_np(pthread_self());
- GC_ASSERT((void *)&dummy HOTTER_THAN b->mem_base);
+ GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)b->mem_base);
return GC_SUCCESS;
}
# define HAVE_GET_STACK_BASE
@@ -1311,7 +1326,8 @@ GC_INNER word GC_page_size = 0;
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb)
{
stack_t stack;
- pthread_stackseg_np(pthread_self(), &stack);
+ if (pthread_stackseg_np(pthread_self(), &stack))
+ ABORT("pthread_stackseg_np(self) failed");
sb->mem_base = stack.ss_sp;
return GC_SUCCESS;
}
@@ -1335,6 +1351,7 @@ GC_INNER word GC_page_size = 0;
{
stack_t s;
pthread_t self = pthread_self();
+
if (self == stackbase_main_self)
{
/* If the client calls GC_get_stack_base() from the main thread */
@@ -1352,7 +1369,7 @@ GC_INNER word GC_page_size = 0;
ABORT("thr_stksegment failed");
}
/* s.ss_sp holds the pointer to the stack bottom. */
- GC_ASSERT((void *)&s HOTTER_THAN s.ss_sp);
+ GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)s.ss_sp);
if (!stackbase_main_self && thr_main() != 0)
{
@@ -1378,37 +1395,46 @@ GC_INNER word GC_page_size = 0;
#endif /* GC_RTEMS_PTHREADS */
#ifndef HAVE_GET_STACK_BASE
- /* Retrieve stack base. */
- /* Using the GC_find_limit version is risky. */
- /* On IA64, for example, there is no guard page between the */
- /* stack of one thread and the register backing store of the */
- /* next. Thus this is likely to identify way too large a */
- /* "stack" and thus at least result in disastrous performance. */
- /* FIXME - Implement better strategies here. */
- GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
- {
-# ifdef NEED_FIND_LIMIT
- int dummy;
+# ifdef NEED_FIND_LIMIT
+ /* Retrieve stack base. */
+ /* Using the GC_find_limit version is risky. */
+ /* On IA64, for example, there is no guard page between the */
+ /* stack of one thread and the register backing store of the */
+ /* next. Thus this is likely to identify way too large a */
+ /* "stack" and thus at least result in disastrous performance. */
+ /* FIXME - Implement better strategies here. */
+ GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b)
+ {
IF_CANCEL(int cancel_state;)
DCL_LOCK_STATE;
LOCK();
DISABLE_CANCEL(cancel_state); /* May be unnecessary? */
# ifdef STACK_GROWS_DOWN
- b -> mem_base = GC_find_limit((ptr_t)(&dummy), TRUE);
+ b -> mem_base = GC_find_limit(GC_approx_sp(), TRUE);
# ifdef IA64
b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE);
# endif
# else
- b -> mem_base = GC_find_limit(&dummy, FALSE);
+ b -> mem_base = GC_find_limit(GC_approx_sp(), FALSE);
# endif
RESTORE_CANCEL(cancel_state);
UNLOCK();
return GC_SUCCESS;
-# else
- return GC_UNIMPLEMENTED;
-# endif
- }
+ }
+# else
+ GC_API int GC_CALL GC_get_stack_base(
+ struct GC_stack_base *b GC_ATTR_UNUSED)
+ {
+# if defined(GET_MAIN_STACKBASE_SPECIAL) && !defined(THREADS) \
+ && !defined(IA64)
+ b->mem_base = GC_get_main_stack_base();
+ return GC_SUCCESS;
+# else
+ return GC_UNIMPLEMENTED;
+# endif
+ }
+# endif /* !NEED_FIND_LIMIT */
#endif /* !HAVE_GET_STACK_BASE */
#ifndef GET_MAIN_STACKBASE_SPECIAL
@@ -1416,9 +1442,10 @@ GC_INNER word GC_page_size = 0;
ptr_t GC_get_main_stack_base(void)
{
struct GC_stack_base sb;
+
if (GC_get_stack_base(&sb) != GC_SUCCESS)
ABORT("GC_get_stack_base failed");
- GC_ASSERT((void *)&sb HOTTER_THAN sb.mem_base);
+ GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)sb.mem_base);
return (ptr_t)sb.mem_base;
}
#endif /* !GET_MAIN_STACKBASE_SPECIAL */
@@ -1439,103 +1466,51 @@ void GC_register_data_segments(void)
FILE * myexefile;
struct exe_hdr hdrdos; /* MSDOS header. */
struct e32_exe hdr386; /* Real header for my executable */
- struct o32_obj seg; /* Currrent segment */
+ struct o32_obj seg; /* Current segment */
int nsegs;
-
if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
ABORT("DosGetInfoBlocks failed");
}
module_handle = ppib -> pib_hmte;
if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) {
- GC_err_printf("DosQueryModuleName failed\n");
- ABORT("DosGetInfoBlocks failed");
+ ABORT("DosQueryModuleName failed");
}
myexefile = fopen(path, "rb");
if (myexefile == 0) {
- if (GC_print_stats) {
- GC_err_puts("Couldn't open executable ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Failed to open executable");
+ ABORT_ARG1("Failed to open executable", ": %s", path);
}
if (fread((char *)(&hdrdos), 1, sizeof(hdrdos), myexefile)
< sizeof(hdrdos)) {
- if (GC_print_stats) {
- GC_err_puts("Couldn't read MSDOS header from ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Couldn't read MSDOS header");
+ ABORT_ARG1("Could not read MSDOS header", " from: %s", path);
}
if (E_MAGIC(hdrdos) != EMAGIC) {
- if (GC_print_stats) {
- GC_err_puts("Executable has wrong DOS magic number: ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Bad DOS magic number");
+ ABORT_ARG1("Bad DOS magic number", " in file: %s", path);
}
if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) {
- if (GC_print_stats) {
- GC_err_puts("Seek to new header failed in ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Bad DOS magic number");
+ ABORT_ARG1("Bad DOS magic number", " in file: %s", path);
}
if (fread((char *)(&hdr386), 1, sizeof(hdr386), myexefile)
< sizeof(hdr386)) {
- if (GC_print_stats) {
- GC_err_puts("Couldn't read MSDOS header from ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Couldn't read OS/2 header");
+ ABORT_ARG1("Could not read OS/2 header", " from: %s", path);
}
if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) {
- if (GC_print_stats) {
- GC_err_puts("Executable has wrong OS/2 magic number: ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Bad OS/2 magic number");
+ ABORT_ARG1("Bad OS/2 magic number", " in file: %s", path);
}
if (E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) {
- if (GC_print_stats) {
- GC_err_puts("Executable has wrong byte order: ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Bad byte order");
+ ABORT_ARG1("Bad byte order in executable", " file: %s", path);
}
if (E32_CPU(hdr386) == E32CPU286) {
- if (GC_print_stats) {
- GC_err_puts("GC can't handle 80286 executables: ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Intel 80286 executables are unsupported");
+ ABORT_ARG1("GC cannot handle 80286 executables", ": %s", path);
}
if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386),
SEEK_SET) != 0) {
- if (GC_print_stats) {
- GC_err_puts("Seek to object table failed: ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Seek to object table failed");
+ ABORT_ARG1("Seek to object table failed", " in file: %s", path);
}
for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) {
int flags;
if (fread((char *)(&seg), 1, sizeof(seg), myexefile) < sizeof(seg)) {
- if (GC_print_stats) {
- GC_err_puts("Couldn't read obj table entry from ");
- GC_err_puts(path);
- GC_err_puts("\n");
- }
- ABORT("Couldn't read obj table entry");
+ ABORT_ARG1("Could not read obj table entry", " from file: %s", path);
}
flags = O32_FLAGS(seg);
if (!(flags & OBJWRITE)) continue;
@@ -1625,19 +1600,19 @@ void GC_register_data_segments(void)
} else {
GetWriteWatch_alloc_flag = MEM_WRITE_WATCH;
}
- VirtualFree(page, GC_page_size, MEM_RELEASE);
+ VirtualFree(page, 0 /* dwSize */, MEM_RELEASE);
} else {
/* GetWriteWatch will be useless. */
GetWriteWatch_func = NULL;
}
}
- if (GC_print_stats) {
+# ifndef SMALL_CONFIG
if (GetWriteWatch_func == NULL) {
- GC_log_printf("Did not find a usable GetWriteWatch()\n");
+ GC_COND_LOG_PRINTF("Did not find a usable GetWriteWatch()\n");
} else {
- GC_log_printf("Using GetWriteWatch()\n");
+ GC_COND_LOG_PRINTF("Using GetWriteWatch()\n");
}
- }
+# endif
done = TRUE;
}
@@ -1694,7 +1669,7 @@ void GC_register_data_segments(void)
p = (ptr_t)((word)start & ~(GC_page_size - 1));
for (;;) {
q = (LPVOID)(p - GC_page_size);
- if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break;
+ if ((word)q > (word)p /* underflow */ || (word)q < (word)limit) break;
result = VirtualQuery(q, &buf, sizeof(buf));
if (result != sizeof(buf) || buf.AllocationBase == 0) break;
p = (ptr_t)(buf.AllocationBase);
@@ -1715,7 +1690,7 @@ void GC_register_data_segments(void)
STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */
-# ifndef CYGWIN32
+# ifdef USE_WINALLOC
/* In the long run, a better data structure would also be nice ... */
STATIC struct GC_malloc_heap_list {
void * allocation_base;
@@ -1771,14 +1746,13 @@ void GC_register_data_segments(void)
return;
}
}
- if (GC_print_stats)
- GC_log_printf("Found new system malloc AllocationBase at %p\n",
- candidate);
+ GC_COND_LOG_PRINTF("Found new system malloc AllocationBase at %p\n",
+ candidate);
new_l -> allocation_base = candidate;
new_l -> next = GC_malloc_heap_l;
GC_malloc_heap_l = new_l;
}
-# endif /* !CYGWIN32 */
+# endif /* USE_WINALLOC */
# endif /* !REDIRECT_MALLOC */
@@ -1791,7 +1765,7 @@ void GC_register_data_segments(void)
unsigned i;
# ifndef REDIRECT_MALLOC
if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size;
-# ifndef CYGWIN32
+# ifdef USE_WINALLOC
if (GC_is_malloc_heap_base(p)) return TRUE;
# endif
# endif
@@ -1813,7 +1787,7 @@ void GC_register_data_segments(void)
if (!GC_no_win32_dlls) return;
p = base = limit = GC_least_described_address(static_root);
- while (p < GC_sysinfo.lpMaximumApplicationAddress) {
+ while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) {
result = VirtualQuery(p, &buf, sizeof(buf));
if (result != sizeof(buf) || buf.AllocationBase == 0
|| GC_is_heap_base(buf.AllocationBase)) break;
@@ -1829,7 +1803,7 @@ void GC_register_data_segments(void)
limit = new_limit;
}
}
- if (p > (LPVOID)new_limit /* overflow */) break;
+ if ((word)p > (word)new_limit /* overflow */) break;
p = (LPVOID)new_limit;
}
if (base != limit) GC_add_roots_inner(base, limit, FALSE);
@@ -1839,8 +1813,8 @@ void GC_register_data_segments(void)
void GC_register_data_segments(void)
{
# ifdef MSWIN32
- static char dummy;
- GC_register_root_section((ptr_t)(&dummy));
+ GC_register_root_section((ptr_t)&GC_pages_executable);
+ /* any other GC global variable would fit too. */
# endif
}
@@ -1856,7 +1830,7 @@ void GC_register_data_segments(void)
word next_page = ((text_end + (word)max_page_size - 1)
& ~((word)max_page_size - 1));
word page_offset = (text_end & ((word)max_page_size - 1));
- volatile char * result = (char *)(next_page + page_offset);
+ char * volatile result = (char *)(next_page + page_offset);
/* Note that this isn't equivalent to just adding */
/* max_page_size to &etext if &etext is at a page boundary */
@@ -1931,7 +1905,7 @@ void GC_register_data_segments(void)
for (;;) {
region_end = GC_find_limit_openbsd(region_start, DATAEND);
GC_add_roots_inner(region_start, region_end, FALSE);
- if (region_end >= DATAEND)
+ if ((word)region_end >= (word)(DATAEND))
break;
region_start = GC_skip_hole_openbsd(region_end, DATAEND);
}
@@ -1951,7 +1925,11 @@ void GC_register_data_segments(void)
extern caddr_t sbrk(int);
GC_ASSERT(DATASTART);
- GC_add_roots_inner(DATASTART, (ptr_t)sbrk(0), FALSE);
+ {
+ ptr_t p = (ptr_t)sbrk(0);
+ if ((word)(DATASTART) < (word)p)
+ GC_add_roots_inner(DATASTART, p, FALSE);
+ }
# else
GC_ASSERT(DATASTART);
GC_add_roots_inner(DATASTART, (ptr_t)(DATAEND), FALSE);
@@ -1998,16 +1976,16 @@ void GC_register_data_segments(void)
/* change. */
}
-# endif /* ! AMIGA */
-# endif /* ! MSWIN32 && ! MSWINCE*/
-# endif /* ! OS2 */
+# endif /* !AMIGA */
+# endif /* !MSWIN32 && !MSWINCE */
+# endif /* !OS2 */
/*
* Auxiliary routines for obtaining memory from OS.
*/
-# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \
- && !defined(MSWINCE) && !defined(MACOS) && !defined(DOS4GW) \
+# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \
+ && !defined(USE_WINALLOC) && !defined(MACOS) && !defined(DOS4GW) \
&& !defined(NONSTOP) && !defined(SN_TARGET_PS3) && !defined(RTEMS) \
&& !defined(__CC_ARM)
@@ -2039,6 +2017,10 @@ void GC_register_data_segments(void)
# define HEAP_START ((ptr_t)0)
#endif
+#ifdef SYMBIAN
+ extern char* GC_get_private_path_and_zero_file(void);
+#endif
+
STATIC ptr_t GC_unix_mmap_get_mem(word bytes)
{
void *result;
@@ -2048,7 +2030,16 @@ STATIC ptr_t GC_unix_mmap_get_mem(word bytes)
static GC_bool initialized = FALSE;
if (!EXPECT(initialized, TRUE)) {
+# ifdef SYMBIAN
+ char* path = GC_get_private_path_and_zero_file();
+ zero_fd = open(path, O_RDWR | O_CREAT, 0666);
+ free(path);
+# else
zero_fd = open("/dev/zero", O_RDONLY);
+# endif
+ if (zero_fd == -1)
+ ABORT("Could not open /dev/zero");
+
fcntl(zero_fd, F_SETFD, FD_CLOEXEC);
initialized = TRUE;
}
@@ -2176,11 +2167,62 @@ void * os2_alloc(size_t bytes)
# endif /* OS2 */
-# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
- GC_INNER SYSTEM_INFO GC_sysinfo;
-# endif
+#ifdef MSWINCE
+ ptr_t GC_wince_get_mem(word bytes)
+ {
+ ptr_t result = 0; /* initialized to prevent warning. */
+ word i;
-#ifdef MSWIN32
+ /* Round up allocation size to multiple of page size */
+ bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
+
+ /* Try to find reserved, uncommitted pages */
+ for (i = 0; i < GC_n_heap_bases; i++) {
+ if (((word)(-(signed_word)GC_heap_lengths[i])
+ & (GC_sysinfo.dwAllocationGranularity-1))
+ >= bytes) {
+ result = GC_heap_bases[i] + GC_heap_lengths[i];
+ break;
+ }
+ }
+
+ if (i == GC_n_heap_bases) {
+ /* Reserve more pages */
+ word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
+ & ~(GC_sysinfo.dwAllocationGranularity-1);
+ /* If we ever support MPROTECT_VDB here, we will probably need to */
+ /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */
+ /* never spans regions. It seems to be OK for a VirtualFree */
+ /* argument to span regions, so we should be OK for now. */
+ result = (ptr_t) VirtualAlloc(NULL, res_bytes,
+ MEM_RESERVE | MEM_TOP_DOWN,
+ GC_pages_executable ? PAGE_EXECUTE_READWRITE :
+ PAGE_READWRITE);
+ if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
+ /* 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");
+ if (result == NULL) return NULL;
+ GC_heap_bases[GC_n_heap_bases] = result;
+ GC_heap_lengths[GC_n_heap_bases] = 0;
+ GC_n_heap_bases++;
+ }
+
+ /* Commit pages */
+ result = (ptr_t) VirtualAlloc(result, bytes, MEM_COMMIT,
+ GC_pages_executable ? PAGE_EXECUTE_READWRITE :
+ PAGE_READWRITE);
+# undef IGNORE_PAGES_EXECUTABLE
+
+ if (result != NULL) {
+ if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
+ GC_heap_lengths[i] += bytes;
+ }
+
+ return(result);
+ }
+
+#elif defined(USE_WINALLOC) || defined(CYGWIN32)
# ifdef USE_GLOBAL_ALLOC
# define GLOBAL_ALLOC_TEST 1
@@ -2188,32 +2230,32 @@ void * os2_alloc(size_t bytes)
# define GLOBAL_ALLOC_TEST GC_no_win32_dlls
# endif
-# ifdef GC_USE_MEM_TOP_DOWN
- STATIC DWORD GC_mem_top_down = MEM_TOP_DOWN;
+# if defined(GC_USE_MEM_TOP_DOWN) && defined(USE_WINALLOC)
+ DWORD GC_mem_top_down = MEM_TOP_DOWN;
/* Use GC_USE_MEM_TOP_DOWN for better 64-bit */
/* testing. Otherwise all addresses tend to */
/* end up in first 4GB, hiding bugs. */
# else
- STATIC DWORD GC_mem_top_down = 0;
-# endif
+# define GC_mem_top_down 0
+# endif /* !GC_USE_MEM_TOP_DOWN */
-#endif /* MSWIN32 */
-
-#if defined(MSWIN32) || defined(CYGWIN32)
ptr_t GC_win32_get_mem(word bytes)
{
ptr_t result;
-# ifdef CYGWIN32
+# ifndef USE_WINALLOC
result = GC_unix_get_mem(bytes);
# else
- if (GLOBAL_ALLOC_TEST) {
+# ifdef MSWIN32
+ if (GLOBAL_ALLOC_TEST) {
/* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE. */
/* There are also unconfirmed rumors of other */
/* problems, so we dodge the issue. */
result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE);
result = (ptr_t)(((word)result + HBLKSIZE - 1) & ~(HBLKSIZE-1));
- } else {
+ } else
+# endif
+ /* else */ {
/* VirtualProtect only works on regions returned by a */
/* single VirtualAlloc call. Thus we allocate one */
/* extra page, which will prevent merging of blocks */
@@ -2247,7 +2289,7 @@ void * os2_alloc(size_t bytes)
PAGE_READWRITE);
# undef IGNORE_PAGES_EXECUTABLE
}
-# endif /* !CYGWIN32 */
+# endif /* USE_WINALLOC */
if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
/* If I read the documentation correctly, this can */
/* only happen if HBLKSIZE > 64k or not a power of 2. */
@@ -2259,20 +2301,29 @@ void * os2_alloc(size_t bytes)
GC_API void GC_CALL GC_win32_free_heap(void)
{
# ifndef CYGWIN32
- if (GC_no_win32_dlls)
+ if (GLOBAL_ALLOC_TEST)
# endif
{
while (GC_n_heap_bases-- > 0) {
# ifdef CYGWIN32
- /* FIXME: Is it ok to use non-GC free() here? */
+ /* FIXME: Is it OK to use non-GC free() here? */
# else
GlobalFree(GC_heap_bases[GC_n_heap_bases]);
# endif
GC_heap_bases[GC_n_heap_bases] = 0;
}
- }
+ } /* else */
+# ifndef CYGWIN32
+ else {
+ /* Avoiding VirtualAlloc leak. */
+ while (GC_n_heap_bases > 0) {
+ VirtualFree(GC_heap_bases[--GC_n_heap_bases], 0, MEM_RELEASE);
+ GC_heap_bases[GC_n_heap_bases] = 0;
+ }
+ }
+# endif
}
-#endif /* MSWIN32 || CYGWIN32 */
+#endif /* USE_WINALLOC || CYGWIN32 */
#ifdef AMIGA
# define GC_AMIGA_AM
@@ -2280,63 +2331,6 @@ void * os2_alloc(size_t bytes)
# undef GC_AMIGA_AM
#endif
-
-#ifdef MSWINCE
- ptr_t GC_wince_get_mem(word bytes)
- {
- ptr_t result = 0; /* initialized to prevent warning. */
- word i;
-
- /* Round up allocation size to multiple of page size */
- bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1);
-
- /* Try to find reserved, uncommitted pages */
- for (i = 0; i < GC_n_heap_bases; i++) {
- if (((word)(-(signed_word)GC_heap_lengths[i])
- & (GC_sysinfo.dwAllocationGranularity-1))
- >= bytes) {
- result = GC_heap_bases[i] + GC_heap_lengths[i];
- break;
- }
- }
-
- if (i == GC_n_heap_bases) {
- /* Reserve more pages */
- word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1)
- & ~(GC_sysinfo.dwAllocationGranularity-1);
- /* If we ever support MPROTECT_VDB here, we will probably need to */
- /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */
- /* never spans regions. It seems to be OK for a VirtualFree */
- /* argument to span regions, so we should be OK for now. */
- result = (ptr_t) VirtualAlloc(NULL, res_bytes,
- MEM_RESERVE | MEM_TOP_DOWN,
- GC_pages_executable ? PAGE_EXECUTE_READWRITE :
- PAGE_READWRITE);
- if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
- /* 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");
- if (result == NULL) return NULL;
- GC_heap_bases[GC_n_heap_bases] = result;
- GC_heap_lengths[GC_n_heap_bases] = 0;
- GC_n_heap_bases++;
- }
-
- /* Commit pages */
- result = (ptr_t) VirtualAlloc(result, bytes, MEM_COMMIT,
- GC_pages_executable ? PAGE_EXECUTE_READWRITE :
- PAGE_READWRITE);
-# undef IGNORE_PAGES_EXECUTABLE
-
- if (result != NULL) {
- if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result");
- GC_heap_lengths[i] += bytes;
- }
-
- return(result);
- }
-#endif
-
#ifdef USE_MUNMAP
/* For now, this only works on Win32/WinCE and some Unix-like */
@@ -2344,12 +2338,10 @@ void * os2_alloc(size_t bytes)
/* USE_MUNMAP. */
#if !defined(MSWIN32) && !defined(MSWINCE)
-
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
+# include <unistd.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+# include <sys/types.h>
#endif
/* Compute a page aligned starting address for the unmap */
@@ -2360,7 +2352,7 @@ STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes)
ptr_t result;
/* Round start to next page boundary. */
result = (ptr_t)((word)(start + GC_page_size - 1) & ~(GC_page_size - 1));
- if (result + GC_page_size > start + bytes) return 0;
+ if ((word)(result + GC_page_size) > (word)(start + bytes)) return 0;
return result;
}
@@ -2387,11 +2379,13 @@ GC_INNER void GC_unmap(ptr_t start, size_t bytes)
ptr_t start_addr = GC_unmap_start(start, bytes);
ptr_t end_addr = GC_unmap_end(start, bytes);
word len = end_addr - start_addr;
+
if (0 == start_addr) return;
-# if defined(MSWIN32) || defined(MSWINCE)
+# ifdef USE_WINALLOC
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
GC_word free_len;
+
if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
!= sizeof(mem_info))
ABORT("Weird VirtualQuery result");
@@ -2407,6 +2401,7 @@ GC_INNER void GC_unmap(ptr_t start, size_t bytes)
/* accidentally grabbing the same address space. */
{
void * result;
+
result = mmap(start_addr, len, PROT_NONE,
MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
zero_fd, 0/* offset */);
@@ -2422,15 +2417,15 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes)
ptr_t start_addr = GC_unmap_start(start, bytes);
ptr_t end_addr = GC_unmap_end(start, bytes);
word len = end_addr - start_addr;
+ if (0 == start_addr) return;
/* FIXME: Handle out-of-memory correctly (at least for Win32) */
-# if defined(MSWIN32) || defined(MSWINCE)
- ptr_t result;
-
- if (0 == start_addr) return;
+# ifdef USE_WINALLOC
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
GC_word alloc_len;
+ ptr_t result;
+
if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
!= sizeof(mem_info))
ABORT("Weird VirtualQuery result");
@@ -2452,14 +2447,8 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes)
}
# else
/* It was already remapped with PROT_NONE. */
- int result;
- if (0 == start_addr) return;
-
-# ifndef NACL
- result = mprotect(start_addr, len, (PROT_READ | PROT_WRITE)
- | (GC_pages_executable ? PROT_EXEC : 0));
-# else
- {
+ {
+# ifdef NACL
/* NaCl does not expose mprotect, but mmap should work fine. */
void *mmap_result = mmap(start_addr, len, (PROT_READ | PROT_WRITE)
| (GC_pages_executable ? PROT_EXEC : 0),
@@ -2467,18 +2456,16 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes)
zero_fd, 0 /* offset */);
if (mmap_result != (void *)start_addr)
ABORT("mmap as mprotect failed");
- /* Fake the return value as if mprotect succeeded. */
- result = 0;
- }
-# endif /* NACL */
-# undef IGNORE_PAGES_EXECUTABLE
-
- if (result != 0) {
- if (GC_print_stats)
- GC_log_printf("Mprotect failed at %p (length %lu) with errno %d\n",
- start_addr, (unsigned long)len, errno);
- ABORT("mprotect remapping failed");
+# else
+ if (mprotect(start_addr, len, (PROT_READ | PROT_WRITE)
+ | (GC_pages_executable ? PROT_EXEC : 0)) != 0) {
+ ABORT_ARG3("mprotect remapping failed",
+ " at %p (length %lu), errcode= %d",
+ start_addr, (unsigned long)len, errno);
+ }
+# endif /* !NACL */
}
+# undef IGNORE_PAGES_EXECUTABLE
GC_unmapped_bytes -= len;
# endif
}
@@ -2496,15 +2483,17 @@ GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2,
ptr_t start_addr = end1_addr;
ptr_t end_addr = start2_addr;
size_t len;
+
GC_ASSERT(start1 + bytes1 == start2);
if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2);
if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2);
if (0 == start_addr) return;
len = end_addr - start_addr;
-# if defined(MSWIN32) || defined(MSWINCE)
+# ifdef USE_WINALLOC
while (len != 0) {
MEMORY_BASIC_INFORMATION mem_info;
GC_word free_len;
+
if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info))
!= sizeof(mem_info))
ABORT("Weird VirtualQuery result");
@@ -2535,7 +2524,7 @@ GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2,
/* environment, this is also responsible for marking from */
/* thread stacks. */
#ifndef THREADS
- GC_INNER void (*GC_push_other_roots)(void) = 0;
+ GC_push_other_roots_proc GC_push_other_roots = 0;
#else /* THREADS */
# ifdef PCR
@@ -2562,7 +2551,7 @@ PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data)
extern struct PCR_MM_ProcsRep * GC_old_allocator;
/* defined in pcr_interface.c. */
-STATIC void GC_default_push_other_roots(void)
+STATIC void GC_CALLBACK GC_default_push_other_roots(void)
{
/* Traverse data allocated by previous memory managers. */
if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false,
@@ -2581,14 +2570,14 @@ STATIC void GC_default_push_other_roots(void)
# endif /* PCR */
# if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS)
- STATIC void GC_default_push_other_roots(void)
+ STATIC void GC_CALLBACK GC_default_push_other_roots(void)
{
GC_push_all_stacks();
}
# endif /* GC_WIN32_THREADS || GC_PTHREADS */
# ifdef SN_TARGET_PS3
- STATIC void GC_default_push_other_roots(void)
+ STATIC void GC_CALLBACK GC_default_push_other_roots(void)
{
ABORT("GC_default_push_other_roots is not implemented");
}
@@ -2599,9 +2588,19 @@ STATIC void GC_default_push_other_roots(void)
}
# endif /* SN_TARGET_PS3 */
- GC_INNER void (*GC_push_other_roots)(void) = GC_default_push_other_roots;
+ GC_push_other_roots_proc GC_push_other_roots = GC_default_push_other_roots;
#endif /* THREADS */
+GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc fn)
+{
+ GC_push_other_roots = fn;
+}
+
+GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void)
+{
+ return GC_push_other_roots;
+}
+
/*
* Routines for accessing dirty bits on virtual pages.
* There are six ways to maintain this information:
@@ -2773,9 +2772,9 @@ STATIC void GC_default_push_other_roots(void)
while (pages != pages_end) {
struct hblk * h = (struct hblk *) *pages++;
struct hblk * h_end = (struct hblk *) ((char *) h + page_size);
- do
+ do {
set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h));
- while (++h < h_end);
+ } while ((word)(++h) < (word)h_end);
}
}
} while (count == GC_GWW_BUF_LEN);
@@ -2797,8 +2796,7 @@ STATIC void GC_default_push_other_roots(void)
/* Initialize virtual dirty bit implementation. */
GC_INNER void GC_dirty_init(void)
{
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Initializing DEFAULT_VDB...\n");
+ GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n");
GC_dirty_maintained = TRUE;
}
@@ -2843,8 +2841,7 @@ STATIC void GC_default_push_other_roots(void)
/* Initialize virtual dirty bit implementation. */
GC_INNER void GC_dirty_init(void)
{
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Initializing MANUAL_VDB...\n");
+ GC_VERBOSE_LOG_PRINTF("Initializing MANUAL_VDB...\n");
/* GC_dirty_pages and GC_grungy_pages are already cleared. */
GC_dirty_maintained = TRUE;
}
@@ -2903,13 +2900,13 @@ STATIC void GC_default_push_other_roots(void)
* SIGBUS or SIGSEGV. We assume no write faults occur in system calls.
* This means that clients must ensure that system calls don't write
* to the write-protected heap. Probably the best way to do this is to
- * ensure that system calls write at most to POINTERFREE objects in the
+ * ensure that system calls write at most to pointer-free objects in the
* heap, and do even that only if we are on a platform on which those
* are not protected. Another alternative is to wrap system calls
* (see example for read below), but the current implementation holds
* applications.
* We assume the page size is a multiple of HBLKSIZE.
- * We prefer them to be the same. We avoid protecting POINTERFREE
+ * We prefer them to be the same. We avoid protecting pointer-free
* objects only if they are the same.
*/
# ifdef DARWIN
@@ -2918,21 +2915,17 @@ STATIC void GC_default_push_other_roots(void)
# include <mach/vm_map.h>
STATIC mach_port_t GC_task_self = 0;
# define PROTECT(addr,len) \
- if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
- FALSE, VM_PROT_READ \
- | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \
- != KERN_SUCCESS) { \
- ABORT("vm_protect(PROTECT) failed"); \
- }
+ if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \
+ FALSE, VM_PROT_READ \
+ | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \
+ == KERN_SUCCESS) {} else ABORT("vm_protect(PROTECT) failed")
# define UNPROTECT(addr,len) \
- if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \
- FALSE, (VM_PROT_READ | VM_PROT_WRITE) \
- | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \
- != KERN_SUCCESS) { \
- ABORT("vm_protect(UNPROTECT) failed"); \
- }
+ if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \
+ FALSE, (VM_PROT_READ | VM_PROT_WRITE) \
+ | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \
+ == KERN_SUCCESS) {} else ABORT("vm_protect(UNPROTECT) failed")
-# elif !defined(MSWIN32) && !defined(MSWINCE)
+# elif !defined(USE_WINALLOC)
# include <sys/mman.h>
# include <signal.h>
# include <sys/syscall.h>
@@ -2940,42 +2933,38 @@ STATIC void GC_default_push_other_roots(void)
# define PROTECT(addr, len) \
if (mprotect((caddr_t)(addr), (size_t)(len), \
PROT_READ \
- | (GC_pages_executable ? PROT_EXEC : 0)) < 0) { \
- ABORT("mprotect failed"); \
- }
+ | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \
+ } else ABORT("mprotect failed")
# define UNPROTECT(addr, len) \
if (mprotect((caddr_t)(addr), (size_t)(len), \
(PROT_READ | PROT_WRITE) \
- | (GC_pages_executable ? PROT_EXEC : 0)) < 0) { \
- ABORT(GC_pages_executable ? "un-mprotect executable page" \
- " failed (probably disabled by OS)" : \
- "un-mprotect failed"); \
- }
+ | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \
+ } else ABORT(GC_pages_executable ? \
+ "un-mprotect executable page failed" \
+ " (probably disabled by OS)" : \
+ "un-mprotect failed")
# undef IGNORE_PAGES_EXECUTABLE
-# else /* MSWIN32 */
+# else /* USE_WINALLOC */
# ifndef MSWINCE
# include <signal.h>
# endif
static DWORD protect_junk;
# define PROTECT(addr, len) \
- if (!VirtualProtect((addr), (len), \
- GC_pages_executable ? PAGE_EXECUTE_READ : \
- PAGE_READONLY, \
- &protect_junk)) { \
- if (GC_print_stats) \
- GC_log_printf("Last error code: 0x%lx\n", (long)GetLastError()); \
- ABORT("VirtualProtect failed"); \
- }
+ if (VirtualProtect((addr), (len), \
+ GC_pages_executable ? PAGE_EXECUTE_READ : \
+ PAGE_READONLY, \
+ &protect_junk)) { \
+ } else ABORT_ARG1("VirtualProtect failed", \
+ ": errcode= 0x%X", (unsigned)GetLastError())
# define UNPROTECT(addr, len) \
- if (!VirtualProtect((addr), (len), \
- GC_pages_executable ? PAGE_EXECUTE_READWRITE : \
- PAGE_READWRITE, \
- &protect_junk)) { \
- ABORT("un-VirtualProtect failed"); \
- }
-# endif /* MSWIN32 || MSWINCE || DARWIN */
+ if (VirtualProtect((addr), (len), \
+ GC_pages_executable ? PAGE_EXECUTE_READWRITE : \
+ PAGE_READWRITE, \
+ &protect_junk)) { \
+ } else ABORT("un-VirtualProtect failed")
+# endif /* USE_WINALLOC */
# if defined(MSWIN32)
typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR;
@@ -3003,9 +2992,11 @@ STATIC void GC_default_push_other_roots(void)
/* Also old MSWIN32 ACCESS_VIOLATION filter */
# if !defined(MSWIN32) && !defined(MSWINCE)
STATIC SIG_HNDLR_PTR GC_old_bus_handler = 0;
- STATIC GC_bool GC_old_bus_handler_used_si = FALSE;
+# if defined(FREEBSD) || defined(HURD) || defined(HPUX)
+ STATIC GC_bool GC_old_bus_handler_used_si = FALSE;
+# endif
STATIC GC_bool GC_old_segv_handler_used_si = FALSE;
-# endif
+# endif /* !MSWIN32 */
#endif /* !DARWIN */
#if defined(THREADS)
@@ -3039,16 +3030,16 @@ STATIC void GC_default_push_other_roots(void)
/* fail than the old code, which had no reported failures. Thus we */
/* leave it this way while we think of something better, or support */
/* GC_test_and_set on the remaining platforms. */
- static volatile word currently_updating = 0;
+ static int * volatile currently_updating = 0;
static void async_set_pht_entry_from_index(volatile page_hash_table db,
size_t index)
{
- unsigned int update_dummy;
- currently_updating = (word)(&update_dummy);
+ int update_dummy;
+ currently_updating = &update_dummy;
set_pht_entry_from_index(db, index);
/* If we get contention in the 10 or so instruction window here, */
/* and we get stopped by a GC between the two updates, we lose! */
- if (currently_updating != (word)(&update_dummy)) {
+ if (currently_updating != &update_dummy) {
set_pht_entry_from_index_safe(db, index);
/* We claim that if two threads concurrently try to update the */
/* dirty bit vector, the first one to execute UPDATE_START */
@@ -3076,13 +3067,21 @@ STATIC void GC_default_push_other_roots(void)
# define SIG_OK (sig == SIGBUS || sig == SIGSEGV)
# else
# define SIG_OK (sig == SIGSEGV)
+ /* Catch SIGSEGV but ignore SIGBUS. */
# endif
# if defined(FREEBSD)
# ifndef SEGV_ACCERR
# define SEGV_ACCERR 2
# endif
-# define CODE_OK (si -> si_code == BUS_PAGE_FAULT \
- || si -> si_code == SEGV_ACCERR)
+# if defined(POWERPC)
+# define AIM /* Pretend that we're AIM. */
+# include <machine/trap.h>
+# define CODE_OK (si -> si_code == EXC_DSI \
+ || si -> si_code == SEGV_ACCERR)
+# else
+# define CODE_OK (si -> si_code == BUS_PAGE_FAULT \
+ || si -> si_code == SEGV_ACCERR)
+# endif
# elif defined(OSF1)
# define CODE_OK (si -> si_code == 2 /* experimentally determined */)
# elif defined(IRIX5)
@@ -3157,20 +3156,22 @@ STATIC void GC_default_push_other_roots(void)
# else
GC_bool used_si;
- if (sig == SIGSEGV) {
- old_handler = GC_old_segv_handler;
- used_si = GC_old_segv_handler_used_si;
- } else {
+# if defined(FREEBSD) || defined(HURD) || defined(HPUX)
+ if (sig == SIGBUS) {
old_handler = GC_old_bus_handler;
used_si = GC_old_bus_handler_used_si;
+ } else
+# endif
+ /* else */ {
+ old_handler = GC_old_segv_handler;
+ used_si = GC_old_segv_handler_used_si;
}
# endif
if (old_handler == (SIG_HNDLR_PTR)SIG_DFL) {
# if !defined(MSWIN32) && !defined(MSWINCE)
- if (GC_print_stats)
- GC_log_printf("Unexpected segfault at %p\n", addr);
- ABORT("Unexpected bus error or segmentation fault");
+ ABORT_ARG1("Unexpected bus error or segmentation fault",
+ " at %p", addr);
# else
return(EXCEPTION_CONTINUE_SEARCH);
# endif
@@ -3220,9 +3221,8 @@ STATIC void GC_default_push_other_roots(void)
# if defined(MSWIN32) || defined(MSWINCE)
return EXCEPTION_CONTINUE_SEARCH;
# else
- if (GC_print_stats)
- GC_log_printf("Unexpected segfault at %p\n", addr);
- ABORT("Unexpected bus error or segmentation fault");
+ ABORT_ARG1("Unexpected bus error or segmentation fault",
+ " at %p", addr);
# endif
}
@@ -3259,9 +3259,10 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
/* already marked dirty, and hence unprotected. */
return;
}
- for (current = h_trunc; current < h_end; ++current) {
+ for (current = h_trunc; (word)current < (word)h_end; ++current) {
size_t index = PHT_HASH(current);
- if (!is_ptrfree || current < h || current >= h + nblocks) {
+ if (!is_ptrfree || (word)current < (word)h
+ || (word)current >= (word)(h + nblocks)) {
async_set_pht_entry_from_index(GC_dirty_pages, index);
}
}
@@ -3272,25 +3273,26 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
GC_INNER void GC_dirty_init(void)
{
# if !defined(MSWIN32) && !defined(MSWINCE)
- struct sigaction act, oldact;
- act.sa_flags = SA_RESTART | SA_SIGINFO;
+ struct sigaction act, oldact;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
act.sa_sigaction = GC_write_fault_handler;
(void)sigemptyset(&act.sa_mask);
-# ifdef SIG_SUSPEND
- /* Arrange to postpone SIG_SUSPEND while we're in a write fault */
+# if defined(THREADS) && !defined(GC_OPENBSD_UTHREADS) \
+ && !defined(GC_WIN32_THREADS) && !defined(NACL)
+ /* Arrange to postpone the signal while we are in a write fault */
/* handler. This effectively makes the handler atomic w.r.t. */
/* stopping the world for GC. */
- (void)sigaddset(&act.sa_mask, SIG_SUSPEND);
-# endif /* SIG_SUSPEND */
-# endif
- if (GC_print_stats == VERBOSE)
- GC_log_printf(
+ (void)sigaddset(&act.sa_mask, GC_get_suspend_signal());
+# endif
+# endif /* !MSWIN32 */
+ GC_VERBOSE_LOG_PRINTF(
"Initializing mprotect virtual dirty bit implementation\n");
GC_dirty_maintained = TRUE;
if (GC_page_size % HBLKSIZE != 0) {
ABORT("Page size not multiple of HBLKSIZE");
}
# if !defined(MSWIN32) && !defined(MSWINCE)
+ /* act.sa_restorer is deprecated and should not be initialized. */
# if defined(GC_IRIX_THREADS)
sigaction(SIGSEGV, 0, &oldact);
sigaction(SIGSEGV, &act, 0);
@@ -3308,32 +3310,35 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
GC_old_segv_handler_used_si = FALSE;
}
if (GC_old_segv_handler == (SIG_HNDLR_PTR)SIG_IGN) {
- if (GC_print_stats)
- GC_err_printf("Previously ignored segmentation violation!?\n");
+ WARN("Previously ignored segmentation violation!?\n", 0);
GC_old_segv_handler = (SIG_HNDLR_PTR)SIG_DFL;
}
if (GC_old_segv_handler != (SIG_HNDLR_PTR)SIG_DFL) {
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Replaced other SIGSEGV handler\n");
+ GC_VERBOSE_LOG_PRINTF("Replaced other SIGSEGV handler\n");
}
# if defined(HPUX) || defined(LINUX) || defined(HURD) \
- || (defined(FREEBSD) && defined(SUNOS5SIGS))
+ || (defined(FREEBSD) && defined(SUNOS5SIGS))
sigaction(SIGBUS, &act, &oldact);
- if (oldact.sa_flags & SA_SIGINFO) {
+ if ((oldact.sa_flags & SA_SIGINFO) != 0) {
GC_old_bus_handler = oldact.sa_sigaction;
- GC_old_bus_handler_used_si = TRUE;
+# if !defined(LINUX)
+ GC_old_bus_handler_used_si = TRUE;
+# endif
} else {
GC_old_bus_handler = (SIG_HNDLR_PTR)oldact.sa_handler;
- GC_old_bus_handler_used_si = FALSE;
+# if !defined(LINUX)
+ GC_old_bus_handler_used_si = FALSE;
+# endif
}
if (GC_old_bus_handler == (SIG_HNDLR_PTR)SIG_IGN) {
- if (GC_print_stats)
- GC_err_printf("Previously ignored bus error!?\n");
- GC_old_bus_handler = (SIG_HNDLR_PTR)SIG_DFL;
- }
- if (GC_old_bus_handler != (SIG_HNDLR_PTR)SIG_DFL) {
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Replaced other SIGBUS handler\n");
+ WARN("Previously ignored bus error!?\n", 0);
+# if !defined(LINUX)
+ GC_old_bus_handler = (SIG_HNDLR_PTR)SIG_DFL;
+# else
+ /* GC_old_bus_handler is not used by GC_write_fault_handler. */
+# endif
+ } else if (GC_old_bus_handler != (SIG_HNDLR_PTR)SIG_DFL) {
+ GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n");
}
# endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */
# endif /* ! MS windows */
@@ -3344,8 +3349,7 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
# if defined(MSWIN32)
GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler);
if (GC_old_segv_handler != NULL) {
- if (GC_print_stats)
- GC_log_printf("Replaced other UnhandledExceptionFilter\n");
+ GC_COND_LOG_PRINTF("Replaced other UnhandledExceptionFilter\n");
} else {
GC_old_segv_handler = SIG_DFL;
}
@@ -3358,6 +3362,8 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks,
GC_API int GC_CALL GC_incremental_protection_needs(void)
{
+ GC_ASSERT(GC_is_initialized);
+
if (GC_page_size == HBLKSIZE) {
return GC_PROTECTS_POINTER_HEAP;
} else {
@@ -3389,7 +3395,7 @@ STATIC void GC_protect_heap(void)
GC_ASSERT(PAGE_ALIGNED(start));
current_start = current = (struct hblk *)start;
limit = (struct hblk *)(start + len);
- while (current < limit) {
+ while ((word)current < (word)limit) {
hdr * hhdr;
word nhblks;
GC_bool is_ptrfree;
@@ -3414,7 +3420,7 @@ STATIC void GC_protect_heap(void)
is_ptrfree = IS_PTRFREE(hhdr);
}
if (is_ptrfree) {
- if (current_start < current) {
+ if ((word)current_start < (word)current) {
PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
}
current_start = (current += nhblks);
@@ -3422,7 +3428,7 @@ STATIC void GC_protect_heap(void)
current += nhblks;
}
}
- if (current_start < current) {
+ if ((word)current_start < (word)current) {
PROTECT(current_start, (ptr_t)current - (ptr_t)current_start);
}
}
@@ -3430,7 +3436,7 @@ STATIC void GC_protect_heap(void)
}
/* We assume that either the world is stopped or its OK to lose dirty */
-/* bits while this is happenning (as in GC_enable_incremental). */
+/* bits while this is happening (as in GC_enable_incremental). */
GC_INNER void GC_read_dirty(void)
{
# if defined(GWW_VDB)
@@ -3503,7 +3509,7 @@ void GC_unprotect_range(ptr_t addr, word len)
start_block = (struct hblk *)((word)addr & ~(GC_page_size - 1));
end_block = (struct hblk *)((word)(addr + len - 1) & ~(GC_page_size - 1));
end_block += GC_page_size/HBLKSIZE - 1;
- for (h = start_block; h <= end_block; h++) {
+ for (h = start_block; (word)h <= (word)end_block; h++) {
register word index = PHT_HASH(h);
async_set_pht_entry_from_index(GC_dirty_pages, index);
@@ -3519,15 +3525,15 @@ void GC_unprotect_range(ptr_t addr, word len)
/* This still serves as sample code if you do want to wrap system calls.*/
#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP)
-/* Replacement for UNIX system call. */
-/* Other calls that write to the heap should be handled similarly. */
-/* Note that this doesn't work well for blocking reads: It will hold */
-/* the allocation lock for the entire duration of the call. Multithreaded */
-/* clients should really ensure that it won't block, either by setting */
-/* the descriptor nonblocking, or by calling select or poll first, to */
-/* make sure that input is available. */
-/* Another, preferred alternative is to ensure that system calls never */
-/* write to the protected heap (see above). */
+/* Replacement for UNIX system call. */
+/* Other calls that write to the heap should be handled similarly. */
+/* Note that this doesn't work well for blocking reads: It will hold */
+/* the allocation lock for the entire duration of the call. */
+/* Multi-threaded clients should really ensure that it won't block, */
+/* either by setting the descriptor non-blocking, or by calling select */
+/* or poll first, to make sure that input is available. */
+/* Another, preferred alternative is to ensure that system calls never */
+/* write to the protected heap (see above). */
# include <unistd.h>
# include <sys/uio.h>
ssize_t read(int fd, void *buf, size_t nbyte)
@@ -3627,13 +3633,13 @@ GC_INNER void GC_dirty_init(void)
if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) {
memset(GC_written_pages, 0xff, sizeof(page_hash_table));
- if (GC_print_stats == VERBOSE)
- GC_log_printf("Allocated bytes:%lu:all pages may have been written\n",
- (unsigned long)(GC_bytes_allocd
- + GC_bytes_allocd_before_gc));
+ GC_VERBOSE_LOG_PRINTF(
+ "Allocated %lu bytes: all pages may have been written\n",
+ (unsigned long)(GC_bytes_allocd + GC_bytes_allocd_before_gc));
}
- sprintf(buf, "/proc/%ld", (long)getpid());
+ (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid());
+ buf[sizeof(buf) - 1] = '\0';
fd = open(buf, O_RDONLY);
if (fd < 0) {
ABORT("/proc open failed");
@@ -3648,6 +3654,8 @@ GC_INNER void GC_dirty_init(void)
GC_dirty_maintained = TRUE;
GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size);
+ if (GC_proc_buf == NULL)
+ ABORT("Insufficient space for /proc read");
}
# define READ read
@@ -3668,10 +3676,9 @@ GC_INNER void GC_read_dirty(void)
/* Retry with larger buffer. */
word new_size = 2 * GC_proc_buf_size;
char *new_buf;
- if (GC_print_stats)
- GC_err_printf("/proc read failed: GC_proc_buf_size = %lu\n",
- (unsigned long)GC_proc_buf_size);
+ WARN("/proc read failed: GC_proc_buf_size = %" WARN_PRIdPTR "\n",
+ (signed_word)GC_proc_buf_size);
new_buf = GC_scratch_alloc(new_size);
if (new_buf != 0) {
GC_proc_buf = bufp = new_buf;
@@ -3691,7 +3698,6 @@ GC_INNER void GC_read_dirty(void)
# ifdef DEBUG_DIRTY_BITS
GC_log_printf("Proc VDB read: pr_nmap= %u, pr_npage= %lu\n",
nmaps, ((struct prpageheader *)bufp)->pr_npage);
-
# endif
bufp += sizeof(struct prpageheader);
for (i = 0; i < nmaps; i++) {
@@ -3707,14 +3713,15 @@ GC_INNER void GC_read_dirty(void)
bufp += sizeof(struct prasmap);
limit = vaddr + pagesize * npages;
- for (; vaddr < limit; vaddr += pagesize) {
+ for (; (word)vaddr < (word)limit; vaddr += pagesize) {
if ((*bufp++) & PG_MODIFIED) {
register struct hblk * h;
ptr_t next_vaddr = vaddr + pagesize;
# ifdef DEBUG_DIRTY_BITS
GC_log_printf("dirty page at: %p\n", vaddr);
# endif
- for (h = (struct hblk *)vaddr; (ptr_t)h < next_vaddr; h++) {
+ for (h = (struct hblk *)vaddr;
+ (word)h < (word)next_vaddr; h++) {
register word index = PHT_HASH(h);
set_pht_entry_from_index(GC_grungy_pages, index);
}
@@ -3780,8 +3787,9 @@ GC_INNER void GC_read_dirty(void)
GC_INNER GC_bool GC_page_was_dirty(struct hblk *h)
{
- if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) {
- return(TRUE);
+ if ((word)h < (word)GC_vd_base
+ || (word)h >= (word)(GC_vd_base + NPAGES*HBLKSIZE)) {
+ return(TRUE);
}
return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit);
}
@@ -3892,6 +3900,9 @@ STATIC struct {
(void (*)(void))catch_exception_raise_state,
(void (*)(void))catch_exception_raise_state_identity
},
+# ifdef THREADS
+ 0, /* for 'exception' */
+# endif
0
};
@@ -3906,9 +3917,8 @@ typedef enum {
} GC_mprotect_state_t;
#ifdef THREADS
- /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, */
- /* but it isn't documented. Use the source and see if they */
- /* should be ok. */
+ /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, but it */
+ /* is not documented. Use the source and see if they should be OK. */
# define ID_STOP 1
# define ID_RESUME 2
@@ -4024,10 +4034,8 @@ STATIC void *GC_mprotect_thread(void *arg)
# endif /* THREADS */
if (r != MACH_MSG_SUCCESS) {
- if (GC_print_stats)
- GC_log_printf("mach_msg failed with code %d: %s\n", (int)r,
- mach_error_string(r));
- ABORT("mach_msg failed");
+ ABORT_ARG2("mach_msg failed",
+ ": errcode= %d (%s)", (int)r, mach_error_string(r));
}
switch(id) {
@@ -4101,9 +4109,22 @@ GC_INNER void GC_dirty_init(void)
pthread_attr_t attr;
exception_mask_t mask;
- if (GC_print_stats == VERBOSE)
- GC_log_printf(
- "Initializing mach/darwin mprotect virtual dirty bit implementation\n");
+# ifdef CAN_HANDLE_FORK
+ if (GC_handle_fork) {
+ /* To both support GC incremental mode and GC functions usage in */
+ /* the forked child, pthread_atfork should be used to install */
+ /* handlers that switch off GC_dirty_maintained in the child */
+ /* gracefully (unprotecting all pages and clearing */
+ /* GC_mach_handler_thread). For now, we just disable incremental */
+ /* mode if fork() handling is requested by the client. */
+ GC_COND_LOG_PRINTF("GC incremental mode disabled since fork()"
+ " handling requested\n");
+ return;
+ }
+# endif
+
+ GC_VERBOSE_LOG_PRINTF("Initializing mach/darwin mprotect"
+ " virtual dirty bit implementation\n");
# ifdef BROKEN_EXCEPTION_HANDLING
WARN("Enabling workarounds for various darwin "
"exception handling bugs.\n", 0);
@@ -4162,11 +4183,11 @@ GC_INNER void GC_dirty_init(void)
sa.sa_handler = (SIG_HNDLR_PTR)GC_darwin_sigbus;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART|SA_SIGINFO;
+ /* sa.sa_restorer is deprecated and should not be initialized. */
if (sigaction(SIGBUS, &sa, &oldsa) < 0)
ABORT("sigaction failed");
if ((SIG_HNDLR_PTR)oldsa.sa_handler != SIG_DFL) {
- if (GC_print_stats == VERBOSE)
- GC_err_printf("Replaced other SIGBUS handler\n");
+ GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n");
}
}
# endif /* BROKEN_EXCEPTION_HANDLING */
@@ -4339,7 +4360,7 @@ catch_exception_raise(mach_port_t exception_port GC_ATTR_UNUSED,
/* Can't pass it along to the signal handler because that is */
/* ignoring SIGBUS signals. We also shouldn't call ABORT here as */
/* signals don't always work too well from the exception handler. */
- exit(EXIT_FAILURE);
+ EXIT();
# else /* BROKEN_EXCEPTION_HANDLING */
/* Pass it along to the next exception handler
(which should call SIGBUS/SIGSEGV) */
@@ -4548,8 +4569,9 @@ GC_INNER void GC_save_callers(struct callinfo info[NFRAMES])
fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS);
#endif
- for (; (!(fp HOTTER_THAN frame) && !(GC_stackbottom HOTTER_THAN (ptr_t)fp)
- && (nframes < NFRAMES));
+ for (; !((word)fp HOTTER_THAN (word)frame)
+ && !((word)GC_stackbottom HOTTER_THAN (word)fp)
+ && nframes < NFRAMES;
fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) {
register int i;
@@ -4618,7 +4640,8 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
# else
char buf[40];
char *name = buf;
- sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc);
+ (void)snprintf(buf, sizeof(buf), "##PC##= 0x%lx", info[i].ci_pc);
+ buf[sizeof(buf) - 1] = '\0';
# endif
# if defined(LINUX) && !defined(SMALL_CONFIG)
/* Try for a line number. */
@@ -4653,15 +4676,18 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
/* Then we use popen to start addr2line -e <exe> <addr> */
/* There are faster ways to do this, but hopefully this */
/* isn't time critical. */
- sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name,
- (unsigned long)info[i].ci_pc);
+ (void)snprintf(cmd_buf, sizeof(cmd_buf),
+ "/usr/bin/addr2line -f -e %s 0x%lx",
+ exe_name, (unsigned long)info[i].ci_pc);
+ cmd_buf[sizeof(cmd_buf) - 1] = '\0';
old_preload = GETENV("LD_PRELOAD");
if (0 != old_preload) {
- if (strlen (old_preload) >= PRELOAD_SZ) {
+ size_t old_len = strlen(old_preload);
+ if (old_len >= PRELOAD_SZ) {
will_fail = TRUE;
goto out;
}
- strcpy (preload_buf, old_preload);
+ BCOPY(old_preload, preload_buf, old_len + 1);
unsetenv ("LD_PRELOAD");
}
pipe = popen(cmd_buf, "r");
@@ -4670,8 +4696,8 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
WARN("Failed to reset LD_PRELOAD\n", 0);
}
if (pipe == NULL
- || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe))
- == 0) {
+ || (result_len = fread(result_buf, 1,
+ RESULT_SZ - 1, pipe)) == 0) {
if (pipe != NULL) pclose(pipe);
will_fail = TRUE;
goto out;
@@ -4687,7 +4713,8 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
/* Get rid of embedded newline, if any. Test for "main" */
{
char * nl = strchr(result_buf, '\n');
- if (nl != NULL && nl < result_buf + result_len) {
+ if (nl != NULL
+ && (word)nl < (word)(result_buf + result_len)) {
*nl = ':';
}
if (strncmp(result_buf, "main", nl - result_buf) == 0) {
@@ -4696,8 +4723,10 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
}
if (result_len < RESULT_SZ - 25) {
/* Add in hex address */
- sprintf(result_buf + result_len, " [0x%lx]",
- (unsigned long)info[i].ci_pc);
+ (void)snprintf(&result_buf[result_len],
+ sizeof(result_buf) - result_len,
+ " [0x%lx]", (unsigned long)info[i].ci_pc);
+ result_buf[sizeof(result_buf) - 1] = '\0';
}
name = result_buf;
pclose(pipe);
@@ -4723,8 +4752,11 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES])
/* addresses in FIND_LEAK output. */
void GC_print_address_map(void)
{
+ char *maps;
+
GC_err_printf("---------- Begin address map ----------\n");
- GC_err_puts(GC_get_maps());
+ maps = GC_get_maps();
+ GC_err_puts(maps != NULL ? maps : "Failed to get map!\n");
GC_err_printf("---------- End address map ----------\n");
}
#endif /* LINUX && ELF */
diff --git a/pcr_interface.c b/pcr_interface.c
index a359b2da..c6c868c3 100644
--- a/pcr_interface.c
+++ b/pcr_interface.c
@@ -41,7 +41,7 @@ void * GC_DebugAllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear )
{
if (ptrFree) {
void * result = (void *)GC_debug_malloc_atomic(size, __FILE__,
- __LINE__);
+ __LINE__);
if (clear && result != 0) BZERO(result, size);
return(result);
} else {
@@ -79,7 +79,7 @@ void GC_enumerate_block(struct hblk *h; enumerate_data * ed)
descr = hhdr -> hb_descr;
sz = hhdr -> hb_sz;
if (descr != 0 && ed -> ed_pointerfree
- || descr == 0 && !(ed -> ed_pointerfree)) return;
+ || descr == 0 && !(ed -> ed_pointerfree)) return;
lim = (ptr_t)(h+1) - sz;
p = (ptr_t)h;
do {
@@ -87,7 +87,7 @@ void GC_enumerate_block(struct hblk *h; enumerate_data * ed)
ed -> ed_fail_code =
(*(ed -> ed_proc))(p, sz, ed -> ed_client_data);
p+= sz;
- } while (p <= lim);
+ } while ((word)p <= (word)lim);
}
struct PCR_MM_ProcsRep * GC_old_allocator = 0;
@@ -108,8 +108,8 @@ PCR_ERes GC_EnumerateProc(
if (ed.ed_fail_code != PCR_ERes_okay) {
return(ed.ed_fail_code);
} else {
- /* Also enumerate objects allocated by my predecessors */
- return((*(GC_old_allocator->mmp_enumerate))(ptrFree, proc, data));
+ /* Also enumerate objects allocated by my predecessors */
+ return((*(GC_old_allocator->mmp_enumerate))(ptrFree, proc, data));
}
}
@@ -118,23 +118,23 @@ void GC_DummyFreeProc(void *p) {}
void GC_DummyShutdownProc(void) {}
struct PCR_MM_ProcsRep GC_Rep = {
- MY_MAGIC,
- GC_AllocProc,
- GC_ReallocProc,
- GC_DummyFreeProc, /* mmp_free */
- GC_FreeProc, /* mmp_unsafeFree */
- GC_EnumerateProc,
- GC_DummyShutdownProc /* mmp_shutdown */
+ MY_MAGIC,
+ GC_AllocProc,
+ GC_ReallocProc,
+ GC_DummyFreeProc, /* mmp_free */
+ GC_FreeProc, /* mmp_unsafeFree */
+ GC_EnumerateProc,
+ GC_DummyShutdownProc /* mmp_shutdown */
};
struct PCR_MM_ProcsRep GC_DebugRep = {
- MY_DEBUGMAGIC,
- GC_DebugAllocProc,
- GC_DebugReallocProc,
- GC_DummyFreeProc, /* mmp_free */
- GC_DebugFreeProc, /* mmp_unsafeFree */
- GC_EnumerateProc,
- GC_DummyShutdownProc /* mmp_shutdown */
+ MY_DEBUGMAGIC,
+ GC_DebugAllocProc,
+ GC_DebugReallocProc,
+ GC_DummyFreeProc, /* mmp_free */
+ GC_DebugFreeProc, /* mmp_unsafeFree */
+ GC_EnumerateProc,
+ GC_DummyShutdownProc /* mmp_shutdown */
};
GC_bool GC_use_debug = 0;
@@ -163,9 +163,9 @@ PCR_GC_Run(void)
* awful hack to test whether VD is implemented ...
*/
if( PCR_VD_Start( 0, NIL, 0) != PCR_ERes_FromErr(ENOSYS) ) {
- GC_enable_incremental();
- }
- }
+ GC_enable_incremental();
+ }
+ }
}
return PCR_ERes_okay;
}
diff --git a/pthread_start.c b/pthread_start.c
index 776a368d..bd4fc4a0 100644
--- a/pthread_start.c
+++ b/pthread_start.c
@@ -42,7 +42,8 @@
#include <sched.h>
/* Invoked from GC_start_routine(). */
-void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg)
+GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine(
+ struct GC_stack_base *sb, void *arg)
{
void * (*start)(void *);
void * start_arg;
@@ -54,7 +55,7 @@ void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg)
pthread_cleanup_push(GC_thread_exit_proc, me);
# endif
result = (*start)(start_arg);
-# ifdef DEBUG_THREADS
+# if defined(DEBUG_THREADS) && !defined(GC_PTHREAD_START_STANDALONE)
GC_log_printf("Finishing thread %p\n", (void *)pthread_self());
# endif
me -> status = result;
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 26326842..3e629297 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -35,7 +35,11 @@ GC_INNER __thread GC_thread GC_nacl_gc_thread_self = NULL;
int GC_nacl_thread_parked[MAX_NACL_GC_THREADS];
int GC_nacl_thread_used[MAX_NACL_GC_THREADS];
-#elif !defined(GC_OPENBSD_THREADS)
+#elif defined(GC_OPENBSD_UTHREADS)
+
+# include <pthread_np.h>
+
+#else /* !GC_OPENBSD_UTHREADS && !NACL */
#include <signal.h>
#include <semaphore.h>
@@ -66,12 +70,10 @@ int GC_nacl_thread_used[MAX_NACL_GC_THREADS];
if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
ABORT("pthread_sigmask failed");
- GC_printf("Blocked: ");
for (i = 1; i < NSIG; i++) {
if (sigismember(&blocked, i))
- GC_printf("%d ", i);
+ GC_printf("Signal blocked: %d\n", i);
}
- GC_printf("\n");
}
#endif /* DEBUG_THREADS */
@@ -83,7 +85,7 @@ STATIC void GC_remove_allowed_signals(sigset_t *set)
|| sigdelset(set, SIGQUIT) != 0
|| sigdelset(set, SIGABRT) != 0
|| sigdelset(set, SIGTERM) != 0) {
- ABORT("sigdelset() failed");
+ ABORT("sigdelset failed");
}
# ifdef MPROTECT_VDB
@@ -94,7 +96,7 @@ STATIC void GC_remove_allowed_signals(sigset_t *set)
|| sigdelset(set, SIGBUS) != 0
# endif
) {
- ABORT("sigdelset() failed");
+ ABORT("sigdelset failed");
}
# endif
}
@@ -141,6 +143,33 @@ STATIC volatile AO_t GC_world_is_stopped = FALSE;
# endif
#endif
+STATIC int GC_sig_suspend = SIG_SUSPEND;
+STATIC int GC_sig_thr_restart = SIG_THR_RESTART;
+
+GC_API void GC_CALL GC_set_suspend_signal(int sig)
+{
+ if (GC_is_initialized) return;
+
+ GC_sig_suspend = sig;
+}
+
+GC_API void GC_CALL GC_set_thr_restart_signal(int sig)
+{
+ if (GC_is_initialized) return;
+
+ GC_sig_thr_restart = sig;
+}
+
+GC_API int GC_CALL GC_get_suspend_signal(void)
+{
+ return GC_sig_suspend;
+}
+
+GC_API int GC_CALL GC_get_thr_restart_signal(void)
+{
+ return GC_sig_thr_restart;
+}
+
#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
/* Some targets (eg., Solaris) might require this to be called when */
/* doing thread registering from the thread destructor. */
@@ -148,8 +177,8 @@ STATIC volatile AO_t GC_world_is_stopped = FALSE;
{
sigset_t set;
sigemptyset(&set);
- sigaddset(&set, SIG_SUSPEND);
- sigaddset(&set, SIG_THR_RESTART);
+ sigaddset(&set, GC_sig_suspend);
+ sigaddset(&set, GC_sig_thr_restart);
if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0)
ABORT("pthread_sigmask failed");
}
@@ -196,7 +225,7 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg,
IF_CANCEL(int cancel_state;)
AO_t my_stop_count = AO_load(&GC_stop_count);
- if ((signed_word)sig_arg != SIG_SUSPEND)
+ if ((signed_word)sig_arg != GC_sig_suspend)
ABORT("Bad signal in suspend_handler");
DISABLE_CANCEL(cancel_state);
@@ -228,7 +257,7 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg,
# ifdef SPARC
me -> stop_info.stack_ptr = GC_save_regs_in_stack();
# else
- me -> stop_info.stack_ptr = (ptr_t)(&me);
+ me -> stop_info.stack_ptr = GC_approx_sp();
# endif
# ifdef IA64
me -> backing_store_ptr = GC_save_regs_in_stack();
@@ -241,10 +270,9 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg,
me -> stop_info.last_stop_count = my_stop_count;
/* Wait until that thread tells us to restart by sending */
- /* this thread a SIG_THR_RESTART signal. */
- /* SIG_THR_RESTART should be masked at this point. Thus */
- /* there is no race. */
- /* We do not continue until we receive a SIG_THR_RESTART, */
+ /* this thread a GC_sig_thr_restart signal (should be masked */
+ /* at this point thus there is no race). */
+ /* We do not continue until we receive that signal, */
/* but we do not take that as authoritative. (We may be */
/* accidentally restarted by one of the user signals we */
/* don't block.) After we receive the signal, we use a */
@@ -274,7 +302,8 @@ STATIC void GC_restart_handler(int sig)
int old_errno = errno; /* Preserve errno value. */
# endif
- if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
+ if (sig != GC_sig_thr_restart)
+ ABORT("Bad signal in suspend_handler");
# ifdef GC_NETBSD_THREADS_WORKAROUND
sem_post(&GC_restart_ack_sem);
@@ -296,7 +325,7 @@ STATIC void GC_restart_handler(int sig)
# endif
}
-#endif /* !GC_OPENBSD_THREADS && !NACL */
+#endif /* !GC_OPENBSD_UTHREADS && !NACL */
#ifdef IA64
# define IF_IA64(x) x
@@ -314,6 +343,7 @@ GC_INNER void GC_push_all_stacks(void)
ptr_t lo, hi;
/* On IA64, we also need to scan the register backing store. */
IF_IA64(ptr_t bs_lo; ptr_t bs_hi;)
+ struct GC_traced_stack_sect_s *traced_stack_sect;
pthread_t self = pthread_self();
word total_size = 0;
@@ -326,6 +356,7 @@ GC_INNER void GC_push_all_stacks(void)
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> flags & FINISHED) continue;
++nthreads;
+ traced_stack_sect = p -> traced_stack_sect;
if (THREAD_EQUAL(p -> id, self)) {
GC_ASSERT(!p->thread_blocked);
# ifdef SPARC
@@ -338,6 +369,13 @@ GC_INNER void GC_push_all_stacks(void)
} else {
lo = p -> stop_info.stack_ptr;
IF_IA64(bs_hi = p -> backing_store_ptr;)
+ if (traced_stack_sect != NULL
+ && traced_stack_sect->saved_stack_ptr == lo) {
+ /* If the thread has never been stopped since the recent */
+ /* GC_call_with_gc_active invocation then skip the top */
+ /* "stack section" as stack_ptr already points to. */
+ traced_stack_sect = traced_stack_sect->prev;
+ }
}
if ((p -> flags & MAIN_THREAD) == 0) {
hi = p -> stack_end;
@@ -352,7 +390,7 @@ GC_INNER void GC_push_all_stacks(void)
(void *)p->id, lo, hi);
# endif
if (0 == lo) ABORT("GC_push_all_stacks: sp not set!");
- GC_push_all_stack_sections(lo, hi, p -> traced_stack_sect);
+ GC_push_all_stack_sections(lo, hi, traced_stack_sect);
# ifdef STACK_GROWS_UP
total_size += lo - hi;
# else
@@ -373,14 +411,12 @@ GC_INNER void GC_push_all_stacks(void)
/* entries, and hence overflow the mark stack, which is bad. */
GC_push_all_register_sections(bs_lo, bs_hi,
THREAD_EQUAL(p -> id, self),
- p -> traced_stack_sect);
+ traced_stack_sect);
total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */
# endif
}
}
- if (GC_print_stats == VERBOSE) {
- GC_log_printf("Pushed %d thread stacks\n", (int)nthreads);
- }
+ GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", (int)nthreads);
if (!found_me && !GC_in_thread_creation)
ABORT("Collecting from unknown thread");
GC_total_stacksize = total_size;
@@ -421,7 +457,7 @@ STATIC int GC_suspend_all(void)
# ifndef NACL
GC_thread p;
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
int result;
# endif
pthread_t self = pthread_self();
@@ -435,7 +471,7 @@ STATIC int GC_suspend_all(void)
if (!THREAD_EQUAL(p -> id, self)) {
if (p -> flags & FINISHED) continue;
if (p -> thread_blocked) /* Will wait */ continue;
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
if (p -> stop_info.last_stop_count == GC_stop_count) continue;
n_live_threads++;
# endif
@@ -443,21 +479,20 @@ STATIC int GC_suspend_all(void)
GC_log_printf("Sending suspend signal to %p\n", (void *)p->id);
# endif
-# ifdef GC_OPENBSD_THREADS
- if (pthread_suspend_np(p -> id) != 0)
- ABORT("pthread_suspend_np failed");
- /* This will only work for userland pthreads. It will */
- /* fail badly on rthreads. Perhaps we should consider */
- /* a pthread_sp_np() function that returns the stack */
- /* pointer for a suspended thread and implement in both */
- /* pthreads and rthreads. */
- p -> stop_info.stack_ptr =
- *(ptr_t *)((char *)p -> id + UTHREAD_SP_OFFSET);
+# ifdef GC_OPENBSD_UTHREADS
+ {
+ stack_t stack;
+ if (pthread_suspend_np(p -> id) != 0)
+ ABORT("pthread_suspend_np failed");
+ if (pthread_stackseg_np(p->id, &stack))
+ ABORT("pthread_stackseg_np failed");
+ p -> stop_info.stack_ptr = (ptr_t)stack.ss_sp - stack.ss_size;
+ }
# else
# ifndef PLATFORM_ANDROID
- result = pthread_kill(p -> id, SIG_SUSPEND);
+ result = pthread_kill(p -> id, GC_sig_suspend);
# else
- result = android_thread_kill(p -> kernel_id, SIG_SUSPEND);
+ result = android_thread_kill(p -> kernel_id, GC_sig_suspend);
# endif
switch(result) {
case ESRCH:
@@ -467,7 +502,8 @@ STATIC int GC_suspend_all(void)
case 0:
break;
default:
- ABORT("pthread_kill failed");
+ ABORT_ARG1("pthread_kill failed at suspend",
+ ": errcode= %d", result);
}
# endif
}
@@ -478,6 +514,9 @@ STATIC int GC_suspend_all(void)
# ifndef NACL_PARK_WAIT_NANOSECONDS
# define NACL_PARK_WAIT_NANOSECONDS (100 * 1000)
# endif
+# define NANOS_PER_SECOND (1000UL * 1000 * 1000)
+ unsigned long num_sleeps = 0;
+
# ifdef DEBUG_THREADS
GC_log_printf("pthread_stop_world: num_threads %d\n",
GC_nacl_num_gc_threads - 1);
@@ -515,6 +554,12 @@ STATIC int GC_suspend_all(void)
# endif
/* This requires _POSIX_TIMERS feature. */
nanosleep(&ts, 0);
+ if (++num_sleeps > NANOS_PER_SECOND / NACL_PARK_WAIT_NANOSECONDS) {
+ WARN("GC appears stalled waiting for %" WARN_PRIdPTR
+ " threads to park...\n",
+ GC_nacl_num_gc_threads - num_threads_parked - 1);
+ num_sleeps = 0;
+ }
}
# endif /* NACL */
return n_live_threads;
@@ -522,7 +567,7 @@ STATIC int GC_suspend_all(void)
GC_INNER void GC_stop_world(void)
{
-# if !defined(GC_OPENBSD_THREADS) && !defined(NACL)
+# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL)
int i;
int n_live_threads;
int code;
@@ -544,7 +589,7 @@ GC_INNER void GC_stop_world(void)
}
# endif /* PARALLEL_MARK */
-# if defined(GC_OPENBSD_THREADS) || defined(NACL)
+# if defined(GC_OPENBSD_UTHREADS) || defined(NACL)
(void)GC_suspend_all();
# else
AO_store(&GC_stop_count, GC_stop_count+1);
@@ -564,9 +609,7 @@ GC_INNER void GC_stop_world(void)
if (wait_usecs > RETRY_INTERVAL) {
int newly_sent = GC_suspend_all();
- if (GC_print_stats) {
- GC_log_printf("Resent %d signals after timeout\n", newly_sent);
- }
+ GC_COND_LOG_PRINTF("Resent %d signals after timeout\n", newly_sent);
sem_getvalue(&GC_suspend_ack_sem, &ack_count);
if (newly_sent < n_live_threads - ack_count) {
WARN("Lost some threads during GC_stop_world?!\n",0);
@@ -581,7 +624,8 @@ GC_INNER void GC_stop_world(void)
for (i = 0; i < n_live_threads; i++) {
retry:
- if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+ code = sem_wait(&GC_suspend_ack_sem);
+ if (0 != code) {
/* On Linux, sem_wait is documented to always return zero. */
/* But the documentation appears to be incorrect. */
if (errno == EINTR) {
@@ -640,10 +684,9 @@ GC_INNER void GC_stop_world(void)
GC_API_OSCALL void nacl_pre_syscall_hook(void)
{
- int local_dummy = 0;
if (GC_nacl_thread_idx != -1) {
NACL_STORE_REGS();
- GC_nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy);
+ GC_nacl_gc_thread_self->stop_info.stack_ptr = GC_approx_sp();
GC_nacl_thread_parked[GC_nacl_thread_idx] = 1;
}
}
@@ -652,7 +695,6 @@ GC_INNER void GC_stop_world(void)
{
if (GC_nacl_park_threads_now) {
pthread_t self = pthread_self();
- int local_dummy = 0;
/* Don't try to park the thread parker. */
if (GC_nacl_thread_parker == self)
@@ -667,7 +709,7 @@ GC_INNER void GC_stop_world(void)
/* so don't bother storing registers again, the GC has a set. */
if (!GC_nacl_thread_parked[GC_nacl_thread_idx]) {
NACL_STORE_REGS();
- GC_nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy);
+ GC_nacl_gc_thread_self->stop_info.stack_ptr = GC_approx_sp();
}
GC_nacl_thread_parked[GC_nacl_thread_idx] = 1;
while (GC_nacl_park_threads_now) {
@@ -694,9 +736,12 @@ GC_INNER void GC_stop_world(void)
STATIC GC_bool GC_nacl_thread_parking_inited = FALSE;
STATIC pthread_mutex_t GC_nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER;
+ extern void nacl_register_gc_hooks(void (*pre)(void), void (*post)(void));
+
GC_INNER void GC_nacl_initialize_gc_thread(void)
{
int i;
+ nacl_register_gc_hooks(nacl_pre_syscall_hook, nacl_post_syscall_hook);
pthread_mutex_lock(&GC_nacl_thread_alloc_lock);
if (!EXPECT(GC_nacl_thread_parking_inited, TRUE)) {
BZERO(GC_nacl_thread_parked, sizeof(GC_nacl_thread_parked));
@@ -736,7 +781,7 @@ GC_INNER void GC_start_world(void)
pthread_t self = pthread_self();
register int i;
register GC_thread p;
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
register int n_live_threads = 0;
register int result;
# endif
@@ -748,7 +793,7 @@ GC_INNER void GC_start_world(void)
GC_log_printf("World starting\n");
# endif
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
AO_store(&GC_world_is_stopped, FALSE);
# endif
for (i = 0; i < THREAD_TABLE_SZ; i++) {
@@ -756,21 +801,22 @@ GC_INNER void GC_start_world(void)
if (!THREAD_EQUAL(p -> id, self)) {
if (p -> flags & FINISHED) continue;
if (p -> thread_blocked) continue;
-# ifndef GC_OPENBSD_THREADS
+# ifndef GC_OPENBSD_UTHREADS
n_live_threads++;
# endif
# ifdef DEBUG_THREADS
GC_log_printf("Sending restart signal to %p\n", (void *)p->id);
# endif
-# ifdef GC_OPENBSD_THREADS
+# ifdef GC_OPENBSD_UTHREADS
if (pthread_resume_np(p -> id) != 0)
ABORT("pthread_resume_np failed");
# else
# ifndef PLATFORM_ANDROID
- result = pthread_kill(p -> id, SIG_THR_RESTART);
+ result = pthread_kill(p -> id, GC_sig_thr_restart);
# else
- result = android_thread_kill(p -> kernel_id, SIG_THR_RESTART);
+ result = android_thread_kill(p -> kernel_id,
+ GC_sig_thr_restart);
# endif
switch(result) {
case ESRCH:
@@ -780,7 +826,8 @@ GC_INNER void GC_start_world(void)
case 0:
break;
default:
- ABORT("pthread_kill failed");
+ ABORT_ARG1("pthread_kill failed at resume",
+ ": errcode= %d", result);
}
# endif
}
@@ -790,9 +837,8 @@ GC_INNER void GC_start_world(void)
for (i = 0; i < n_live_threads; i++) {
while (0 != (code = sem_wait(&GC_restart_ack_sem))) {
if (errno != EINTR) {
- if (GC_print_stats)
- GC_log_printf("sem_wait() returned %d\n", code);
- ABORT("sem_wait() for restart handler failed");
+ ABORT_ARG1("sem_wait() for restart handler failed",
+ ": errcode= %d", code);
}
}
}
@@ -810,7 +856,7 @@ GC_INNER void GC_start_world(void)
GC_INNER void GC_stop_init(void)
{
-# if !defined(GC_OPENBSD_THREADS) && !defined(NACL)
+# if !defined(GC_OPENBSD_UTHREADS) && !defined(NACL)
struct sigaction act;
if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0)
@@ -830,38 +876,41 @@ GC_INNER void GC_stop_init(void)
# endif
;
if (sigfillset(&act.sa_mask) != 0) {
- ABORT("sigfillset() failed");
+ ABORT("sigfillset failed");
}
# ifdef GC_RTEMS_PTHREADS
if(sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL) != 0) {
- ABORT("rtems sigprocmask() failed");
+ ABORT("sigprocmask failed");
}
# endif
GC_remove_allowed_signals(&act.sa_mask);
- /* SIG_THR_RESTART is set in the resulting mask. */
- /* It is unmasked by the handler when necessary. */
+ /* GC_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) {
+ /* act.sa_restorer is deprecated and should not be initialized. */
+ if (GC_sig_suspend == GC_sig_thr_restart)
+ ABORT("Cannot use same signal for thread suspend and resume");
+ if (sigaction(GC_sig_suspend, &act, NULL) != 0) {
ABORT("Cannot set SIG_SUSPEND handler");
}
# ifdef SA_SIGINFO
- act.sa_flags &= ~ SA_SIGINFO;
+ act.sa_flags &= ~SA_SIGINFO;
# endif
act.sa_handler = GC_restart_handler;
- if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
+ if (sigaction(GC_sig_thr_restart, &act, NULL) != 0) {
ABORT("Cannot set SIG_THR_RESTART handler");
}
- /* Initialize suspend_handler_mask. It excludes SIG_THR_RESTART. */
- if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset() failed");
+ /* Initialize suspend_handler_mask (excluding GC_sig_thr_restart). */
+ if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset failed");
GC_remove_allowed_signals(&suspend_handler_mask);
- if (sigdelset(&suspend_handler_mask, SIG_THR_RESTART) != 0)
- ABORT("sigdelset() failed");
+ if (sigdelset(&suspend_handler_mask, GC_sig_thr_restart) != 0)
+ ABORT("sigdelset failed");
/* Check for GC_RETRY_SIGNALS. */
if (0 != GETENV("GC_RETRY_SIGNALS")) {
@@ -870,19 +919,10 @@ GC_INNER void GC_stop_init(void)
if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
GC_retry_signals = FALSE;
}
- if (GC_print_stats && GC_retry_signals) {
- GC_log_printf("Will retry suspend signal if necessary\n");
+ if (GC_retry_signals) {
+ GC_COND_LOG_PRINTF("Will retry suspend signal if necessary\n");
}
-# endif /* !GC_OPENBSD_THREADS && !NACL */
+# endif /* !GC_OPENBSD_UTHREADS && !NACL */
}
- GC_API int GC_CALL GC_get_thr_restart_signal(void)
- {
-# if !defined(GC_OPENBSD_THREADS) && !defined(NACL)
- return SIG_THR_RESTART;
-# else
- return -1;
-# endif
- }
-
-#endif
+#endif /* GC_PTHREADS && !GC_DARWIN_THREADS && !GC_WIN32_THREADS */
diff --git a/pthread_support.c b/pthread_support.c
index 218b662e..957408d8 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -82,9 +82,11 @@
#if !defined(USE_SPIN_LOCK)
GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
#endif
-GC_INNER unsigned long GC_lock_holder = NO_THREAD;
- /* Used only for assertions, and to prevent */
- /* recursive reentry in the system call wrapper. */
+
+#ifdef GC_ASSERTIONS
+ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
+ /* Used only for assertions. */
+#endif
#if defined(GC_DGUX386_THREADS)
# include <sys/dg_sys_info.h>
@@ -144,7 +146,7 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
# define REAL_FUNC(f) GC_real_##f
/* We define both GC_f and plain f to be the wrapped function. */
/* In that way plain calls work, as do calls from files that */
- /* included gc.h, wich redefined f to GC_f. */
+ /* included gc.h, which redefined f to GC_f. */
/* FIXME: Needs work for DARWIN and True64 (OSF1) */
typedef int (* GC_pthread_create_t)(pthread_t *,
GC_PTHREAD_CREATE_CONST pthread_attr_t *,
@@ -264,17 +266,17 @@ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
GC_syms_initialized = TRUE;
}
-# define INIT_REAL_SYMS() if (!EXPECT(GC_syms_initialized, TRUE)) \
- GC_init_real_syms()
+# define INIT_REAL_SYMS() if (EXPECT(GC_syms_initialized, TRUE)) {} \
+ else GC_init_real_syms()
#else
-# define INIT_REAL_SYMS()
+# define INIT_REAL_SYMS() (void)0
#endif
static GC_bool parallel_initialized = FALSE;
GC_INNER GC_bool GC_need_to_lock = FALSE;
-STATIC long GC_nprocs = 1;
+STATIC int GC_nprocs = 1;
/* Number of processors. We may not have */
/* access to all of them, but this is as good */
/* a guess as any ... */
@@ -343,7 +345,7 @@ static ptr_t marker_sp[MAX_MARKERS - 1] = {0};
GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread)
{
int i;
- for (i = 0; i < GC_markers - 1; i++) {
+ for (i = 0; i < GC_markers_m1; i++) {
if (marker_mach_threads[i] == thread)
return TRUE;
}
@@ -390,16 +392,28 @@ STATIC void * GC_mark_thread(void * id)
STATIC pthread_t GC_mark_threads[MAX_MARKERS];
-static void start_mark_threads(void)
+#ifdef CAN_HANDLE_FORK
+ static int available_markers_m1 = 0;
+# define start_mark_threads GC_start_mark_threads
+ GC_API void GC_CALL
+#else
+# define available_markers_m1 GC_markers_m1
+ static void
+#endif
+start_mark_threads(void)
{
int i;
pthread_attr_t attr;
GC_ASSERT(I_DONT_HOLD_LOCK());
+# ifdef CAN_HANDLE_FORK
+ if (available_markers_m1 <= 0 || GC_parallel) return;
+ /* Skip if parallel markers disabled or already started. */
+# endif
+
INIT_REAL_SYMS(); /* for pthread_create */
if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
-
if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
ABORT("pthread_attr_setdetachstate failed");
@@ -410,7 +424,6 @@ static void start_mark_threads(void)
# define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word))
{
size_t old_size;
- int code;
if (pthread_attr_getstacksize(&attr, &old_size) != 0)
ABORT("pthread_attr_getstacksize failed");
@@ -420,21 +433,18 @@ static void start_mark_threads(void)
}
}
# endif /* HPUX || GC_DGUX386_THREADS */
- for (i = 0; i < GC_markers - 1; ++i) {
+ for (i = 0; i < available_markers_m1; ++i) {
if (0 != REAL_FUNC(pthread_create)(GC_mark_threads + i, &attr,
GC_mark_thread, (void *)(word)i)) {
WARN("Marker thread creation failed, errno = %" WARN_PRIdPTR "\n",
errno);
/* Don't try to create other marker threads. */
- GC_markers = i + 1;
- if (i == 0) GC_parallel = FALSE;
break;
}
}
- if (GC_print_stats) {
- GC_log_printf("Started %ld mark helper threads\n", GC_markers - 1);
- }
+ GC_markers_m1 = i;
pthread_attr_destroy(&attr);
+ GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
}
#endif /* PARALLEL_MARK */
@@ -449,7 +459,7 @@ void GC_push_thread_structures(void)
GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
# if defined(THREAD_LOCAL_ALLOC)
GC_push_all((ptr_t)(&GC_thread_key),
- (ptr_t)(&GC_thread_key) + sizeof(&GC_thread_key));
+ (ptr_t)(&GC_thread_key) + sizeof(GC_thread_key));
# endif
}
@@ -629,8 +639,8 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void)
LOCK();
me = GC_lookup_thread(pthread_self());
UNLOCK();
- return (char *)tsd >= (char *)&me->tlfs
- && (char *)tsd < (char *)&me->tlfs + sizeof(me->tlfs);
+ return (word)tsd >= (word)(&me->tlfs)
+ && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs);
}
#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
@@ -646,7 +656,7 @@ GC_API int GC_CALL GC_thread_is_registered(void)
return me != NULL;
}
-#ifdef HANDLE_FORK
+#ifdef CAN_HANDLE_FORK
/* Remove all entries from the GC_threads table, except the */
/* one for the current thread. We need to do this in the child */
/* process after a fork(), since only the current thread */
@@ -664,10 +674,27 @@ STATIC void GC_remove_all_threads_but_me(void)
if (THREAD_EQUAL(p -> id, self)) {
me = p;
p -> next = 0;
+# ifdef GC_DARWIN_THREADS
+ /* Update thread Id after fork (it is OK to call */
+ /* GC_destroy_thread_local and GC_free_internal */
+ /* before update). */
+ me -> stop_info.mach_thread = mach_thread_self();
+# elif defined(PLATFORM_ANDROID)
+ me -> kernel_id = gettid();
+# endif
+# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
+ /* Some TLS implementations might be not fork-friendly, so */
+ /* we re-assign thread-local pointer to 'tlfs' for safety */
+ /* instead of the assertion check (again, it is OK to call */
+ /* GC_destroy_thread_local and GC_free_internal before). */
+ if (GC_setspecific(GC_thread_key, &me->tlfs) != 0)
+ ABORT("GC_setspecific failed (in child)");
+# endif
} else {
# ifdef THREAD_LOCAL_ALLOC
if (!(p -> flags & FINISHED)) {
GC_destroy_thread_local(&(p->tlfs));
+ GC_remove_specific(GC_thread_key);
}
# endif
if (p != &first_thread) GC_INTERNAL_FREE(p);
@@ -676,7 +703,7 @@ STATIC void GC_remove_all_threads_but_me(void)
GC_threads[hv] = me;
}
}
-#endif /* HANDLE_FORK */
+#endif /* CAN_HANDLE_FORK */
#ifdef USE_PROC_FOR_LIBRARIES
GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
@@ -686,10 +713,13 @@ STATIC void GC_remove_all_threads_but_me(void)
GC_ASSERT(I_HOLD_LOCK());
# ifdef PARALLEL_MARK
- for (i = 0; i < GC_markers - 1; ++i) {
- if (marker_sp[i] > lo && marker_sp[i] < hi) return TRUE;
+ for (i = 0; i < GC_markers_m1; ++i) {
+ if ((word)marker_sp[i] > (word)lo && (word)marker_sp[i] < (word)hi)
+ return TRUE;
# ifdef IA64
- if (marker_bsp[i] > lo && marker_bsp[i] < hi) return TRUE;
+ if ((word)marker_bsp[i] > (word)lo
+ && (word)marker_bsp[i] < (word)hi)
+ return TRUE;
# endif
}
# endif
@@ -697,9 +727,13 @@ STATIC void GC_remove_all_threads_but_me(void)
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (0 != p -> stack_end) {
# ifdef STACK_GROWS_UP
- if (p -> stack_end >= lo && p -> stack_end < hi) return TRUE;
+ if ((word)p->stack_end >= (word)lo
+ && (word)p->stack_end < (word)hi)
+ return TRUE;
# else /* STACK_GROWS_DOWN */
- if (p -> stack_end > lo && p -> stack_end <= hi) return TRUE;
+ if ((word)p->stack_end > (word)lo
+ && (word)p->stack_end <= (word)hi)
+ return TRUE;
# endif
}
}
@@ -720,14 +754,16 @@ STATIC void GC_remove_all_threads_but_me(void)
GC_ASSERT(I_HOLD_LOCK());
# ifdef PARALLEL_MARK
- for (i = 0; i < GC_markers - 1; ++i) {
- if (marker_sp[i] > result && marker_sp[i] < bound)
+ for (i = 0; i < GC_markers_m1; ++i) {
+ if ((word)marker_sp[i] > (word)result
+ && (word)marker_sp[i] < (word)bound)
result = marker_sp[i];
}
# endif
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> stack_end > result && p -> stack_end < bound) {
+ if ((word)p->stack_end > (word)result
+ && (word)p->stack_end < (word)bound) {
result = p -> stack_end;
}
}
@@ -744,7 +780,26 @@ STATIC void GC_remove_all_threads_but_me(void)
/* the real one. */
#endif
-#if defined(GC_LINUX_THREADS) && !defined(PLATFORM_ANDROID) && !defined(NACL)
+#ifdef GC_HPUX_THREADS
+# define GC_get_nprocs() pthread_num_processors_np()
+
+#elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
+ || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS) \
+ || defined(PLATFORM_ANDROID) || defined(NACL)
+ GC_INLINE int GC_get_nprocs(void)
+ {
+ int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
+ return nprocs > 0 ? nprocs : 1; /* ignore error silently */
+ }
+
+#elif defined(GC_IRIX_THREADS)
+ GC_INLINE int GC_get_nprocs(void)
+ {
+ int nprocs = (int)sysconf(_SC_NPROC_ONLN);
+ return nprocs > 0 ? nprocs : 1; /* ignore error silently */
+ }
+
+#elif defined(GC_LINUX_THREADS) /* && !PLATFORM_ANDROID && !NACL */
/* Return the number of processors. */
STATIC int GC_get_nprocs(void)
{
@@ -778,7 +833,42 @@ STATIC void GC_remove_all_threads_but_me(void)
}
return result;
}
-#endif /* GC_LINUX_THREADS && !PLATFORM_ANDROID && !NACL */
+
+#elif defined(GC_DGUX386_THREADS)
+ /* Return the number of processors, or i <= 0 if it can't be determined. */
+ STATIC int GC_get_nprocs(void)
+ {
+ int numCpus;
+ struct dg_sys_info_pm_info pm_sysinfo;
+ int status = 0;
+
+ status = dg_sys_info((long int *) &pm_sysinfo,
+ DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION);
+ if (status < 0)
+ /* set -1 for error */
+ numCpus = -1;
+ else
+ /* Active CPUs */
+ numCpus = pm_sysinfo.idle_vp_count;
+ return(numCpus);
+ }
+
+#elif defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \
+ || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
+ STATIC int GC_get_nprocs(void)
+ {
+ int mib[] = {CTL_HW,HW_NCPU};
+ int res;
+ size_t len = sizeof(res);
+
+ sysctl(mib, sizeof(mib)/sizeof(int), &res, &len, NULL, 0);
+ return res;
+ }
+
+#else
+ /* E.g., GC_RTEMS_PTHREADS */
+# define GC_get_nprocs() 1 /* not implemented */
+#endif /* !GC_LINUX_THREADS && !GC_DARWIN_THREADS && ... */
#if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL)
/* Some buggy Linux/arm kernels show only non-sleeping CPUs in */
@@ -842,7 +932,7 @@ STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
}
}
-#ifdef HANDLE_FORK
+#ifdef CAN_HANDLE_FORK
/* Procedures called before and after a fork. The goal here is to make */
/* it safe to call GC_malloc() in a forked child. It's unclear that is */
/* attainable, since the single UNIX spec seems to imply that one */
@@ -855,7 +945,7 @@ IF_CANCEL(static int fork_cancel_state;)
/* protected by allocation lock. */
/* Called before a fork() */
-STATIC void GC_fork_prepare_proc(void)
+static void fork_prepare_proc(void)
{
/* Acquire all relevant locks, so that after releasing the locks */
/* the child will see a consistent state in which monitor */
@@ -878,8 +968,8 @@ STATIC void GC_fork_prepare_proc(void)
# endif
}
-/* Called in parent after a fork() */
-STATIC void GC_fork_parent_proc(void)
+/* Called in parent after a fork() (even if the latter failed). */
+static void fork_parent_proc(void)
{
# if defined(PARALLEL_MARK)
if (GC_parallel)
@@ -890,7 +980,7 @@ STATIC void GC_fork_parent_proc(void)
}
/* Called in child after a fork() */
-STATIC void GC_fork_child_proc(void)
+static void fork_child_proc(void)
{
/* Clean up the thread table, so that just our thread is left. */
# if defined(PARALLEL_MARK)
@@ -901,50 +991,37 @@ STATIC void GC_fork_child_proc(void)
# ifdef PARALLEL_MARK
/* Turn off parallel marking in the child, since we are probably */
/* just going to exec, and we would have to restart mark threads. */
- GC_markers = 1;
GC_parallel = FALSE;
# endif /* PARALLEL_MARK */
RESTORE_CANCEL(fork_cancel_state);
UNLOCK();
}
-#endif /* HANDLE_FORK */
-#if defined(GC_DGUX386_THREADS)
- /* Return the number of processors, or i<= 0 if it can't be determined. */
- STATIC int GC_get_nprocs(void)
+ /* Routines for fork handling by client (no-op if pthread_atfork works). */
+ GC_API void GC_CALL GC_atfork_prepare(void)
{
- int numCpus;
- struct dg_sys_info_pm_info pm_sysinfo;
- int status = 0;
-
- status = dg_sys_info((long int *) &pm_sysinfo,
- DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION);
- if (status < 0)
- /* set -1 for error */
- numCpus = -1;
- else
- /* Active CPUs */
- numCpus = pm_sysinfo.idle_vp_count;
-
-# ifdef DEBUG_THREADS
- GC_log_printf("Number of active CPUs in this system: %d\n", numCpus);
-# endif
- return(numCpus);
+# if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB)
+ if (GC_dirty_maintained) {
+ GC_ASSERT(0 == GC_handle_fork);
+ ABORT("Unable to fork while mprotect_thread is running");
+ }
+# endif
+ if (GC_handle_fork <= 0)
+ fork_prepare_proc();
}
-#endif /* GC_DGUX386_THREADS */
-#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \
- || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
- static int get_ncpu(void)
+ GC_API void GC_CALL GC_atfork_parent(void)
{
- int mib[] = {CTL_HW,HW_NCPU};
- int res;
- size_t len = sizeof(res);
+ if (GC_handle_fork <= 0)
+ fork_parent_proc();
+ }
- sysctl(mib, sizeof(mib)/sizeof(int), &res, &len, NULL, 0);
- return res;
+ GC_API void GC_CALL GC_atfork_child(void)
+ {
+ if (GC_handle_fork <= 0)
+ fork_child_proc();
}
-#endif /* GC_DARWIN_THREADS || ... */
+#endif /* CAN_HANDLE_FORK */
#ifdef INCLUDE_LINUX_THREAD_DESCR
__thread int GC_dummy_thread_local;
@@ -955,16 +1032,23 @@ STATIC void GC_fork_child_proc(void)
/* We hold the allocation lock. */
GC_INNER void GC_thr_init(void)
{
-# ifndef GC_DARWIN_THREADS
- int dummy;
-# endif
if (GC_thr_initialized) return;
GC_thr_initialized = TRUE;
-# ifdef HANDLE_FORK
- /* Prepare for a possible fork. */
- pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
- GC_fork_child_proc);
+ GC_ASSERT((word)&GC_threads % sizeof(word) == 0);
+# ifdef CAN_HANDLE_FORK
+ /* Prepare for forks if requested. */
+ if (GC_handle_fork) {
+# ifdef CAN_CALL_ATFORK
+ if (pthread_atfork(fork_prepare_proc, fork_parent_proc,
+ fork_child_proc) == 0) {
+ /* Handlers successfully registered. */
+ GC_handle_fork = 1;
+ } else
+# endif
+ /* else */ if (GC_handle_fork != -1)
+ ABORT("pthread_atfork failed");
+ }
# endif
# ifdef INCLUDE_LINUX_THREAD_DESCR
/* Explicitly register the region including the address */
@@ -991,7 +1075,7 @@ GC_INNER void GC_thr_init(void)
# ifdef GC_DARWIN_THREADS
t -> stop_info.mach_thread = mach_thread_self();
# else
- t -> stop_info.stack_ptr = (ptr_t)(&dummy);
+ t -> stop_info.stack_ptr = GC_approx_sp();
# endif
t -> flags = DETACHED | MAIN_THREAD;
}
@@ -1013,72 +1097,51 @@ GC_INNER void GC_thr_init(void)
# endif
)
{
-# if defined(GC_HPUX_THREADS)
- GC_nprocs = pthread_num_processors_np();
-# elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
- || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS) \
- || defined(PLATFORM_ANDROID) || defined(NACL)
- GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
- if (GC_nprocs <= 0) GC_nprocs = 1;
-# elif defined(GC_IRIX_THREADS)
- GC_nprocs = sysconf(_SC_NPROC_ONLN);
- if (GC_nprocs <= 0) GC_nprocs = 1;
-# elif defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \
- || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
- GC_nprocs = get_ncpu();
-# elif defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
- GC_nprocs = GC_get_nprocs();
-# elif defined(GC_RTEMS_PTHREADS)
- GC_nprocs = 1; /* not implemented */
-# endif
+ GC_nprocs = GC_get_nprocs();
}
if (GC_nprocs <= 0) {
WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n", GC_nprocs);
GC_nprocs = 2; /* assume dual-core */
# ifdef PARALLEL_MARK
- GC_markers = 1;
+ available_markers_m1 = 0; /* but use only one marker */
# endif
} else {
# ifdef PARALLEL_MARK
{
char * markers_string = GETENV("GC_MARKERS");
+ int markers_m1;
+
if (markers_string != NULL) {
- GC_markers = atoi(markers_string);
- if (GC_markers > MAX_MARKERS) {
+ markers_m1 = atoi(markers_string) - 1;
+ if (markers_m1 >= MAX_MARKERS) {
WARN("Limiting number of mark threads\n", 0);
- GC_markers = MAX_MARKERS;
+ markers_m1 = MAX_MARKERS - 1;
}
} else {
- GC_markers = GC_nprocs;
+ markers_m1 = GC_nprocs - 1;
# ifdef GC_MIN_MARKERS
/* This is primarily for targets without getenv(). */
- if (GC_markers < GC_MIN_MARKERS)
- GC_markers = GC_MIN_MARKERS;
+ if (markers_m1 < GC_MIN_MARKERS - 1)
+ markers_m1 = GC_MIN_MARKERS - 1;
# endif
- if (GC_markers >= MAX_MARKERS)
- GC_markers = MAX_MARKERS; /* silently limit GC_markers value */
+ if (markers_m1 >= MAX_MARKERS)
+ markers_m1 = MAX_MARKERS - 1; /* silently limit the value */
}
+ available_markers_m1 = markers_m1;
}
# endif
}
+ GC_COND_LOG_PRINTF("Number of processors = %d\n", GC_nprocs);
# ifdef PARALLEL_MARK
- if (GC_print_stats) {
- GC_log_printf(
- "Number of processors = %ld, number of marker threads = %ld\n",
- GC_nprocs, GC_markers);
- }
- if (GC_markers <= 1) {
+ if (available_markers_m1 <= 0) {
+ /* Disable parallel marking. */
GC_parallel = FALSE;
- if (GC_print_stats) {
- GC_log_printf("Single marker thread, turning off parallel marking\n");
- }
+ GC_COND_LOG_PRINTF(
+ "Single marker thread, turning off parallel marking\n");
} else {
- GC_parallel = TRUE;
/* Disable true incremental collection, but generational is OK. */
GC_time_limit = GC_TIME_UNLIMITED;
- }
- /* If we are using a parallel marker, actually start helper threads. */
- if (GC_parallel) {
+ /* If we are using a parallel marker, actually start helper threads. */
start_mark_threads();
}
# endif
@@ -1112,11 +1175,14 @@ GC_INNER void GC_init_parallel(void)
sigset_t *oset)
{
sigset_t fudged_set;
+ int sig_suspend;
INIT_REAL_SYMS();
if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
fudged_set = *set;
- sigdelset(&fudged_set, SIG_SUSPEND);
+ sig_suspend = GC_get_suspend_signal();
+ GC_ASSERT(sig_suspend >= 0);
+ sigdelset(&fudged_set, sig_suspend);
set = &fudged_set;
}
return(REAL_FUNC(pthread_sigmask)(how, set, oset));
@@ -1190,18 +1256,21 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
/* GC_get_stack_base() was used which returned GC_SUCCESS). */
if ((me -> flags & MAIN_THREAD) == 0) {
GC_ASSERT(me -> stack_end != NULL);
- if (me -> stack_end HOTTER_THAN (ptr_t)(&stacksect))
+ if ((word)me->stack_end HOTTER_THAN (word)(&stacksect))
me -> stack_end = (ptr_t)(&stacksect);
} else {
/* The original stack. */
- if (GC_stackbottom HOTTER_THAN (ptr_t)(&stacksect))
+ if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect))
GC_stackbottom = (ptr_t)(&stacksect);
}
if (!me->thread_blocked) {
/* We are not inside GC_do_blocking() - do nothing more. */
UNLOCK();
- return fn(client_data);
+ client_data = fn(client_data);
+ /* Prevent treating the above as a tail call. */
+ GC_noop1((word)(&stacksect));
+ return client_data; /* result */
}
/* Setup new "stack section". */
@@ -1244,6 +1313,7 @@ STATIC void GC_unregister_my_thread_inner(GC_thread me)
# endif
GC_ASSERT(!(me -> flags & FINISHED));
# if defined(THREAD_LOCAL_ALLOC)
+ GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs);
GC_destroy_thread_local(&(me->tlfs));
# endif
# if defined(GC_PTHREAD_EXIT_ATTRIBUTE) || !defined(GC_NO_PTHREAD_CANCEL)
@@ -1294,7 +1364,7 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
/* results in at most a tiny one-time leak. And */
/* linuxthreads doesn't reclaim the main threads */
/* resources or id anyway. */
-GC_INNER void GC_thread_exit_proc(void *arg)
+GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg)
{
# ifdef DEBUG_THREADS
GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread = %p\n",
@@ -1530,7 +1600,8 @@ struct start_info {
/* Called from GC_inner_start_routine(). Defined in this file to */
/* minimize the number of include files in pthread_start.c (because */
/* sem_t and sem_post() are not used that file directly). */
-GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *),
+GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread(
+ void *(**pstart)(void *),
void **pstart_arg,
struct GC_stack_base *sb, void *arg)
{
@@ -1560,7 +1631,8 @@ GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *),
return me;
}
-void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg);
+GC_INNER_PTHRSTART void * GC_CALLBACK GC_inner_start_routine(
+ struct GC_stack_base *sb, void *arg);
/* defined in pthread_start.c */
STATIC void * GC_start_routine(void * arg)
@@ -1629,8 +1701,10 @@ GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread,
}
if (0 == stack_size) {
pthread_attr_t my_attr;
+
pthread_attr_init(&my_attr);
pthread_attr_getstacksize(&my_attr, &stack_size);
+ pthread_attr_destroy(&my_attr);
}
/* On Solaris 10, with default attr initialization, */
/* stack_size remains 0. Fudge it. */
@@ -1709,7 +1783,7 @@ STATIC void GC_pause(void)
__asm__ __volatile__ (" " : : : "memory");
# else
/* Something that's unlikely to be optimized away. */
- GC_noop(++dummy);
+ GC_noop1(++dummy);
# endif
}
}
@@ -1744,9 +1818,9 @@ GC_INNER volatile GC_bool GC_collecting = 0;
/* #define LOCK_STATS */
/* Note that LOCK_STATS requires AO_HAVE_test_and_set. */
#ifdef LOCK_STATS
- AO_t GC_spin_count = 0;
- AO_t GC_block_count = 0;
- AO_t GC_unlocked_count = 0;
+ volatile AO_t GC_spin_count = 0;
+ volatile AO_t GC_block_count = 0;
+ volatile AO_t GC_unlocked_count = 0;
#endif
STATIC void GC_generic_lock(pthread_mutex_t * lock)
@@ -1856,7 +1930,7 @@ yield:
}
}
-#else /* !USE_SPINLOCK */
+#else /* !USE_SPIN_LOCK */
GC_INNER void GC_lock(void)
{
#ifndef NO_PTHREAD_TRYLOCK
@@ -1870,13 +1944,24 @@ GC_INNER void GC_lock(void)
#endif /* !NO_PTHREAD_TRYLOCK */
}
-#endif /* !USE_SPINLOCK */
+#endif /* !USE_SPIN_LOCK */
#ifdef PARALLEL_MARK
-#ifdef GC_ASSERTIONS
- GC_INNER unsigned long GC_mark_lock_holder = NO_THREAD;
-#endif
+# ifdef GC_ASSERTIONS
+ STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
+# define SET_MARK_LOCK_HOLDER \
+ (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
+# define UNSET_MARK_LOCK_HOLDER \
+ do { \
+ GC_ASSERT(GC_mark_lock_holder \
+ == NUMERIC_THREAD_ID(pthread_self())); \
+ GC_mark_lock_holder = NO_THREAD; \
+ } while (0)
+# else
+# define SET_MARK_LOCK_HOLDER (void)0
+# define UNSET_MARK_LOCK_HOLDER (void)0
+# endif /* !GC_ASSERTIONS */
#ifdef GLIBC_2_1_MUTEX_HACK
/* Ugly workaround for a linux threads bug in the final versions */
@@ -1896,18 +1981,14 @@ static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
GC_INNER void GC_acquire_mark_lock(void)
{
+ GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
GC_generic_lock(&mark_mutex);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_release_mark_lock(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_mutex_unlock(&mark_mutex) != 0) {
ABORT("pthread_mutex_unlock failed");
}
@@ -1920,18 +2001,13 @@ GC_INNER void GC_release_mark_lock(void)
/* free-list link may be ignored. */
STATIC void GC_wait_builder(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
ASSERT_CANCEL_DISABLED();
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_wait_for_reclaim(void)
@@ -1955,18 +2031,13 @@ static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
GC_INNER void GC_wait_marker(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
ASSERT_CANCEL_DISABLED();
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_notify_all_marker(void)
diff --git a/ptr_chck.c b/ptr_chck.c
index a4766339..011b27ef 100644
--- a/ptr_chck.c
+++ b/ptr_chck.c
@@ -20,8 +20,8 @@
STATIC void GC_CALLBACK GC_default_same_obj_print_proc(void * p, void * q)
{
- GC_err_printf("%p and %p are not in the same object\n", p, q);
- ABORT("GC_same_obj test failed");
+ ABORT_ARG2("GC_same_obj test failed",
+ ": %p and %p are not in the same object", p, q);
}
void (GC_CALLBACK *GC_same_obj_print_proc) (void *, void *)
@@ -30,8 +30,8 @@ void (GC_CALLBACK *GC_same_obj_print_proc) (void *, void *)
/* Check that p and q point to the same object. Call */
/* *GC_same_obj_print_proc if they don't. */
/* Returns the first argument. (Return value may be hard */
-/* to use,due to typing issues. But if we had a suitable */
-/* preprocessor ...) */
+/* to use due to typing issues. But if we had a suitable */
+/* preprocessor...) */
/* Succeeds if neither p nor q points to the heap. */
/* We assume this is performance critical. (It shouldn't */
/* be called by production code, but this can easily make */
@@ -62,7 +62,8 @@ GC_API void * GC_CALL GC_same_obj(void *p, void *q)
hhdr = HDR(h);
}
limit = (ptr_t)h + hhdr -> hb_sz;
- if ((ptr_t)p >= limit || (ptr_t)q >= limit || (ptr_t)q < (ptr_t)h ) {
+ if ((word)p >= (word)limit || (word)q >= (word)limit
+ || (word)q < (word)h) {
goto fail;
}
return(p);
@@ -71,7 +72,7 @@ GC_API void * GC_CALL GC_same_obj(void *p, void *q)
if (sz > MAXOBJBYTES) {
base = (ptr_t)HBLKPTR(p);
limit = base + sz;
- if ((ptr_t)p >= limit) {
+ if ((word)p >= (word)limit) {
goto fail;
}
} else {
@@ -90,7 +91,7 @@ GC_API void * GC_CALL GC_same_obj(void *p, void *q)
/* If p is not inside a valid object, then either q is */
/* also outside any valid object, or it is outside */
/* [base, limit). */
- if ((ptr_t)q >= limit || (ptr_t)q < base) {
+ if ((word)q >= (word)limit || (word)q < (word)base) {
goto fail;
}
return(p);
@@ -101,8 +102,7 @@ fail:
STATIC void GC_CALLBACK GC_default_is_valid_displacement_print_proc (void *p)
{
- GC_err_printf("%p does not point to valid object displacement\n", p);
- ABORT("GC_is_valid_displacement test failed");
+ ABORT_ARG1("GC_is_valid_displacement test failed", ": %p not valid", p);
}
void (GC_CALLBACK *GC_is_valid_displacement_print_proc)(void *) =
@@ -138,9 +138,9 @@ GC_API void * GC_CALL GC_is_valid_displacement(void *p)
sz = hhdr -> hb_sz;
pdispl = HBLKDISPL(p);
offset = pdispl % sz;
- if ((sz > MAXOBJBYTES && (ptr_t)p >= (ptr_t)h + sz)
+ if ((sz > MAXOBJBYTES && (word)p >= (word)h + sz)
|| !GC_valid_offsets[offset]
- || (ptr_t)p - offset + sz > (ptr_t)(h + 1)) {
+ || (word)p - offset + sz > (word)(h + 1)) {
goto fail;
}
return(p);
@@ -151,8 +151,7 @@ fail:
STATIC void GC_CALLBACK GC_default_is_visible_print_proc(void * p)
{
- GC_err_printf("%p is not a GC visible pointer location\n", p);
- ABORT("GC_is_visible test failed");
+ ABORT_ARG1("GC_is_visible test failed", ": %p not GC-visible", p);
}
void (GC_CALLBACK *GC_is_visible_print_proc)(void * p) =
@@ -162,17 +161,18 @@ void (GC_CALLBACK *GC_is_visible_print_proc)(void * p) =
/* Could p be a stack address? */
STATIC GC_bool GC_on_stack(ptr_t p)
{
- int dummy;
-# ifdef STACK_GROWS_DOWN
- if ((ptr_t)p >= (ptr_t)(&dummy) && (ptr_t)p < GC_stackbottom ) {
- return(TRUE);
- }
-# else
- if ((ptr_t)p <= (ptr_t)(&dummy) && (ptr_t)p > GC_stackbottom ) {
- return(TRUE);
- }
-# endif
- return(FALSE);
+# ifdef STACK_GROWS_DOWN
+ if ((word)p >= (word)GC_approx_sp()
+ && (word)p < (word)GC_stackbottom) {
+ return(TRUE);
+ }
+# else
+ if ((word)p <= (word)GC_approx_sp()
+ && (word)p > (word)GC_stackbottom) {
+ return(TRUE);
+ }
+# endif
+ return(FALSE);
}
#endif
@@ -183,7 +183,7 @@ void (GC_CALLBACK *GC_is_visible_print_proc)(void * p) =
/* in hard cases. (This is intended for debugging use with */
/* untyped allocations. The idea is that it should be possible, though */
/* slow, to add such a call to all indirect pointer stores.) */
-/* Currently useless for multithreaded worlds. */
+/* Currently useless for the multi-threaded worlds. */
GC_API void * GC_CALL GC_is_visible(void *p)
{
hdr *hhdr;
@@ -223,12 +223,11 @@ GC_API void * GC_CALL GC_is_visible(void *p)
retry:
switch(descr & GC_DS_TAGS) {
case GC_DS_LENGTH:
- if ((word)((ptr_t)p - (ptr_t)base) > (word)descr) goto fail;
+ if ((word)p - (word)base > descr) goto fail;
break;
case GC_DS_BITMAP:
- if ((word)((ptr_t)p - (ptr_t)base)
- >= WORDS_TO_BYTES(BITMAP_BITS)
- || ((word)p & (sizeof(word) - 1))) goto fail;
+ if ((word)p - (word)base >= WORDS_TO_BYTES(BITMAP_BITS)
+ || ((word)p & (sizeof(word) - 1))) goto fail;
if (!(((word)1 << (WORDSZ - ((ptr_t)p - (ptr_t)base) - 1))
& descr)) goto fail;
break;
@@ -242,8 +241,8 @@ GC_API void * GC_CALL GC_is_visible(void *p)
} else {
ptr_t type_descr = *(ptr_t *)base;
descr = *(word *)(type_descr
- - (descr - (word)(GC_DS_PER_OBJECT
- - GC_INDIR_PER_OBJ_BIAS)));
+ - (descr - (word)(GC_DS_PER_OBJECT
+ - GC_INDIR_PER_OBJ_BIAS)));
}
goto retry;
}
diff --git a/real_malloc.c b/real_malloc.c
index da11f342..145e73f3 100644
--- a/real_malloc.c
+++ b/real_malloc.c
@@ -13,7 +13,7 @@
*/
# ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
# endif
# ifdef PCR
diff --git a/reclaim.c b/reclaim.c
index dee0d56b..285e1460 100644
--- a/reclaim.c
+++ b/reclaim.c
@@ -89,18 +89,15 @@ GC_INNER void GC_print_all_errors(void)
have_errors = FALSE;
}
+ if (GC_n_leaked > 0) {
+ GC_err_printf("Found %u leaked objects:\n", GC_n_leaked);
+ have_errors = TRUE;
+ }
for (i = 0; i < GC_n_leaked; ++i) {
ptr_t p = GC_leaked[i];
- if (HDR(p) -> hb_obj_kind == PTRFREE) {
- GC_err_printf("Leaked atomic object at ");
- } else {
- GC_err_printf("Leaked composite object at ");
- }
GC_print_heap_obj(p);
- GC_err_printf("\n");
GC_free(p);
GC_leaked[i] = 0;
- have_errors = TRUE;
}
GC_n_leaked = 0;
@@ -155,8 +152,8 @@ STATIC ptr_t GC_reclaim_clear(struct hblk *hbp, hdr *hhdr, size_t sz,
plim = (word *)(hbp->hb_body + HBLKSIZE - sz);
/* go through all words in block */
- while (p <= plim) {
- if( mark_bit_from_hdr(hhdr, bit_no) ) {
+ while ((word)p <= (word)plim) {
+ if (mark_bit_from_hdr(hhdr, bit_no)) {
p = (word *)((ptr_t)p + sz);
} else {
n_bytes_found += sz;
@@ -170,13 +167,13 @@ STATIC ptr_t GC_reclaim_clear(struct hblk *hbp, hdr *hhdr, size_t sz,
&& !((word)p & (2 * sizeof(word) - 1)));
p[1] = 0;
p += 2;
- while (p < q) {
+ while ((word)p < (word)q) {
CLEAR_DOUBLE(p);
p += 2;
}
# else
p++; /* Skip link field */
- while (p < q) {
+ while ((word)p < (word)q) {
*p++ = 0;
}
# endif
@@ -200,8 +197,8 @@ STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, size_t sz,
plim = (word *)((ptr_t)hbp + HBLKSIZE - sz);
/* go through all words in block */
- while (p <= plim) {
- if( !mark_bit_from_hdr(hhdr, bit_no) ) {
+ while ((word)p <= (word)plim) {
+ if (!mark_bit_from_hdr(hhdr, bit_no)) {
n_bytes_found += sz;
/* object is available - put on list */
obj_link(p) = list;
@@ -230,7 +227,7 @@ STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, size_t sz,
p = (word *)(hbp -> hb_body);
plim = (word *)((ptr_t)p + HBLKSIZE - sz);
- while (p <= plim) {
+ while ((word)p <= (word)plim) {
int marked = mark_bit_from_hdr(hhdr, bit_no);
if (!marked && (*disclaim)(p)) {
hhdr -> hb_n_marks++;
@@ -250,13 +247,13 @@ STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, size_t sz,
GC_ASSERT(((word)p & (2 * sizeof(word) - 1)) == 0);
p[1] = 0;
p += 2;
- while (p < q) {
+ while ((word)p < (word)q) {
CLEAR_DOUBLE(p);
p += 2;
}
# else
p++; /* Skip link field */
- while (p < q) {
+ while ((word)p < (word)q) {
*p++ = 0;
}
# endif
@@ -278,7 +275,8 @@ STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz)
/* go through all words in block */
p = hbp->hb_body;
plim = p + HBLKSIZE - sz;
- for (bit_no = 0; p <= plim; p += sz, bit_no += MARK_BIT_OFFSET(sz)) {
+ for (bit_no = 0; (word)p <= (word)plim;
+ p += sz, bit_no += MARK_BIT_OFFSET(sz)) {
if (!mark_bit_from_hdr(hhdr, bit_no)) {
GC_add_leaked(p);
}
@@ -466,8 +464,9 @@ struct Print_stats
#ifdef USE_MARK_BYTES
-/* Return the number of set mark bits in the given header */
-STATIC int GC_n_set_marks(hdr *hhdr)
+/* Return the number of set mark bits in the given header. */
+/* Remains externally visible as used by GNU GCJ currently. */
+int GC_n_set_marks(hdr *hhdr)
{
int result = 0;
int i;
@@ -497,8 +496,7 @@ static int set_bits(word n)
return(result);
}
-/* Return the number of set mark bits in the given header */
-STATIC int GC_n_set_marks(hdr *hhdr)
+int GC_n_set_marks(hdr *hhdr)
{
int result = 0;
int i;
@@ -534,10 +532,10 @@ STATIC void GC_print_block_descr(struct hblk *h,
unsigned n_marks = GC_n_set_marks(hhdr);
if (hhdr -> hb_n_marks != n_marks) {
- GC_printf("(%u:%u,%u!=%u)", hhdr -> hb_obj_kind, (unsigned)bytes,
- (unsigned)hhdr -> hb_n_marks, n_marks);
+ GC_printf("(%u:%u,%u!=%u)\n", hhdr->hb_obj_kind, (unsigned)bytes,
+ (unsigned)hhdr->hb_n_marks, n_marks);
} else {
- GC_printf("(%u:%u,%u)", hhdr -> hb_obj_kind,
+ GC_printf("(%u:%u,%u)\n", hhdr->hb_obj_kind,
(unsigned)bytes, n_marks);
}
bytes += HBLKSIZE-1;
@@ -556,7 +554,7 @@ void GC_print_block_list(void)
pstats.number_of_blocks = 0;
pstats.total_bytes = 0;
GC_apply_to_all_blocks(GC_print_block_descr, (word)&pstats);
- GC_printf("\nblocks = %lu, bytes = %lu\n",
+ GC_printf("blocks= %lu, bytes= %lu\n",
(unsigned long)pstats.number_of_blocks,
(unsigned long)pstats.total_bytes);
}
@@ -566,16 +564,12 @@ void GC_print_free_list(int kind, size_t sz_in_granules)
{
struct obj_kind * ok = &GC_obj_kinds[kind];
ptr_t flh = ok -> ok_freelist[sz_in_granules];
- struct hblk *lastBlock = 0;
int n;
- for (n = 1; flh; n++) {
+ for (n = 0; flh; n++) {
struct hblk *block = HBLKPTR(flh);
- if (block != lastBlock) {
- GC_printf("\nIn heap block at %p:\n\t", (void *)block);
- lastBlock = block;
- }
- GC_printf("%d: %p;", n, flh);
+ GC_printf("Free object in heap block %p [%d]: %p\n",
+ (void *)block, n, flh);
flh = obj_link(flh);
}
}
@@ -624,7 +618,8 @@ GC_INNER void GC_start_reclaim(GC_bool report_if_found)
if (rlist == 0) continue; /* This kind not used. */
if (!report_if_found) {
lim = &(GC_obj_kinds[kind].ok_freelist[MAXOBJGRANULES+1]);
- for( fop = GC_obj_kinds[kind].ok_freelist; fop < lim; fop++ ) {
+ for (fop = GC_obj_kinds[kind].ok_freelist;
+ (word)fop < (word)lim; fop++) {
if (*fop != 0) {
if (should_clobber) {
GC_clear_fl_links(fop);
@@ -732,8 +727,8 @@ GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old)
# ifndef SMALL_CONFIG
if (GC_print_stats == VERBOSE) {
GET_TIME(done_time);
- GC_log_printf("Disposing of reclaim lists took %lu msecs\n",
- MS_TIME_DIFF(done_time,start_time));
+ GC_verbose_log_printf("Disposing of reclaim lists took %lu msecs\n",
+ MS_TIME_DIFF(done_time,start_time));
}
# endif
return(TRUE);
diff --git a/specific.c b/specific.c
index bc35d662..3657fbad 100644
--- a/specific.c
+++ b/specific.c
@@ -11,7 +11,6 @@
* modified is included with the above copyright notice.
*/
-#include "private/gc_priv.h" /* For configuration, pthreads.h. */
#include "private/thread_local_alloc.h"
/* To determine type of tsd impl. */
/* Includes private/specific.h */
@@ -19,9 +18,7 @@
#if defined(USE_CUSTOM_SPECIFIC)
-#include "atomic_ops.h"
-
-static tse invalid_tse = {INVALID_QTID, 0, 0, INVALID_THREADID};
+static const tse invalid_tse = {INVALID_QTID, 0, 0, INVALID_THREADID};
/* A thread-specific data entry which will never */
/* appear valid to a reader. Used to fill in empty */
/* cache entries to avoid a check for 0. */
@@ -32,38 +29,43 @@ GC_INNER int GC_key_create_inner(tsd ** key_ptr)
tsd * result = (tsd *)MALLOC_CLEAR(sizeof(tsd));
/* A quick alignment check, since we need atomic stores */
- GC_ASSERT((unsigned long)(&invalid_tse.next) % sizeof(tse *) == 0);
+ GC_ASSERT((word)(&invalid_tse.next) % sizeof(tse *) == 0);
if (0 == result) return ENOMEM;
pthread_mutex_init(&(result -> lock), NULL);
for (i = 0; i < TS_CACHE_SIZE; ++i) {
- result -> cache[i] = &invalid_tse;
+ result -> cache[i] = (/* no const */ tse *)&invalid_tse;
}
# ifdef GC_ASSERTIONS
for (i = 0; i < TS_HASH_SIZE; ++i) {
- GC_ASSERT(result -> hash[i] == 0);
+ GC_ASSERT(result -> hash[i].p == 0);
}
# endif
*key_ptr = result;
return 0;
}
+/* Called with the lock held. */
GC_INNER int GC_setspecific(tsd * key, void * value)
{
pthread_t self = pthread_self();
int hash_val = HASH(self);
- volatile tse * entry = (volatile tse *)MALLOC_CLEAR(sizeof (tse));
+ volatile tse * entry;
GC_ASSERT(self != INVALID_THREADID);
+ GC_dont_gc++; /* disable GC */
+ entry = (volatile tse *)MALLOC_CLEAR(sizeof(tse));
+ GC_dont_gc--;
if (0 == entry) return ENOMEM;
+
pthread_mutex_lock(&(key -> lock));
/* Could easily check for an existing entry here. */
- entry -> next = key -> hash[hash_val];
+ entry -> next = key->hash[hash_val].p;
entry -> thread = self;
entry -> value = value;
GC_ASSERT(entry -> qtid == INVALID_QTID);
/* There can only be one writer at a time, but this needs to be */
/* atomic with respect to concurrent readers. */
- AO_store_release((volatile AO_t *)(key -> hash + hash_val), (AO_t)entry);
+ AO_store_release(&key->hash[hash_val].ao, (AO_t)entry);
pthread_mutex_unlock(&(key -> lock));
return 0;
}
@@ -75,7 +77,7 @@ GC_INNER void GC_remove_specific(tsd * key)
pthread_t self = pthread_self();
unsigned hash_val = HASH(self);
tse *entry;
- tse **link = key -> hash + hash_val;
+ tse **link = &key->hash[hash_val].p;
pthread_mutex_lock(&(key -> lock));
entry = *link;
@@ -109,12 +111,12 @@ GC_INNER void GC_remove_specific(tsd * key)
}
/* Note that even the slow path doesn't lock. */
-GC_INNER void * GC_slow_getspecific(tsd * key, unsigned long qtid,
+GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
tse * volatile * cache_ptr)
{
pthread_t self = pthread_self();
unsigned hash_val = HASH(self);
- tse *entry = key -> hash[hash_val];
+ tse *entry = key->hash[hash_val].p;
GC_ASSERT(qtid != INVALID_QTID);
while (entry != NULL && entry -> thread != self) {
@@ -145,19 +147,16 @@ GC_INNER void * GC_slow_getspecific(tsd * key, unsigned long qtid,
ABORT("Unmarked thread-specific-data table");
}
for (i = 0; i < TS_HASH_SIZE; ++i) {
- for (p = key -> hash[i]; p != 0; p = p -> next) {
+ for (p = key->hash[i].p; p != 0; p = p -> next) {
if (!GC_is_marked(GC_base(p))) {
- GC_err_printf("Thread-specific-data entry at %p not marked\n", p);
- ABORT("Unmarked tse");
+ ABORT_ARG1("Unmarked thread-specific-data entry", " at %p", p);
}
}
}
for (i = 0; i < TS_CACHE_SIZE; ++i) {
p = key -> cache[i];
if (p != &invalid_tse && !GC_is_marked(GC_base(p))) {
- GC_err_printf("Cached thread-specific-data entry at %p not marked\n",
- p);
- ABORT("Unmarked cached tse");
+ ABORT_ARG1("Unmarked cached thread-specific-data entry", " at %p", p);
}
}
}
diff --git a/tests/disclaim_bench.c b/tests/disclaim_bench.c
index 44f1c207..5159829c 100644
--- a/tests/disclaim_bench.c
+++ b/tests/disclaim_bench.c
@@ -18,7 +18,6 @@
#include "private/gc_priv.h"
-#include "atomic_ops.h"
#include "gc_disclaim.h"
#define my_assert(e) \
diff --git a/tests/disclaim_test.c b/tests/disclaim_test.c
index f9133ae2..7bba6fb8 100644
--- a/tests/disclaim_test.c
+++ b/tests/disclaim_test.c
@@ -20,6 +20,11 @@
#include <stdio.h>
#include <string.h>
+#ifdef HAVE_CONFIG_H
+ /* For GC_[P]THREADS */
+# include "config.h"
+#endif
+
#include "gc_disclaim.h"
#define my_assert(e) \
@@ -40,8 +45,12 @@ int memeq(void *s, int c, size_t len)
void GC_CALLBACK misc_sizes_dct(void *obj, void *cd)
{
- size_t size = (size_t)1 << *(unsigned char *)obj;
+ unsigned log_size = *(unsigned char *)obj;
+ size_t size;
+
+ my_assert(log_size < sizeof(size_t) * 8);
my_assert(cd == NULL);
+ size = (size_t)1 << log_size;
my_assert(memeq((char *)obj + 1, 0x56, size - 1));
}
diff --git a/tests/initsecondarythread.c b/tests/initsecondarythread.c
index 93088653..5f214050 100644
--- a/tests/initsecondarythread.c
+++ b/tests/initsecondarythread.c
@@ -15,6 +15,10 @@
* thread.
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#ifndef GC_THREADS
# define GC_THREADS
#endif
@@ -61,7 +65,7 @@ int main(void)
DWORD thread_id;
# endif
# if !(defined(BEOS) || defined(MSWIN32) || defined(MSWINCE) \
- || defined(CYGWIN32) || defined(GC_OPENBSD_THREADS) \
+ || defined(CYGWIN32) || defined(GC_OPENBSD_UTHREADS) \
|| (defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP)) \
|| (defined(LINUX) && !defined(NACL)) \
|| (defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC)) \
@@ -72,21 +76,21 @@ int main(void)
# endif
# ifdef GC_PTHREADS
if ((code = pthread_create (&t, NULL, thread, NULL)) != 0) {
- printf("Thread creation failed %d\n", code);
+ fprintf(stderr, "Thread creation failed %d\n", code);
return 1;
}
if ((code = pthread_join (t, NULL)) != 0) {
- printf("Thread join failed %d\n", code);
+ fprintf(stderr, "Thread join failed %d\n", code);
return 1;
}
# else
t = CreateThread(NULL, 0, thread, 0, 0, &thread_id);
if (t == NULL) {
- printf("Thread creation failed %d\n", (int)GetLastError());
+ fprintf(stderr, "Thread creation failed %d\n", (int)GetLastError());
return 1;
}
if (WaitForSingleObject(t, INFINITE) != WAIT_OBJECT_0) {
- printf("Thread join failed %d\n", (int)GetLastError());
+ fprintf(stderr, "Thread join failed %d\n", (int)GetLastError());
CloseHandle(t);
return 1;
}
diff --git a/tests/middle.c b/tests/middle.c
index ff0a2355..55686f75 100644
--- a/tests/middle.c
+++ b/tests/middle.c
@@ -13,12 +13,12 @@ int main (void)
GC_INIT();
for (i = 0; i < 20000; ++i) {
- GC_malloc_atomic (4096);
- GC_malloc (4096);
+ (void)GC_malloc_atomic(4096);
+ (void)GC_malloc(4096);
}
for (i = 0; i < 20000; ++i) {
- GC_malloc_atomic (2048);
- GC_malloc (2048);
+ (void)GC_malloc_atomic(2048);
+ (void)GC_malloc(2048);
}
printf("Final heap size is %lu\n", (unsigned long)GC_get_heap_size());
return 0;
diff --git a/tests/staticrootslib.c b/tests/staticrootslib.c
index 2a8fcd49..ef828c7b 100644
--- a/tests/staticrootslib.c
+++ b/tests/staticrootslib.c
@@ -1,33 +1,57 @@
/* This test file is intended to be compiled into a DLL. */
-#include <stdio.h>
-
#ifndef GC_DEBUG
# define GC_DEBUG
#endif
#include "gc.h"
+#ifndef GC_TEST_EXPORT_API
+# if defined(GC_VISIBILITY_HIDDEN_SET) \
+ && !defined(__CEGCC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
+# define GC_TEST_EXPORT_API \
+ extern __attribute__((__visibility__("default")))
+# else
+# define GC_TEST_EXPORT_API extern
+# endif
+#endif
+
struct treenode {
struct treenode *x;
struct treenode *y;
-} * root[10];
+};
-struct treenode * libsrl_mktree(int i)
-{
- struct treenode * r = GC_MALLOC(sizeof(struct treenode));
- if (0 == i) return 0;
- if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode));
- if (r) {
- r -> x = libsrl_mktree(i-1);
- r -> y = libsrl_mktree(i-1);
+static struct treenode *root[10] = { 0 };
+static struct treenode *root_nz[10] = { (void *)(GC_word)2 };
+
+#ifdef STATICROOTSLIB2
+# define libsrl_getpelem libsrl_getpelem2
+#else
+
+ GC_TEST_EXPORT_API struct treenode * libsrl_mktree(int i)
+ {
+ struct treenode * r = GC_MALLOC(sizeof(struct treenode));
+ if (0 == i) return 0;
+ if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode));
+ if (r) {
+ r -> x = libsrl_mktree(i-1);
+ r -> y = libsrl_mktree(i-1);
+ }
+ return r;
}
- return r;
-}
-void * libsrl_init(void)
+ GC_TEST_EXPORT_API void * libsrl_init(void)
+ {
+# ifndef STATICROOTSLIB_INIT_IN_MAIN
+ GC_INIT();
+# endif
+ return GC_MALLOC(sizeof(struct treenode));
+ }
+
+#endif /* !STATICROOTSLIB2 */
+
+GC_TEST_EXPORT_API struct treenode ** libsrl_getpelem(int i, int j)
{
- GC_INIT();
- return GC_MALLOC(sizeof(struct treenode));
+ return &((j & 1) != 0 ? root_nz : root)[i];
}
diff --git a/tests/staticrootstest.c b/tests/staticrootstest.c
index f0b45bb6..34ce9b40 100644
--- a/tests/staticrootstest.c
+++ b/tests/staticrootstest.c
@@ -9,63 +9,59 @@
#include "gc.h"
#include "gc_backptr.h"
-#ifndef GC_VISIBILITY_HIDDEN_SET
+#ifndef GC_TEST_IMPORT_API
+# define GC_TEST_IMPORT_API extern
+#endif
+/* Should match that in staticrootslib.c. */
struct treenode {
struct treenode *x;
struct treenode *y;
-} * root[10];
+};
+
+struct treenode *root[10] = { NULL };
+
+/* Same as "root" variable but initialized to some non-zero value (to */
+/* be placed to .data section instead of .bss). */
+struct treenode *root_nz[10] = { (void *)(GC_word)1 };
static char *staticroot = 0;
-extern struct treenode * libsrl_mktree(int i);
-extern void * libsrl_init(void);
+GC_TEST_IMPORT_API struct treenode * libsrl_mktree(int i);
+GC_TEST_IMPORT_API void * libsrl_init(void);
+GC_TEST_IMPORT_API struct treenode ** libsrl_getpelem(int i, int j);
-/*
-struct treenode * mktree(int i) {
- struct treenode * r = GC_MALLOC(sizeof(struct treenode));
- if (0 == i) return 0;
- if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode));
- r -> x = mktree(i-1);
- r -> y = mktree(i-1);
- return r;
-}*/
+GC_TEST_IMPORT_API struct treenode ** libsrl_getpelem2(int i, int j);
int main(void)
{
- int i;
- /*GC_INIT();
- staticroot = GC_MALLOC(sizeof(struct treenode));*/
+ int i, j;
+
+# ifdef STATICROOTSLIB_INIT_IN_MAIN
+ GC_INIT();
+# endif
staticroot = libsrl_init();
+ if (NULL == staticroot) {
+ fprintf(stderr, "GC_malloc returned NULL\n");
+ return 2;
+ }
memset(staticroot, 0x42, sizeof(struct treenode));
GC_gcollect();
- for (i = 0; i < 10; ++i) {
- root[i] = libsrl_mktree(12);
- GC_gcollect();
- }
- for (i = 0; i < (int)sizeof(struct treenode); ++i) {
- if (staticroot[i] != 0x42)
- return -1;
- }
- for (i = 0; i < 10; ++i) {
- root[i] = libsrl_mktree(12);
- GC_gcollect();
- }
- for (i = 0; i < (int)sizeof(struct treenode); ++i) {
- if (staticroot[i] != 0x42)
- return -1;
+ for (j = 0; j < 4; j++) {
+ for (i = 0; i < (int)(sizeof(root) / sizeof(root[0])); ++i) {
+# ifdef STATICROOTSLIB2
+ *libsrl_getpelem2(i, j) = libsrl_mktree(12);
+# endif
+ *libsrl_getpelem(i, j) = libsrl_mktree(12);
+ ((j & 1) != 0 ? root_nz : root)[i] = libsrl_mktree(12);
+ GC_gcollect();
+ }
+ for (i = 0; i < (int)sizeof(struct treenode); ++i) {
+ if (staticroot[i] != 0x42) {
+ fprintf(stderr, "Memory check failed\n");
+ return -1;
+ }
+ }
}
return 0;
}
-
-#else
-
-/* Skip since symbols defined in staticrootslib are not visible */
-
-int main(void)
-{
- printf("staticrootstest skipped\n");
- return 0;
-}
-
-#endif
diff --git a/tests/subthread_create.c b/tests/subthread_create.c
index bff545b7..9a7095b6 100644
--- a/tests/subthread_create.c
+++ b/tests/subthread_create.c
@@ -1,10 +1,18 @@
+#ifdef HAVE_CONFIG_H
+ /* For PARALLEL_MARK */
+# include "config.h"
+#endif
+
#ifndef GC_THREADS
# define GC_THREADS
#endif
-
#include "gc.h"
-#include <atomic_ops.h>
+
+#ifdef PARALLEL_MARK
+# define AO_REQUIRE_CAS
+#endif
+#include "atomic_ops.h"
#include <stdio.h>
@@ -62,7 +70,7 @@ volatile AO_t thread_ended_cnt = 0;
DWORD thread_id;
th = CreateThread(NULL, 0, entry, (LPVOID)my_depth, 0, &thread_id);
if (th == NULL) {
- printf("Thread #%d creation failed: %d\n", thread_num,
+ fprintf(stderr, "Thread #%d creation failed: %d\n", thread_num,
(int)GetLastError());
exit(2);
}
@@ -96,7 +104,8 @@ int main(void)
DWORD thread_id;
th[i] = CreateThread(NULL, 0, entry, 0, 0, &thread_id);
if (th[i] == NULL) {
- printf("Thread creation failed: %d\n", (int)GetLastError());
+ fprintf(stderr, "Thread creation failed: %d\n",
+ (int)GetLastError());
exit(1);
}
# endif
@@ -112,7 +121,8 @@ int main(void)
}
# else
if (WaitForSingleObject(th[i], INFINITE) != WAIT_OBJECT_0) {
- printf("Failed to join thread: %d\n", (int)GetLastError());
+ fprintf(stderr, "Failed to join thread: %d\n",
+ (int)GetLastError());
CloseHandle(th[i]);
exit(1);
}
diff --git a/tests/test.c b/tests/test.c
index 863d0c97..12baa943 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -19,7 +19,7 @@
/* checking for some of the tests. */
# ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
# endif
# undef GC_BUILD
@@ -45,7 +45,7 @@
# include <winbase.h>
/* # define assert ASSERT */
# else
-# include <assert.h> /* Not normally used, but handy for debugging. */
+# include <assert.h> /* Not normally used, but handy for debugging. */
# endif
# include "gc_typed.h"
@@ -56,14 +56,25 @@
# include <windows.h>
# endif
-# ifdef GC_DLL
-# ifdef GC_PRINT_VERBOSE_STATS
-# define GC_print_stats VERBOSE
-# else
-# define GC_print_stats 0 /* Not exported from DLL */
- /* Redefine to 1 to generate output. */
-# endif
+#ifdef GC_PRINT_VERBOSE_STATS
+# define print_stats VERBOSE
+# define INIT_PRINT_STATS /* empty */
+#else
+ /* Use own variable as GC_print_stats might not be exported. */
+ static int print_stats = 0;
+# ifdef GC_READ_ENV_FILE
+ /* GETENV uses GC internal function in this case. */
+# define INIT_PRINT_STATS /* empty */
+# else
+# define INIT_PRINT_STATS \
+ { \
+ if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) \
+ print_stats = VERBOSE; \
+ else if (0 != GETENV("GC_PRINT_STATS")) \
+ print_stats = 1; \
+ }
# endif
+#endif /* !GC_PRINT_VERBOSE_STATS */
# ifdef PCR
# include "th/PCR_ThCrSec.h"
@@ -75,12 +86,40 @@
# include <pthread.h>
# endif
+# if (!defined(THREADS) || !defined(HANDLE_FORK) \
+ || (defined(DARWIN) && defined(MPROTECT_VDB) \
+ && !defined(NO_INCREMENTAL) && !defined(MAKE_BACK_GRAPH))) \
+ && !defined(NO_TEST_HANDLE_FORK) && !defined(TEST_HANDLE_FORK) \
+ && !defined(TEST_FORK_WITHOUT_ATFORK)
+# define NO_TEST_HANDLE_FORK
+# endif
+
+# ifndef NO_TEST_HANDLE_FORK
+# include <unistd.h>
+# ifdef HANDLE_FORK
+# define INIT_FORK_SUPPORT GC_set_handle_fork(1)
+ /* Causes abort in GC_init on pthread_atfork failure. */
+# elif !defined(TEST_FORK_WITHOUT_ATFORK)
+# define INIT_FORK_SUPPORT GC_set_handle_fork(-1)
+ /* Passing -1 implies fork() should be as well manually */
+ /* surrounded with GC_atfork_prepare/parent/child. */
+# endif
+# endif
+
+# ifndef INIT_FORK_SUPPORT
+# define INIT_FORK_SUPPORT /* empty */
+# endif
+
# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
static CRITICAL_SECTION incr_cs;
# endif
# include <stdarg.h>
+#ifndef GC_ALPHA_VERSION
+# define GC_ALPHA_VERSION GC_TMP_ALPHA_VERSION
+#endif
+
#define CHECH_GCLIB_VERSION \
if (GC_get_version() != ((GC_VERSION_MAJOR<<16) \
| (GC_VERSION_MINOR<<8) \
@@ -92,13 +131,16 @@
/* 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) \
- || defined(THREAD_LOCAL_ALLOC) \
+ || defined(PLATFORM_ANDROID) || defined(THREAD_LOCAL_ALLOC) \
|| (defined(MSWINCE) && !defined(GC_WINMAIN_REDIRECT))
-# define GC_COND_INIT() GC_INIT(); CHECH_GCLIB_VERSION
+# define GC_OPT_INIT GC_INIT()
#else
-# define GC_COND_INIT() CHECH_GCLIB_VERSION
+# define GC_OPT_INIT /* empty */
#endif
+#define GC_COND_INIT() \
+ INIT_FORK_SUPPORT; GC_OPT_INIT; CHECH_GCLIB_VERSION; INIT_PRINT_STATS
+
#define CHECK_OUT_OF_MEMORY(p) \
if ((p) == NULL) { \
GC_printf("Out of memory\n"); \
@@ -121,10 +163,8 @@ int realloc_count = 0;
void *GC_amiga_gctest_malloc_explicitly_typed(size_t lb, GC_descr d){
void *ret=GC_malloc_explicitly_typed(lb,d);
if(ret==NULL){
- if(!GC_dont_gc){
GC_gcollect();
ret=GC_malloc_explicitly_typed(lb,d);
- }
if(ret==NULL){
GC_printf("Out of memory, (typed allocations are not directly "
"supported with the GC_AMIGA_FASTALLOC option.)\n");
@@ -136,10 +176,8 @@ int realloc_count = 0;
void *GC_amiga_gctest_calloc_explicitly_typed(size_t a,size_t lb, GC_descr d){
void *ret=GC_calloc_explicitly_typed(a,lb,d);
if(ret==NULL){
- if(!GC_dont_gc){
GC_gcollect();
ret=GC_calloc_explicitly_typed(a,lb,d);
- }
if(ret==NULL){
GC_printf("Out of memory, (typed allocations are not directly "
"supported with the GC_AMIGA_FASTALLOC option.)\n");
@@ -202,7 +240,7 @@ sexpr cons (sexpr x, sexpr y)
r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra);
CHECK_OUT_OF_MEMORY(r);
for (p = (int *)r;
- ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) {
+ (word)p < (word)r + my_extra + sizeof(struct SEXPR); p++) {
if (*p) {
GC_printf("Found nonzero at %p - allocator is broken\n",
(void *)p);
@@ -364,7 +402,7 @@ sexpr gcj_ints(int low, int up)
}
#endif /* GC_GCJ_SUPPORT */
-/* To check uncollectable allocation we build lists with disguised cdr */
+/* To check uncollectible allocation we build lists with disguised cdr */
/* pointers, and make sure they don't go away. */
sexpr uncollectable_ints(int low, int up)
{
@@ -494,7 +532,10 @@ void check_marks_int_list(sexpr x)
{
DWORD thread_id;
HANDLE h;
- h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id);
+ h = GC_CreateThread((SECURITY_ATTRIBUTES *)NULL, (word)0,
+ tiny_reverse_test, NULL, (DWORD)0, &thread_id);
+ /* Explicitly specify types of the */
+ /* arguments to test the prototype. */
if (h == (HANDLE)NULL) {
GC_printf("Small thread creation failed %d\n",
(int)GetLastError());
@@ -582,7 +623,7 @@ void *GC_CALLBACK reverse_test_inner(void *data)
h[1999] = gcj_ints(1,200);
for (i = 0; i < 51; ++i)
h[1999] = gcj_reverse(h[1999]);
- /* Leave it as the reveresed list for now. */
+ /* Leave it as the reversed list for now. */
# else
h[1999] = ints(1,200);
# endif
@@ -701,9 +742,15 @@ size_t counter = 0;
# if !defined(MACOS)
GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0};
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ GC_FAR void *live_long_refs[MAX_FINALIZED] = { NULL };
+# endif
#else
/* Too big for THINK_C. have to allocate it dynamically. */
GC_word *live_indicators = 0;
+# ifndef GC_LONG_REFS_NOT_NEEDED
+# define GC_LONG_REFS_NOT_NEEDED
+# endif
#endif
int live_indicators_count = 0;
@@ -736,8 +783,10 @@ tn * mktree(int n)
result -> rchild -> lchild = tmp;
}
if (counter++ % 119 == 0) {
- int my_index;
- void *new_link;
+# ifndef GC_NO_FINALIZATION
+ int my_index;
+ void *new_link;
+# endif
{
# ifdef PCR
@@ -751,7 +800,9 @@ tn * mktree(int n)
# endif
/* Losing a count here causes erroneous report of failure. */
finalizable_count++;
- my_index = live_indicators_count++;
+# ifndef GC_NO_FINALIZATION
+ my_index = live_indicators_count++;
+# endif
# ifdef PCR
PCR_ThCrSec_ExitSys();
# endif
@@ -762,6 +813,7 @@ tn * mktree(int n)
# endif
}
+# ifndef GC_NO_FINALIZATION
GC_REGISTER_FINALIZER((void *)result, finalizer, (void *)(GC_word)n,
(GC_finalization_proc *)0, (void * *)0);
if (my_index >= MAX_FINALIZED) {
@@ -799,6 +851,37 @@ tn * mktree(int n)
GC_printf("GC_general_register_disappearing_link failed 2\n");
FAIL;
}
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) {
+ GC_printf("GC_register_long_link failed\n");
+ FAIL;
+ }
+ if (GC_move_long_link(&live_long_refs[my_index],
+ &live_long_refs[my_index]) != GC_SUCCESS) {
+ GC_printf("GC_move_long_link(link,link) failed\n");
+ FAIL;
+ }
+ new_link = live_long_refs[my_index];
+ if (GC_move_long_link(&live_long_refs[my_index],
+ &new_link) != GC_SUCCESS) {
+ GC_printf("GC_move_long_link(new_link) failed\n");
+ FAIL;
+ }
+ if (GC_unregister_long_link(&new_link) == 0) {
+ GC_printf("GC_unregister_long_link failed\n");
+ FAIL;
+ }
+ if (GC_move_long_link(&live_long_refs[my_index],
+ &new_link) != GC_NOT_FOUND) {
+ GC_printf("GC_move_long_link(new_link) failed 2\n");
+ FAIL;
+ }
+ if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) {
+ GC_printf("GC_register_long_link failed 2\n");
+ FAIL;
+ }
+# endif
+# endif
GC_reachable_here(result);
}
return(result);
@@ -1092,7 +1175,7 @@ void run_one_test(void)
}
collectable_count += 1;
if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) {
- GC_printf("GC_malloc(0) failed: GC_size returns %ld\n",
+ GC_printf("GC_malloc(0) failed: GC_size returns %lu\n",
(unsigned long)GC_size(GC_malloc(0)));
FAIL;
}
@@ -1167,7 +1250,7 @@ void run_one_test(void)
{
size_t i;
- GC_malloc(17);
+ (void)GC_malloc(17);
for (i = sizeof(GC_word); i < 512; i *= 2) {
GC_word result = (GC_word) GC_memalign(i, 17);
if (result % i != 0 || result == 0 || *(int *)result != 0) FAIL;
@@ -1200,9 +1283,9 @@ void run_one_test(void)
{
size_t i;
for (i = 0; i < 10000; ++i) {
- GC_MALLOC(0);
+ (void)GC_MALLOC(0);
GC_FREE(GC_MALLOC(0));
- GC_MALLOC_ATOMIC(0);
+ (void)GC_MALLOC_ATOMIC(0);
GC_FREE(GC_MALLOC_ATOMIC(0));
}
}
@@ -1228,10 +1311,36 @@ void run_one_test(void)
GC_free(GC_malloc_atomic(0));
GC_free(GC_malloc(0));
GC_free(GC_malloc_atomic(0));
+# ifndef NO_TEST_HANDLE_FORK
+ GC_atfork_prepare();
+ if (fork() != 0) {
+ GC_atfork_parent();
+ if (print_stats)
+ GC_log_printf("Forked child process (or failed)\n");
+ } else {
+ GC_atfork_child();
+ if (print_stats)
+ GC_log_printf("Started a child process\n");
+# ifdef THREADS
+# ifdef PARALLEL_MARK
+ GC_gcollect(); /* no parallel markers */
+# endif
+ GC_start_mark_threads();
+# endif
+ GC_gcollect();
+# ifdef THREADS
+ tiny_reverse_test(0);
+ GC_gcollect();
+# endif
+ if (print_stats)
+ GC_log_printf("Finished a child process\n");
+ exit(0);
+ }
+# endif
/* Repeated list reversal test. */
GET_TIME(start_time);
reverse_test();
- if (GC_print_stats) {
+ if (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",
@@ -1239,7 +1348,7 @@ void run_one_test(void)
}
# ifndef DBG_HDRS_ALL
typed_test();
- if (GC_print_stats) {
+ if (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",
@@ -1247,7 +1356,7 @@ void run_one_test(void)
}
# endif /* DBG_HDRS_ALL */
tree_test();
- if (GC_print_stats) {
+ if (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",
@@ -1255,7 +1364,7 @@ void run_one_test(void)
}
/* Run reverse_test a second time, so we hopefully notice corruption. */
reverse_test();
- if (GC_print_stats) {
+ if (print_stats) {
GET_TIME(reverse_time);
time_diff = MS_TIME_DIFF(reverse_time, start_time);
GC_log_printf(
@@ -1265,17 +1374,7 @@ void run_one_test(void)
/* GC_allocate_ml and GC_need_to_lock are no longer exported, and */
/* AO_fetch_and_add1() may be unavailable to update a counter. */
(void)GC_call_with_alloc_lock(inc_int_counter, &n_tests);
-# if defined(THREADS) && defined(HANDLE_FORK)
- if (fork() == 0) {
- GC_gcollect();
- tiny_reverse_test(0);
- GC_gcollect();
- if (GC_print_stats)
- GC_log_printf("Finished a child process\n");
- exit(0);
- }
-# endif
- if (GC_print_stats)
+ if (print_stats)
GC_log_printf("Finished %p\n", (void *)&start_time);
}
@@ -1285,9 +1384,14 @@ void check_heap_stats(void)
{
size_t max_heap_sz;
int i;
- int still_live;
-# ifdef FINALIZE_ON_DEMAND
+# ifndef GC_NO_FINALIZATION
+ int still_live;
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ int still_long_live = 0;
+# endif
+# ifdef FINALIZE_ON_DEMAND
int late_finalize_count = 0;
+# endif
# endif
# ifdef VERY_SMALL_CONFIG
@@ -1326,14 +1430,25 @@ void check_heap_stats(void)
while (GC_collect_a_little()) { }
for (i = 0; i < 16; i++) {
GC_gcollect();
-# ifdef FINALIZE_ON_DEMAND
- late_finalize_count +=
-# endif
+# ifndef GC_NO_FINALIZATION
+# ifdef FINALIZE_ON_DEMAND
+ late_finalize_count +=
+# endif
GC_invoke_finalizers();
+# endif
}
- if (GC_print_stats) {
- GC_log_printf("Primordial thread stack bottom: %p\n",
- (void *)GC_stackbottom);
+ if (print_stats) {
+ struct GC_stack_base sb;
+ int res = GC_get_stack_base(&sb);
+
+ if (res == GC_SUCCESS) {
+ GC_log_printf("Primordial thread stack bottom: %p\n", sb.mem_base);
+ } else if (res == GC_UNIMPLEMENTED) {
+ GC_log_printf("GC_get_stack_base() unimplemented\n");
+ } else {
+ GC_printf("GC_get_stack_base() failed: %d\n", res);
+ FAIL;
+ }
}
GC_printf("Completed %u tests\n", n_tests);
GC_printf("Allocated %d collectable objects\n", collectable_count);
@@ -1343,27 +1458,33 @@ void check_heap_stats(void)
GC_printf("Allocated %d stubborn objects\n", stubborn_count);
GC_printf("Finalized %d/%d objects - ",
finalized_count, finalizable_count);
-# ifdef FINALIZE_ON_DEMAND
+# ifndef GC_NO_FINALIZATION
+# ifdef FINALIZE_ON_DEMAND
if (finalized_count != late_finalize_count) {
GC_printf("Demand finalization error\n");
FAIL;
}
-# endif
- if (finalized_count > finalizable_count
- || finalized_count < finalizable_count/2) {
+# endif
+ if (finalized_count > finalizable_count
+ || finalized_count < finalizable_count/2) {
GC_printf("finalization is probably broken\n");
FAIL;
- } else {
+ } else {
GC_printf("finalization is probably ok\n");
- }
- still_live = 0;
- for (i = 0; i < MAX_FINALIZED; i++) {
+ }
+ still_live = 0;
+ for (i = 0; i < MAX_FINALIZED; i++) {
if (live_indicators[i] != 0) {
still_live++;
}
- }
- i = finalizable_count - finalized_count - still_live;
- if (0 != i) {
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ if (live_long_refs[i] != NULL) {
+ still_long_live++;
+ }
+# endif
+ }
+ i = finalizable_count - finalized_count - still_live;
+ if (0 != i) {
GC_printf("%d disappearing links remain and %d more objects "
"were not finalized\n", still_live, i);
if (i > 10) {
@@ -1371,7 +1492,13 @@ void check_heap_stats(void)
} else {
GC_printf("\tSlightly suspicious, but probably OK\n");
}
- }
+ }
+# ifndef GC_LONG_REFS_NOT_NEEDED
+ if (0 != still_long_live) {
+ GC_printf("%d 'long' links remain\n", still_long_live);
+ }
+# endif
+# endif
GC_printf("Total number of bytes allocated is %lu\n",
(unsigned long)GC_get_total_bytes());
GC_printf("Final heap size is %lu bytes\n",
@@ -1393,10 +1520,27 @@ void check_heap_stats(void)
(unsigned long)max_heap_sz);
FAIL;
}
+
+# ifndef GC_GET_HEAP_USAGE_NOT_NEEDED
+ /* Get global counters (just to check the functions work). */
+ GC_get_heap_usage_safe(NULL, NULL, NULL, NULL, NULL);
+ {
+ struct GC_prof_stats_s stats;
+ (void)GC_get_prof_stats(&stats, sizeof(stats));
+# ifdef THREADS
+ (void)GC_get_prof_stats_unsafe(&stats, sizeof(stats));
+# endif
+ }
+# endif
+
# ifdef THREADS
GC_unregister_my_thread(); /* just to check it works (for main) */
# endif
- GC_printf("Collector appears to work\n");
+ GC_printf("Completed %u collections", (unsigned)GC_get_gc_no());
+# ifdef PARALLEL_MARK
+ GC_printf(" (using %d marker threads)", GC_get_parallel() + 1);
+# endif
+ GC_printf("\n" "Collector appears to work\n");
}
#if defined(MACOS)
@@ -1480,21 +1624,6 @@ void GC_CALLBACK warn_proc(char *msg, GC_word p)
# ifndef MSWINCE
fflush(stdout);
# endif
-# ifdef LINT
- /* Entry points we should be testing, but aren't. */
- /* Some can be tested by defining GC_DEBUG at the top of this file */
- /* This is a bit SunOS4 specific. */
- GC_noop(GC_expand_hp, GC_add_roots, GC_clear_roots,
- GC_register_disappearing_link,
- GC_register_finalizer_ignore_self,
- GC_debug_register_displacement, GC_debug_change_stubborn,
- GC_debug_end_stubborn_change, GC_debug_malloc_uncollectable,
- GC_debug_free, GC_debug_realloc,
- GC_generic_malloc_words_small, GC_init,
- GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page,
- GC_set_max_heap_size, GC_get_bytes_since_gc,
- GC_get_total_bytes, GC_pre_incr, GC_post_incr);
-# endif
# ifdef MSWIN32
GC_win32_free_heap();
# endif
@@ -1770,7 +1899,6 @@ int main(void)
check_heap_stats();
(void)fflush(stdout);
pthread_attr_destroy(&attr);
- GC_printf("Completed %u collections\n", (unsigned)GC_get_gc_no());
# ifdef PTW32_STATIC_LIB
pthread_win32_thread_detach_np ();
pthread_win32_process_detach_np ();
diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc
index f1393729..1a413774 100644
--- a/tests/test_cpp.cc
+++ b/tests/test_cpp.cc
@@ -23,7 +23,7 @@ few minutes to complete.
***************************************************************************/
#ifdef HAVE_CONFIG_H
-# include "private/config.h"
+# include "config.h"
#endif
#undef GC_BUILD
@@ -58,10 +58,10 @@ extern "C" {
#endif
#ifdef GC_NAME_CONFLICT
-# define USE_GC UseGC
+# define USE_GC GC_NS_QUALIFY(UseGC)
struct foo * GC;
#else
-# define USE_GC GC
+# define USE_GC GC_NS_QUALIFY(GC)
#endif
#define my_assert( e ) \
@@ -70,9 +70,14 @@ extern "C" {
__LINE__ ); \
exit( 1 ); }
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define ATTR_UNUSED __attribute__((__unused__))
+#else
+# define ATTR_UNUSED /* empty */
+#endif
class A {public:
- /* An uncollectable class. */
+ /* An uncollectible class. */
A( int iArg ): i( iArg ) {}
void Test( int iArg ) {
@@ -80,8 +85,8 @@ class A {public:
int i;};
-class B: public gc, public A {public:
- /* A collectable class. */
+class B: public GC_NS_QUALIFY(gc), public A { public:
+ /* A collectible class. */
B( int j ): A( j ) {}
~B() {
@@ -93,8 +98,8 @@ class B: public gc, public A {public:
int B::deleting = 0;
-class C: public gc_cleanup, public A {public:
- /* A collectable class with cleanup and virtual multiple inheritance. */
+class C: public GC_NS_QUALIFY(gc_cleanup), public A { public:
+ /* A collectible class with cleanup and virtual multiple inheritance. */
C( int levelArg ): A( levelArg ), level( levelArg ) {
nAllocated++;
@@ -124,8 +129,8 @@ int C::nFreed = 0;
int C::nAllocated = 0;
-class D: public gc {public:
- /* A collectable class with a static member function to be used as
+class D: public GC_NS_QUALIFY(gc) { public:
+ /* A collectible class with a static member function to be used as
an explicit clean-up function supplied to ::new. */
D( int iArg ): i( iArg ) {
@@ -145,8 +150,8 @@ int D::nFreed = 0;
int D::nAllocated = 0;
-class E: public gc_cleanup {public:
- /* A collectable class with clean-up for use by F. */
+class E: public GC_NS_QUALIFY(gc_cleanup) { public:
+ /* A collectible class with clean-up for use by F. */
E() {
nAllocated++;}
@@ -161,7 +166,7 @@ int E::nAllocated = 0;
class F: public E {public:
- /* A collectable class with clean-up, a base with clean-up, and a
+ /* A collectible class with clean-up, a base with clean-up, and a
member with clean-up. */
F() {
@@ -187,13 +192,14 @@ void* Undisguise( GC_word i ) {
return (void*) ~ i;}
#ifdef MSWIN32
-int APIENTRY WinMain(
- HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow )
+int APIENTRY WinMain( HINSTANCE instance ATTR_UNUSED,
+ HINSTANCE prev ATTR_UNUSED, LPSTR cmd, int cmdShow ATTR_UNUSED )
{
- int argc;
+ int argc = 0;
char* argv[ 3 ];
- for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) {
+ if (cmd != 0)
+ for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) {
argv[ argc ] = strtok( argc == 1 ? cmd : 0, " \t" );
if (0 == argv[ argc ]) break;}
#elif defined(MACOS)
@@ -205,6 +211,9 @@ int APIENTRY WinMain(
int main( int argc, char* argv[] ) {
#endif
+ GC_set_all_interior_pointers(1);
+ /* needed due to C++ multiple inheritance used */
+
GC_INIT();
int i, iters, n;
@@ -212,12 +221,17 @@ int APIENTRY WinMain(
int *x = gc_allocator<int>().allocate(1);
int *xio;
xio = gc_allocator_ignore_off_page<int>().allocate(1);
+ (void)xio;
int **xptr = traceable_allocator<int *>().allocate(1);
# else
int *x = (int *)gc_alloc::allocate(sizeof(int));
# endif
*x = 29;
# ifndef DONT_USE_STD_ALLOCATOR
+ if (!xptr) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(3);
+ }
*xptr = x;
x = 0;
# endif
@@ -228,14 +242,14 @@ int APIENTRY WinMain(
for (iters = 1; iters <= n; iters++) {
GC_printf( "Starting iteration %d\n", iters );
- /* Allocate some uncollectable As and disguise their pointers.
+ /* Allocate some uncollectible As and disguise their pointers.
Later we'll check to see if the objects are still there. We're
- checking to make sure these objects really are uncollectable. */
+ checking to make sure these objects really are uncollectible. */
GC_word as[ 1000 ];
GC_word bs[ 1000 ];
for (i = 0; i < 1000; i++) {
- as[ i ] = Disguise( new (NoGC ) A( i ) );
- bs[ i ] = Disguise( new (NoGC) B( i ) );}
+ as[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) A(i) );
+ bs[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) B(i) ); }
/* Allocate a fair number of finalizable Cs, Ds, and Fs.
Later we'll check to make sure they've gone away. */
@@ -245,16 +259,21 @@ int APIENTRY WinMain(
D* d;
F* f;
d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i );
+ (void)d;
f = new F;
+ (void)f;
if (0 == i % 10) delete c;}
- /* Allocate a very large number of collectable As and Bs and
+ /* Allocate a very large number of collectible As and Bs and
drop the references to them immediately, forcing many
collections. */
for (i = 0; i < 1000000; i++) {
A* a;
a = new (USE_GC) A( i );
- B* b = new B( i );
+ (void)a;
+ B* b;
+ b = new B( i );
+ (void)b;
b = new (USE_GC) B( i );
if (0 == i % 10) {
B::Deleting( 1 );
@@ -265,7 +284,7 @@ int APIENTRY WinMain(
# endif
}
- /* Make sure the uncollectable As and Bs are still there. */
+ /* Make sure the uncollectible As and Bs are still there. */
for (i = 0; i < 1000; i++) {
A* a = (A*) Undisguise( as[ i ] );
B* b = (B*) Undisguise( bs[ i ] );
diff --git a/tests/tests.am b/tests/tests.am
index 9685ae67..131d4e9a 100644
--- a/tests/tests.am
+++ b/tests/tests.am
@@ -15,6 +15,9 @@ TESTS += gctest$(EXEEXT)
check_PROGRAMS += gctest
gctest_SOURCES = tests/test.c
gctest_LDADD = $(test_ldadd)
+if THREADS
+gctest_LDADD += $(THREADDLLIBS)
+endif
gctest_DEPENDENCIES = $(top_builddir)/libgc.la
TESTS += leaktest$(EXEEXT)
@@ -45,12 +48,20 @@ realloc_test_LDADD = $(test_ldadd)
TESTS += staticrootstest$(EXEEXT)
check_PROGRAMS += staticrootstest
staticrootstest_SOURCES = tests/staticrootstest.c
-staticrootstest_LDADD = $(test_ldadd) libstaticrootslib.la
-check_LTLIBRARIES += libstaticrootslib.la
-libstaticrootslib_la_SOURCES = tests/staticrootslib.c
-libstaticrootslib_la_LIBADD = $(test_ldadd)
-libstaticrootslib_la_LDFLAGS = -version-info 1:3:0 -no-undefined -rpath /nowhere
-libstaticrootslib_la_DEPENDENCIES = $(top_builddir)/libgc.la
+staticrootstest_CFLAGS = -DSTATICROOTSLIB2
+staticrootstest_LDADD = $(test_ldadd) libstaticrootslib_test.la \
+ libstaticrootslib2_test.la
+check_LTLIBRARIES += libstaticrootslib_test.la libstaticrootslib2_test.la
+libstaticrootslib_test_la_SOURCES = tests/staticrootslib.c
+libstaticrootslib_test_la_LIBADD = $(test_ldadd)
+libstaticrootslib_test_la_LDFLAGS = -version-info 1:3:0 -no-undefined \
+ -rpath /nowhere
+libstaticrootslib_test_la_DEPENDENCIES = $(top_builddir)/libgc.la
+libstaticrootslib2_test_la_SOURCES = tests/staticrootslib.c
+libstaticrootslib2_test_la_LIBADD = $(test_ldadd)
+libstaticrootslib2_test_la_CFLAGS = -DSTATICROOTSLIB2
+libstaticrootslib2_test_la_LDFLAGS = -version-info 1:3:0 -no-undefined \
+ -rpath /nowhere
if KEEP_BACK_PTRS
TESTS += tracetest$(EXEEXT)
@@ -63,22 +74,22 @@ if THREADS
TESTS += threadleaktest$(EXEEXT)
check_PROGRAMS += threadleaktest
threadleaktest_SOURCES = tests/thread_leak_test.c
-threadleaktest_LDADD = $(test_ldadd)
+threadleaktest_LDADD = $(test_ldadd) $(THREADDLLIBS)
TESTS += threadkey_test$(EXEEXT)
check_PROGRAMS += threadkey_test
threadkey_test_SOURCES = tests/threadkey_test.c
-threadkey_test_LDADD = $(test_ldadd)
+threadkey_test_LDADD = $(test_ldadd) $(THREADDLLIBS)
-TESTS += subthread_create$(EXEEXT)
-check_PROGRAMS += subthread_create
-subthread_create_SOURCES = tests/subthread_create.c
-subthread_create_LDADD = $(test_ldadd)
+TESTS += subthreadcreate_test$(EXEEXT)
+check_PROGRAMS += subthreadcreate_test
+subthreadcreate_test_SOURCES = tests/subthread_create.c
+subthreadcreate_test_LDADD = $(test_ldadd) $(THREADDLLIBS)
-TESTS += initsecondarythread$(EXEEXT)
-check_PROGRAMS += initsecondarythread
-initsecondarythread_SOURCES = tests/initsecondarythread.c
-initsecondarythread_LDADD = $(test_ldadd)
+TESTS += initsecondarythread_test$(EXEEXT)
+check_PROGRAMS += initsecondarythread_test
+initsecondarythread_test_SOURCES = tests/initsecondarythread.c
+initsecondarythread_test_LDADD = $(test_ldadd) $(THREADDLLIBS)
endif
if CPLUSPLUS
@@ -86,9 +97,9 @@ 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)
+test_cpp_LDADD = gc_cpp.o $(test_ldadd) $(CXXLIBS)
else
-test_cpp_LDADD = libgccpp.la $(test_ldadd)
+test_cpp_LDADD = libgccpp.la $(test_ldadd) $(CXXLIBS)
endif
endif
@@ -97,6 +108,10 @@ TESTS += disclaim_test
check_PROGRAMS += disclaim_test
disclaim_test_SOURCES = tests/disclaim_test.c
disclaim_test_LDADD = $(test_ldadd)
+if THREADS
+disclaim_test_LDADD += $(THREADDLLIBS)
+endif
+
TESTS += disclaim_bench
check_PROGRAMS += disclaim_bench
disclaim_bench_SOURCES = tests/disclaim_bench.c
diff --git a/tests/thread_leak_test.c b/tests/thread_leak_test.c
index 3c3fc1e8..845de00e 100644
--- a/tests/thread_leak_test.c
+++ b/tests/thread_leak_test.c
@@ -1,4 +1,8 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#ifndef GC_THREADS
# define GC_THREADS
#endif
@@ -59,7 +63,8 @@ int main(void) {
code = t[i] != NULL ? 0 : (int)GetLastError();
# endif
if (code != 0) {
- printf("Thread creation failed %d\n", code);
+ fprintf(stderr, "Thread creation failed %d\n", code);
+ exit(2);
}
}
@@ -71,7 +76,8 @@ int main(void) {
(int)GetLastError();
# endif
if (code != 0) {
- printf("Thread join failed %d\n", code);
+ fprintf(stderr, "Thread join failed %d\n", code);
+ exit(2);
}
}
diff --git a/tests/threadkey_test.c b/tests/threadkey_test.c
index c1106890..f87530e3 100644
--- a/tests/threadkey_test.c
+++ b/tests/threadkey_test.c
@@ -1,4 +1,8 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#ifndef GC_THREADS
# define GC_THREADS
#endif
@@ -86,8 +90,9 @@ int main (void)
pthread_t t;
void *res;
if (GC_pthread_create (&t, NULL, entry, NULL) == 0
- && (i & 1) != 0)
- GC_pthread_join (t, &res);
+ && (i & 1) != 0) {
+ (void)GC_pthread_join(t, &res);
+ }
}
return 0;
}
diff --git a/tests/trace_test.c b/tests/trace_test.c
index 923e0f88..79c353f7 100644
--- a/tests/trace_test.c
+++ b/tests/trace_test.c
@@ -18,7 +18,7 @@ struct treenode * mktree(int i) {
if (0 == i) return 0;
if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode));
if (r == NULL) {
- printf("Out of memory\n");
+ fprintf(stderr, "Out of memory\n");
exit(1);
}
r -> x = mktree(i-1);
diff --git a/thread_local_alloc.c b/thread_local_alloc.c
index 82523cd6..a35b0c3f 100644
--- a/thread_local_alloc.c
+++ b/thread_local_alloc.c
@@ -90,6 +90,7 @@ GC_INNER void GC_init_thread_local(GC_tlfs p)
GC_ASSERT(I_HOLD_LOCK());
if (!EXPECT(keys_initialized, TRUE)) {
+ GC_ASSERT((word)&GC_thread_key % sizeof(word) == 0);
if (0 != GC_key_create(&GC_thread_key, 0)) {
ABORT("Failed to create key for local allocator");
}
@@ -127,9 +128,6 @@ GC_INNER void GC_destroy_thread_local(GC_tlfs p)
{
/* We currently only do this from the thread itself or from */
/* the fork handler for a child process. */
-# ifndef HANDLE_FORK
- GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
-# endif
return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
return_freelists(p -> normal_freelists, GC_objfreelist);
# ifdef GC_GCJ_SUPPORT
@@ -177,8 +175,8 @@ GC_API void * GC_CALL GC_malloc(size_t bytes)
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(%u) = %p : %u\n",
- (unsigned)bytes, result, (unsigned)GC_gc_no);
+ GC_log_printf("GC_malloc(%lu) returned %p, recent GC #%lu\n",
+ (unsigned long)bytes, result, (unsigned long)GC_gc_no);
# endif
return result;
}
@@ -312,22 +310,16 @@ GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
/* Check that all thread-local free-lists in p are completely marked. */
void GC_check_tls_for(GC_tlfs p)
{
- ptr_t q;
int j;
for (j = 1; j < TINY_FREELISTS; ++j) {
- q = p -> ptrfree_freelists[j];
- if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
- q = p -> normal_freelists[j];
- if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+ GC_check_fl_marks(&p->ptrfree_freelists[j]);
+ GC_check_fl_marks(&p->normal_freelists[j]);
# ifdef GC_GCJ_SUPPORT
- q = p -> gcj_freelists[j];
- if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+ GC_check_fl_marks(&p->gcj_freelists[j]);
# endif
# ifdef ENABLE_DISCLAIM
- q = p -> finalized_freelists[j];
- if ((word)q > HBLKSIZE)
- GC_check_fl_marks(q);
+ GC_check_fl_marks(&p->finalized_freelists[j]);
# endif
}
}
diff --git a/tools/add_gc_prefix.c b/tools/add_gc_prefix.c
index ecf4ff45..8369d689 100644
--- a/tools/add_gc_prefix.c
+++ b/tools/add_gc_prefix.c
@@ -1,19 +1,20 @@
# include <stdio.h>
# include <gc.h>
-int main(argc, argv, envp)
-int argc;
-char ** argv;
-char ** envp;
+#ifndef GC_ALPHA_VERSION
+# define GC_ALPHA_VERSION GC_TMP_ALPHA_VERSION
+#endif
+
+int main(int argc, char ** argv)
{
int i;
for (i = 1; i < argc; i++) {
if (GC_ALPHA_VERSION == GC_NOT_ALPHA) {
- printf("gc%d.%d/%s ", GC_VERSION_MAJOR, GC_VERSION_MINOR, argv[i]);
+ printf("gc%d.%d/%s ", GC_VERSION_MAJOR, GC_VERSION_MINOR, argv[i]);
} else {
- printf("gc%d.%dalpha%d/%s ", GC_VERSION_MAJOR,
- GC_VERSION_MINOR, GC_ALPHA_VERSION, argv[i]);
+ printf("gc%d.%dalpha%d/%s ", GC_VERSION_MAJOR,
+ GC_VERSION_MINOR, GC_ALPHA_VERSION, argv[i]);
}
}
return(0);
diff --git a/tools/gcname.c b/tools/gcname.c
index 55b7c9fb..74be553c 100644
--- a/tools/gcname.c
+++ b/tools/gcname.c
@@ -1,13 +1,17 @@
#include <stdio.h>
#include <gc.h>
-int main()
+#ifndef GC_ALPHA_VERSION
+# define GC_ALPHA_VERSION GC_TMP_ALPHA_VERSION
+#endif
+
+int main(void)
{
if (GC_ALPHA_VERSION == GC_NOT_ALPHA) {
- printf("gc%d.%d", GC_VERSION_MAJOR, GC_VERSION_MINOR);
+ printf("gc%d.%d", GC_VERSION_MAJOR, GC_VERSION_MINOR);
} else {
- printf("gc%d.%dalpha%d", GC_VERSION_MAJOR,
- GC_VERSION_MINOR, GC_ALPHA_VERSION);
+ printf("gc%d.%dalpha%d", GC_VERSION_MAJOR,
+ GC_VERSION_MINOR, GC_ALPHA_VERSION);
}
return 0;
}
diff --git a/tools/if_mach.c b/tools/if_mach.c
index d3763b60..94053a66 100644
--- a/tools/if_mach.c
+++ b/tools/if_mach.c
@@ -1,11 +1,11 @@
/* Conditionally execute a command based on machine and OS from gcconfig.h */
-# include "private/gcconfig.h"
+# include "private/gc_priv.h"
# include <stdio.h>
# include <string.h>
# include <unistd.h>
-int main(int argc, char **argv, char **envp)
+int main(int argc, char **argv)
{
if (argc < 4) goto Usage;
if (strcmp(MACH_TYPE, argv[1]) != 0) return(0);
@@ -19,6 +19,6 @@ int main(int argc, char **argv, char **envp)
Usage:
fprintf(stderr, "Usage: %s mach_type os_type command\n", argv[0]);
fprintf(stderr, "Currently mach_type = %s, os_type = %s\n",
- MACH_TYPE, OS_TYPE);
+ MACH_TYPE, OS_TYPE);
return(1);
}
diff --git a/tools/if_not_there.c b/tools/if_not_there.c
index b71394c3..c0f095d0 100644
--- a/tools/if_not_there.c
+++ b/tools/if_not_there.c
@@ -1,6 +1,7 @@
/* Conditionally execute a command based if the file argv[1] doesn't exist */
-/* Except for execvp, we stick to ANSI C. */
-# include "private/gcconfig.h"
+/* Except for execvp, we stick to ANSI C. */
+
+# include "private/gc_priv.h"
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
@@ -8,7 +9,7 @@
#include <dirent.h>
#endif /* __DJGPP__ */
-int main(int argc, char **argv, char **envp)
+int main(int argc, char **argv)
{
FILE * f;
#ifdef __DJGPP__
@@ -22,8 +23,8 @@ int main(int argc, char **argv, char **envp)
}
#ifdef __DJGPP__
if ((d = opendir(argv[1])) != 0) {
- closedir(d);
- return(0);
+ closedir(d);
+ return(0);
}
#endif
printf("^^^^Starting command^^^^\n");
diff --git a/tools/setjmp_t.c b/tools/setjmp_t.c
index 0952fb03..1d9a1ad0 100644
--- a/tools/setjmp_t.c
+++ b/tools/setjmp_t.c
@@ -45,6 +45,14 @@ int getpagesize(void)
}
return((int)(result[0]));
}
+#elif defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
+# include <windows.h>
+ int getpagesize(void)
+ {
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwPageSize;
+ }
#endif
struct {
@@ -54,42 +62,43 @@ struct {
int * nested_sp(void)
{
- volatile int sp;
- sp = (int)&sp;
+ volatile word sp;
+ sp = (word)(&sp);
return (int *)sp;
}
int main(void)
{
- int dummy;
+ volatile word sp;
long ps = GETPAGESIZE();
jmp_buf b;
register int x = (int)strlen("a"); /* 1, slightly disguised */
static int y = 0;
+ sp = (word)(&sp);
printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE);
- if (nested_sp() < &dummy) {
+ if ((word)nested_sp() < sp) {
printf("Stack appears to grow down, which is the default.\n");
printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n",
- ((unsigned long)(&dummy) + ps) & ~(ps-1));
+ ((unsigned long)sp + ps) & ~(ps-1));
} else {
printf("Stack appears to grow up.\n");
printf("Define STACK_GROWS_UP in gc_private.h\n");
printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n",
- ((unsigned long)(&dummy) + ps) & ~(ps-1));
+ ((unsigned long)sp + ps) & ~(ps-1));
}
printf("Note that this may vary between machines of ostensibly\n");
printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n");
printf("On many machines the value is not fixed.\n");
printf("A good guess for ALIGNMENT on this machine is %ld.\n",
- (unsigned long)(&(a.a_b))-(unsigned long)(&a));
+ (unsigned long)((word)(&(a.a_b)) - (word)(&a)));
printf("The following is a very dubious test of one root marking"
" strategy.\n");
printf("Results may not be accurate/useful:\n");
/* Encourage the compiler to keep x in a callee-save register */
x = 2*x-1;
- printf("");
+ printf("\n");
x = 2*x-1;
setjmp(b);
if (y == 1) {
@@ -107,8 +116,8 @@ int main(void)
x = 2;
if (y == 1) longjmp(b,1);
printf("Some GC internal configuration stuff: \n");
- printf("\tWORDSZ = %d, ALIGNMENT = %d, GC_GRANULE_BYTES = %d\n",
- WORDSZ, ALIGNMENT, GC_GRANULE_BYTES);
+ printf("\tWORDSZ = %lu, ALIGNMENT = %d, GC_GRANULE_BYTES = %d\n",
+ (unsigned long)WORDSZ, ALIGNMENT, GC_GRANULE_BYTES);
printf("\tUsing one mark ");
# if defined(USE_MARK_BYTES)
printf("byte");
diff --git a/tools/threadlibs.c b/tools/threadlibs.c
index 6d84efdb..a97f91b8 100644
--- a/tools/threadlibs.c
+++ b/tools/threadlibs.c
@@ -15,8 +15,7 @@
* modified is included with the above copyright notice.
*/
-# include "gc_config_macros.h"
-# include "private/gcconfig.h"
+# include "private/gc_priv.h"
# include <stdio.h>
diff --git a/typd_mlc.c b/typd_mlc.c
index caba301e..00f78ce5 100644
--- a/typd_mlc.c
+++ b/typd_mlc.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
- * opyright (c) 1999-2000 by Hewlett-Packard Company. All rights reserved.
+ * Copyright (c) 1999-2000 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
@@ -126,6 +126,7 @@ STATIC signed_word GC_add_ext_descriptor(const GC_word * bm, word nbits)
word ed_size = GC_ed_size;
if (ed_size == 0) {
+ GC_ASSERT((word)&GC_ext_descriptors % sizeof(word) == 0);
GC_push_typed_structures = GC_push_typed_structures_proc;
UNLOCK();
new_size = ED_INITIAL_SIZE;
@@ -360,7 +361,7 @@ STATIC void GC_init_explicit_typing(void)
GC_eobjfreelist = (ptr_t *)GC_new_free_list_inner();
GC_explicit_kind = GC_new_kind_inner(
(void **)GC_eobjfreelist,
- (((word)WORDS_TO_BYTES(-1)) | GC_DS_PER_OBJECT),
+ (WORDS_TO_BYTES((word)-1) | GC_DS_PER_OBJECT),
TRUE, TRUE);
/* Descriptors are in the last word of the object. */
GC_typed_mark_proc_index = GC_new_proc_inner(GC_typed_mark_proc);
@@ -372,9 +373,7 @@ STATIC void GC_init_explicit_typing(void)
GC_MAKE_PROC(GC_array_mark_proc_index, 0),
FALSE, TRUE);
for (i = 0; i < WORDSZ/2; i++) {
- GC_descr d = (((word)(-1)) >> (WORDSZ - i)) << (WORDSZ - i);
- d |= GC_DS_BITMAP;
- GC_bm_table[i] = d;
+ GC_bm_table[i] = (((word)-1) << (WORDSZ - i)) | GC_DS_BITMAP;
}
UNLOCK();
}
@@ -394,7 +393,7 @@ STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr,
if (bm & 1) {
current = *current_p;
FIXUP_POINTER(current);
- if ((ptr_t)current >= least_ha && (ptr_t)current <= greatest_ha) {
+ if (current >= (word)least_ha && current <= (word)greatest_ha) {
PUSH_CONTENTS((ptr_t)current, mark_stack_ptr,
mark_stack_limit, (ptr_t)current_p, exit1);
}
@@ -406,12 +405,12 @@ STATIC mse * GC_typed_mark_proc(word * addr, mse * mark_stack_ptr,
/* we also can't overflow the mark stack unless we actually */
/* mark something. */
mark_stack_ptr++;
- if (mark_stack_ptr >= mark_stack_limit) {
+ if ((word)mark_stack_ptr >= (word)mark_stack_limit) {
mark_stack_ptr = GC_signal_mark_stack_overflow(mark_stack_ptr);
}
mark_stack_ptr -> mse_start = (ptr_t)(addr + WORDSZ);
- mark_stack_ptr -> mse_descr =
- GC_MAKE_PROC(GC_typed_mark_proc_index, env+1);
+ mark_stack_ptr -> mse_descr.w =
+ GC_MAKE_PROC(GC_typed_mark_proc_index, env + 1);
}
return(mark_stack_ptr);
}
@@ -457,7 +456,7 @@ STATIC mse * GC_push_complex_descriptor(word *addr, complex_descriptor *d,
for (i = 0; i < nelements; i++) {
msp++;
msp -> mse_start = current;
- msp -> mse_descr = descr;
+ msp -> mse_descr.w = descr;
current += sz;
}
return(msp);
@@ -522,12 +521,12 @@ STATIC mse * GC_array_mark_proc(word * addr, mse * mark_stack_ptr,
GC_mark_stack_too_small = TRUE;
new_mark_stack_ptr = orig_mark_stack_ptr + 1;
new_mark_stack_ptr -> mse_start = (ptr_t)addr;
- new_mark_stack_ptr -> mse_descr = sz | GC_DS_LENGTH;
+ new_mark_stack_ptr -> mse_descr.w = sz | GC_DS_LENGTH;
} else {
/* Push descriptor itself */
new_mark_stack_ptr++;
new_mark_stack_ptr -> mse_start = (ptr_t)(addr + nwords - 1);
- new_mark_stack_ptr -> mse_descr = sizeof(word) | GC_DS_LENGTH;
+ new_mark_stack_ptr -> mse_descr.w = sizeof(word) | GC_DS_LENGTH;
}
return new_mark_stack_ptr;
}
@@ -591,6 +590,7 @@ GC_API void * GC_CALL GC_malloc_explicitly_typed(size_t lb, GC_descr d)
lb += TYPD_EXTRA_BYTES;
if(SMALL_OBJ(lb)) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
lg = GC_size_map[lb];
opp = &(GC_eobjfreelist[lg]);
LOCK();
@@ -627,6 +627,7 @@ GC_API void * GC_CALL GC_malloc_explicitly_typed_ignore_off_page(size_t lb,
lb += TYPD_EXTRA_BYTES;
if( SMALL_OBJ(lb) ) {
+ GC_DBG_COLLECT_AT_MALLOC(lb);
lg = GC_size_map[lb];
opp = &(GC_eobjfreelist[lg]);
LOCK();
@@ -646,7 +647,7 @@ GC_API void * GC_CALL GC_malloc_explicitly_typed_ignore_off_page(size_t lb,
} else {
op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind);
if (op != NULL) {
- lg = BYTES_TO_WORDS(GC_size(op));
+ lg = BYTES_TO_GRANULES(GC_size(op));
((word *)op)[GRANULES_TO_WORDS(lg) - 1] = d;
}
}
@@ -714,13 +715,16 @@ GC_API void * GC_CALL GC_calloc_explicitly_typed(size_t n, size_t lb,
lp -> ld_descriptor = leaf.ld_descriptor;
((volatile word *)op)[GRANULES_TO_WORDS(lg) - 1] = (word)lp;
} else {
+# ifndef GC_NO_FINALIZATION
size_t lw = GRANULES_TO_WORDS(lg);
((word *)op)[lw - 1] = (word)complex_descr;
/* Make sure the descriptor is cleared once there is any danger */
/* it may have been collected. */
if (GC_general_register_disappearing_link((void * *)((word *)op+lw-1),
- op) == GC_NO_MEMORY) {
+ op) == GC_NO_MEMORY)
+# endif
+ {
/* Couldn't register it due to lack of memory. Punt. */
/* This will probably fail too, but gives the recovery code */
/* a chance. */
diff --git a/win32_threads.c b/win32_threads.c
index a6329ead..4d57bc5e 100644
--- a/win32_threads.c
+++ b/win32_threads.c
@@ -32,13 +32,22 @@
/* Allocation lock declarations. */
#if !defined(USE_PTHREAD_LOCKS)
GC_INNER CRITICAL_SECTION GC_allocate_ml;
- GC_INNER DWORD GC_lock_holder = NO_THREAD;
+# ifdef GC_ASSERTIONS
+ GC_INNER DWORD GC_lock_holder = NO_THREAD;
/* Thread id for current holder of allocation lock */
+# endif
#else
GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER;
- GC_INNER unsigned long GC_lock_holder = NO_THREAD;
+# ifdef GC_ASSERTIONS
+ GC_INNER unsigned long GC_lock_holder = NO_THREAD;
+# endif
#endif
+#undef CreateThread
+#undef ExitThread
+#undef _beginthreadex
+#undef _endthreadex
+
#ifdef GC_PTHREADS
# include <errno.h> /* for EAGAIN */
@@ -55,14 +64,12 @@
STATIC void GC_thread_exit_proc(void *arg);
# include <pthread.h>
+# ifdef CAN_CALL_ATFORK
+# include <unistd.h>
+# endif
#else
-# undef CreateThread
-# undef ExitThread
-# undef _beginthreadex
-# undef _endthreadex
-
# ifdef MSWINCE
/* Force DONT_USE_SIGNALANDWAIT implementation of PARALLEL_MARK */
/* for WinCE (since Win32 SignalObjectAndWait() is missing). */
@@ -172,7 +179,8 @@ STATIC DWORD GC_main_thread = 0;
struct GC_Thread_Rep {
union {
# ifndef GC_NO_THREADS_DISCOVERY
- AO_t in_use; /* Updated without lock. */
+ volatile AO_t in_use;
+ /* Updated without lock. */
/* We assert that unused */
/* entries have invalid ids of */
/* zero and zero stack fields. */
@@ -268,24 +276,27 @@ typedef volatile struct GC_Thread_Rep * GC_vthread;
*/
#ifndef GC_NO_THREADS_DISCOVERY
- STATIC AO_t GC_attached_thread = FALSE;
+ STATIC volatile AO_t GC_attached_thread = FALSE;
#endif
#if !defined(__GNUC__)
/* Return TRUE if an thread was attached since we last asked or */
/* since GC_attached_thread was explicitly reset. */
- GC_bool GC_started_thread_while_stopped(void)
+ GC_INNER GC_bool GC_started_thread_while_stopped(void)
{
# ifndef GC_NO_THREADS_DISCOVERY
- AO_t result;
-
if (GC_win32_dll_threads) {
- AO_nop_full(); /* Prior heap reads need to complete earlier. */
- result = AO_load(&GC_attached_thread);
- if (result) {
- AO_store(&GC_attached_thread, FALSE);
- return TRUE;
- }
+# ifdef AO_HAVE_compare_and_swap_release
+ if (AO_compare_and_swap_release(&GC_attached_thread, TRUE,
+ FALSE /* stored */))
+ return TRUE;
+# else
+ AO_nop_full(); /* Prior heap reads need to complete earlier. */
+ if (AO_load(&GC_attached_thread)) {
+ AO_store(&GC_attached_thread, FALSE);
+ return TRUE;
+ }
+# endif
}
# endif
return FALSE;
@@ -318,7 +329,8 @@ STATIC volatile LONG GC_max_thread_index = 0;
STATIC GC_thread GC_threads[THREAD_TABLE_SZ];
/* It may not be safe to allocate when we register the first thread. */
-/* Thus we allocated one statically. */
+/* Thus we allocated one statically. It does not contain any field we */
+/* need to push ("next" and "status" fields are unused). */
static struct GC_Thread_Rep first_thread;
static GC_bool first_thread_used = FALSE;
@@ -450,10 +462,8 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
(HANDLE*)&(me -> handle),
0 /* dwDesiredAccess */, FALSE /* bInheritHandle */,
DUPLICATE_SAME_ACCESS)) {
- if (GC_print_stats)
- GC_log_printf("DuplicateHandle failed with error code: %d\n",
- (int)GetLastError());
- ABORT("DuplicateHandle failed");
+ ABORT_ARG1("DuplicateHandle failed",
+ ": errcode= 0x%X", (unsigned)GetLastError());
}
# endif
me -> last_stack_min = ADDR_LIMIT;
@@ -579,8 +589,8 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void)
LOCK();
me = GC_lookup_thread_inner(GetCurrentThreadId());
UNLOCK();
- return (char *)tsd >= (char *)&me->tlfs
- && (char *)tsd < (char *)&me->tlfs + sizeof(me->tlfs);
+ return (word)tsd >= (word)(&me->tlfs)
+ && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs);
}
#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
@@ -607,14 +617,14 @@ GC_API int GC_CALL GC_thread_is_registered(void)
&& t != &first_thread) { \
GC_ASSERT(SMALL_OBJ(GC_size(t))); \
GC_remove_protection(HBLKPTR(t), 1, FALSE); \
- }
+ } else (void)0
#else
-# define UNPROTECT_THREAD(t)
+# define UNPROTECT_THREAD(t) (void)0
#endif
#ifdef CYGWIN32
# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id
-#elif defined(GC_WIN32_PTHREADS)
+#elif defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK)
# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p
#endif
@@ -623,10 +633,11 @@ GC_API int GC_CALL GC_thread_is_registered(void)
/* in the table with the same win32 id. */
/* This is OK, but we need a way to delete a specific one. */
/* Assumes we hold the allocation lock unless */
-/* GC_win32_dll_threads is set. */
+/* GC_win32_dll_threads is set. Does not actually free */
+/* GC_thread entry (only unlinks it). */
/* If GC_win32_dll_threads is set it should be called from the */
/* thread being deleted. */
-STATIC void GC_delete_gc_thread(GC_vthread t)
+STATIC void GC_delete_gc_thread_no_free(GC_vthread t)
{
# ifndef MSWINCE
CloseHandle(t->handle);
@@ -641,9 +652,6 @@ STATIC void GC_delete_gc_thread(GC_vthread t)
/* see GC_stop_world() for the information. */
t -> stack_base = 0;
t -> id = 0;
-# ifdef GC_PTHREADS
- GC_PTHREAD_PTRVAL(t->pthread_id) = 0;
-# endif
AO_store_release(&t->tm.in_use, FALSE);
} else
# endif
@@ -664,7 +672,6 @@ STATIC void GC_delete_gc_thread(GC_vthread t)
} else {
prev -> tm.next = p -> tm.next;
}
- GC_INTERNAL_FREE(p);
}
}
@@ -677,12 +684,12 @@ STATIC void GC_delete_gc_thread(GC_vthread t)
STATIC void GC_delete_thread(DWORD id)
{
if (GC_win32_dll_threads) {
- GC_thread t = GC_lookup_thread_inner(id);
+ GC_vthread t = GC_lookup_thread_inner(id);
if (0 == t) {
WARN("Removing nonexistent thread, id = %" WARN_PRIdPTR "\n", id);
} else {
- GC_delete_gc_thread(t);
+ GC_delete_gc_thread_no_free(t);
}
} else {
word hv = THREAD_TABLE_INDEX(id);
@@ -733,9 +740,13 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb)
LOCK();
me = GC_lookup_thread_inner(thread_id);
if (me == 0) {
- GC_register_my_thread_inner(sb, thread_id);
# ifdef GC_PTHREADS
+ me = GC_register_my_thread_inner(sb, thread_id);
me -> flags |= DETACHED;
+ /* Treat as detached, since we do not need to worry about */
+ /* pointer results. */
+# else
+ GC_register_my_thread_inner(sb, thread_id);
# endif
UNLOCK();
return GC_SUCCESS;
@@ -757,6 +768,30 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb)
}
}
+/* Similar to that in pthread_support.c. */
+STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
+{
+ GC_ASSERT(I_HOLD_LOCK());
+ if (GC_incremental && GC_collection_in_progress()) {
+ word old_gc_no = GC_gc_no;
+
+ /* Make sure that no part of our stack is still on the mark stack, */
+ /* since it's about to be unmapped. */
+ do {
+ ENTER_GC();
+ GC_in_thread_creation = TRUE;
+ GC_collect_a_little_inner(1);
+ GC_in_thread_creation = FALSE;
+ EXIT_GC();
+
+ UNLOCK();
+ Sleep(0); /* yield */
+ LOCK();
+ } while (GC_incremental && GC_collection_in_progress()
+ && (wait_for_all || old_gc_no == GC_gc_no));
+ }
+}
+
GC_API int GC_CALL GC_unregister_my_thread(void)
{
DCL_LOCK_STATE;
@@ -765,15 +800,11 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
GC_log_printf("Unregistering thread 0x%lx\n", (long)GetCurrentThreadId());
# endif
- /* FIXME: is GC_wait_for_gc_completion(FALSE) needed here? */
if (GC_win32_dll_threads) {
# if defined(THREAD_LOCAL_ALLOC)
/* Can't happen: see GC_use_threads_discovery(). */
GC_ASSERT(FALSE);
# else
-# ifdef GC_PTHREADS
- /* FIXME: If not DETACHED then just set FINISHED. */
-# endif
/* FIXME: Should we just ignore this? */
GC_delete_thread(GetCurrentThreadId());
# endif
@@ -784,12 +815,14 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
DWORD thread_id = GetCurrentThreadId();
LOCK();
+ GC_wait_for_gc_completion(FALSE);
# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS)
me = GC_lookup_thread_inner(thread_id);
CHECK_LOOKUP_MY_THREAD(me);
GC_ASSERT(!KNOWN_FINISHED(me));
# endif
# if defined(THREAD_LOCAL_ALLOC)
+ GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs);
GC_destroy_thread_local(&(me->tlfs));
# endif
# ifdef GC_PTHREADS
@@ -800,6 +833,10 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
/* else */ {
GC_delete_thread(thread_id);
}
+# if defined(THREAD_LOCAL_ALLOC)
+ /* It is required to call remove_specific defined in specific.c. */
+ GC_remove_specific(GC_thread_key);
+# endif
UNLOCK();
}
return GC_SUCCESS;
@@ -853,13 +890,16 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
/* Adjust our stack base value (this could happen unless */
/* GC_get_stack_base() was used which returned GC_SUCCESS). */
GC_ASSERT(me -> stack_base != NULL);
- if (me -> stack_base < (ptr_t)(&stacksect))
+ if ((word)me->stack_base < (word)(&stacksect))
me -> stack_base = (ptr_t)(&stacksect);
if (me -> thread_blocked_sp == NULL) {
/* We are not inside GC_do_blocking() - do nothing more. */
UNLOCK();
- return fn(client_data);
+ client_data = fn(client_data);
+ /* Prevent treating the above as a tail call. */
+ GC_noop1((word)(&stacksect));
+ return client_data; /* result */
}
/* Setup new "stack section". */
@@ -902,7 +942,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE)
/* It appears pthread_t is really a pointer type ... */
# define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \
- (GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] = (win32_id))
+ (void)(GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] = (win32_id))
# define GET_PTHREAD_MAP_CACHE(pthread_id) \
GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)]
@@ -958,23 +998,130 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
#endif /* GC_PTHREADS */
+#ifdef CAN_HANDLE_FORK
+ /* Similar to that in pthread_support.c but also rehashes the table */
+ /* since hash map key (thread_id) differs from that in the parent. */
+ STATIC void GC_remove_all_threads_but_me(void)
+ {
+ int hv;
+ GC_thread p, next, me = NULL;
+ DWORD thread_id;
+ pthread_t pthread_id = pthread_self(); /* same as in parent */
+
+ GC_ASSERT(!GC_win32_dll_threads);
+ for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
+ for (p = GC_threads[hv]; 0 != p; p = next) {
+ next = p -> tm.next;
+ if (THREAD_EQUAL(p -> pthread_id, pthread_id)) {
+ GC_ASSERT(me == NULL);
+ me = p;
+ p -> tm.next = 0;
+ } else {
+# ifdef THREAD_LOCAL_ALLOC
+ if ((p -> flags & FINISHED) == 0) {
+ GC_destroy_thread_local(&p->tlfs);
+ GC_remove_specific(GC_thread_key);
+ }
+# endif
+ if (&first_thread != p)
+ GC_INTERNAL_FREE(p);
+ }
+ }
+ GC_threads[hv] = NULL;
+ }
+
+ /* Put "me" back to GC_threads. */
+ GC_ASSERT(me != NULL);
+ thread_id = GetCurrentThreadId(); /* differs from that in parent */
+ GC_threads[THREAD_TABLE_INDEX(thread_id)] = me;
+
+ /* Update Win32 thread Id and handle. */
+ me -> id = thread_id;
+# ifndef MSWINCE
+ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), (HANDLE *)&me->handle,
+ 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */,
+ DUPLICATE_SAME_ACCESS))
+ ABORT("DuplicateHandle failed");
+# endif
+
+# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
+ /* For Cygwin, we need to re-assign thread-local pointer to */
+ /* 'tlfs' (it is OK to call GC_destroy_thread_local and */
+ /* GC_free_internal before this action). */
+ if (GC_setspecific(GC_thread_key, &me->tlfs) != 0)
+ ABORT("GC_setspecific failed (in child)");
+# endif
+ }
+
+ static void fork_prepare_proc(void)
+ {
+ LOCK();
+# ifdef PARALLEL_MARK
+ if (GC_parallel)
+ GC_wait_for_reclaim();
+# endif
+ GC_wait_for_gc_completion(TRUE);
+# ifdef PARALLEL_MARK
+ if (GC_parallel)
+ GC_acquire_mark_lock();
+# endif
+ }
+
+ static void fork_parent_proc(void)
+ {
+# ifdef PARALLEL_MARK
+ if (GC_parallel)
+ GC_release_mark_lock();
+# endif
+ UNLOCK();
+ }
+
+ static void fork_child_proc(void)
+ {
+# ifdef PARALLEL_MARK
+ if (GC_parallel) {
+ GC_release_mark_lock();
+ GC_parallel = FALSE; /* or GC_markers_m1 = 0 */
+ /* Turn off parallel marking in the child, since we are */
+ /* probably just going to exec, and we would have to */
+ /* restart mark threads. */
+ }
+# endif
+ GC_remove_all_threads_but_me();
+ UNLOCK();
+ }
+
+ /* Routines for fork handling by client (no-op if pthread_atfork works). */
+ GC_API void GC_CALL GC_atfork_prepare(void)
+ {
+ if (GC_handle_fork <= 0)
+ fork_prepare_proc();
+ }
+
+ GC_API void GC_CALL GC_atfork_parent(void)
+ {
+ if (GC_handle_fork <= 0)
+ fork_parent_proc();
+ }
+
+ GC_API void GC_CALL GC_atfork_child(void)
+ {
+ if (GC_handle_fork <= 0)
+ fork_child_proc();
+ }
+#endif /* CAN_HANDLE_FORK */
+
void GC_push_thread_structures(void)
{
GC_ASSERT(I_HOLD_LOCK());
# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
- /* Unlike the other threads implementations, the thread table here */
- /* contains no pointers to the collectable heap. Thus we have */
- /* no private structures we need to preserve. */
-# ifdef GC_PTHREADS
- int i; /* pthreads may keep a pointer in the thread exit value */
- LONG my_max = GC_get_max_thread_index();
-
- for (i = 0; i <= my_max; i++)
- if (dll_thread_table[i].tm.in_use)
- GC_push_all((ptr_t)&(dll_thread_table[i].status),
- (ptr_t)(&(dll_thread_table[i].status)+1));
-# endif
+ /* Unlike the other threads implementations, the thread table */
+ /* here contains no pointers to the collectible heap (note also */
+ /* that GC_PTHREADS is incompatible with DllMain-based thread */
+ /* registration). Thus we have no private structures we need */
+ /* to preserve. */
} else
# endif
/* else */ {
@@ -982,7 +1129,7 @@ void GC_push_thread_structures(void)
}
# if defined(THREAD_LOCAL_ALLOC)
GC_push_all((ptr_t)(&GC_thread_key),
- (ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key));
+ (ptr_t)(&GC_thread_key) + sizeof(GC_thread_key));
/* Just in case we ever use our own TLS implementation. */
# endif
}
@@ -1008,7 +1155,7 @@ STATIC void GC_suspend(GC_thread t)
/* this breaks pthread_join on Cygwin, which is guaranteed to */
/* only see user pthreads */
GC_ASSERT(GC_win32_dll_threads);
- GC_delete_gc_thread(t);
+ GC_delete_gc_thread_no_free(t);
# endif
return;
}
@@ -1065,10 +1212,9 @@ GC_INNER void GC_stop_world(void)
GC_ASSERT(!GC_write_disabled);
EnterCriticalSection(&GC_write_cs);
/* It's not allowed to call GC_printf() (and friends) here down to */
- /* LeaveCriticalSection (same applies recursively to */
- /* GC_get_max_thread_index(), GC_suspend(), GC_delete_gc_thread() */
- /* (only if GC_win32_dll_threads), GC_size() and */
- /* GC_remove_protection()). */
+ /* LeaveCriticalSection (same applies recursively to GC_suspend, */
+ /* GC_delete_gc_thread_no_free, GC_get_max_thread_index, GC_size */
+ /* and GC_remove_protection). */
# ifdef GC_ASSERTIONS
GC_write_disabled = TRUE;
# endif
@@ -1080,7 +1226,7 @@ GC_INNER void GC_stop_world(void)
/* Any threads being created during this loop will end up setting */
/* GC_attached_thread when they start. This will force marking */
/* to restart. This is not ideal, but hopefully correct. */
- GC_attached_thread = FALSE;
+ AO_store(&GC_attached_thread, FALSE);
my_max = (int)GC_get_max_thread_index();
for (i = 0; i <= my_max; i++) {
GC_vthread t = dll_thread_table + i;
@@ -1211,14 +1357,13 @@ static GC_bool may_be_in_stack(ptr_t s)
STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
{
- int dummy;
ptr_t sp, stack_min;
struct GC_traced_stack_sect_s *traced_stack_sect =
thread -> traced_stack_sect;
if (thread -> id == me) {
GC_ASSERT(thread -> thread_blocked_sp == NULL);
- sp = (ptr_t) &dummy;
+ sp = GC_approx_sp();
} else if ((sp = thread -> thread_blocked_sp) == NULL) {
/* Use saved sp value for blocked threads. */
/* For unblocked threads call GetThreadContext(). */
@@ -1229,10 +1374,10 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
/* Push all registers that might point into the heap. Frame */
/* pointer registers are included in case client code was */
- /* compiled with the 'omit frame pointer' optimisation. */
+ /* compiled with the 'omit frame pointer' optimization. */
# define PUSH1(reg) GC_push_one((word)context.reg)
-# define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2)
-# define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4)
+# define PUSH2(r1,r2) (PUSH1(r1), PUSH1(r2))
+# define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4))
# if defined(I386)
PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp);
sp = (ptr_t)context.Esp;
@@ -1293,21 +1438,23 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
/* First, adjust the latest known minimum stack address if we */
/* are inside GC_call_with_gc_active(). */
if (traced_stack_sect != NULL &&
- thread -> last_stack_min > (ptr_t)traced_stack_sect) {
+ (word)thread->last_stack_min > (word)traced_stack_sect) {
UNPROTECT_THREAD(thread);
thread -> last_stack_min = (ptr_t)traced_stack_sect;
}
- if (sp < thread -> stack_base && sp >= thread -> last_stack_min) {
+ if ((word)sp < (word)thread->stack_base
+ && (word)sp >= (word)thread->last_stack_min) {
stack_min = sp;
} else {
/* In the current thread it is always safe to use sp value. */
if (may_be_in_stack(thread -> id == me &&
- sp < thread -> last_stack_min ?
+ (word)sp < (word)thread->last_stack_min ?
sp : thread -> last_stack_min)) {
stack_min = last_info.BaseAddress;
/* Do not probe rest of the stack if sp is correct. */
- if (sp < stack_min || sp >= thread->stack_base)
+ if ((word)sp < (word)stack_min
+ || (word)sp >= (word)thread->stack_base)
stack_min = GC_get_stack_min(thread -> last_stack_min);
} else {
/* Stack shrunk? Is this possible? */
@@ -1320,10 +1467,12 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
GC_ASSERT(GC_dont_query_stack_min
|| stack_min == GC_get_stack_min(thread -> stack_base)
- || (sp >= stack_min && stack_min < thread -> stack_base
- && stack_min > GC_get_stack_min(thread -> stack_base)));
+ || ((word)sp >= (word)stack_min
+ && (word)stack_min < (word)thread->stack_base
+ && (word)stack_min
+ > (word)GC_get_stack_min(thread -> stack_base)));
- if (sp >= stack_min && sp < thread->stack_base) {
+ if ((word)sp >= (word)stack_min && (word)sp < (word)thread->stack_base) {
# ifdef DEBUG_THREADS
GC_log_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n",
(int)thread -> id, sp, thread -> stack_base, (int)me);
@@ -1333,8 +1482,8 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
/* If not current thread then it is possible for sp to point to */
/* the guarded (untouched yet) page just below the current */
/* stack_min of the thread. */
- if (thread -> id == me || sp >= thread->stack_base
- || sp + GC_page_size < stack_min)
+ if (thread -> id == me || (word)sp >= (word)thread->stack_base
+ || (word)(sp + GC_page_size) < (word)stack_min)
WARN("Thread stack pointer %p out of range, pushing everything\n",
sp);
# ifdef DEBUG_THREADS
@@ -1388,10 +1537,9 @@ GC_INNER void GC_push_all_stacks(void)
}
}
# ifndef SMALL_CONFIG
- if (GC_print_stats == VERBOSE) {
- GC_log_printf("Pushed %d thread stacks%s\n", nthreads,
- GC_win32_dll_threads ? " based on DllMain thread tracking" : "");
- }
+ GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks%s\n", nthreads,
+ GC_win32_dll_threads ?
+ " based on DllMain thread tracking" : "");
# endif
if (!found_me && !GC_in_thread_creation)
ABORT("Collecting from unknown thread");
@@ -1441,7 +1589,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
for (i = 0; i <= my_max; i++) {
ptr_t s = (ptr_t)(dll_thread_table[i].stack_base);
- if (s > start && s < current_min) {
+ if ((word)s > (word)start && (word)s < (word)current_min) {
/* Update address of last_stack_min. */
plast_stack_min = (ptr_t * /* no volatile */)
&dll_thread_table[i].last_stack_min;
@@ -1455,7 +1603,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
for (t = GC_threads[i]; t != 0; t = t -> tm.next) {
ptr_t s = t -> stack_base;
- if (s > start && s < current_min) {
+ if ((word)s > (word)start && (word)s < (word)current_min) {
/* Update address of last_stack_min. */
plast_stack_min = &t -> last_stack_min;
thread = t; /* Remember current thread to unprotect. */
@@ -1464,12 +1612,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
}
# ifdef PARALLEL_MARK
- for (i = 0; i < GC_markers - 1; ++i) {
+ for (i = 0; i < GC_markers_m1; ++i) {
ptr_t s = marker_sp[i];
# ifdef IA64
/* FIXME: not implemented */
# endif
- if (s > start && s < current_min) {
+ if ((word)s > (word)start && (word)s < (word)current_min) {
GC_ASSERT(marker_last_stack_min[i] != NULL);
plast_stack_min = &marker_last_stack_min[i];
current_min = s;
@@ -1485,7 +1633,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
return;
}
- GC_ASSERT(current_min > start && plast_stack_min != NULL);
+ GC_ASSERT((word)current_min > (word)start && plast_stack_min != NULL);
# ifdef MSWINCE
if (GC_dont_query_stack_min) {
*lo = GC_wince_evaluate_stack_min(current_min);
@@ -1494,7 +1642,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
# endif
- if (current_min > limit && !may_be_in_stack(limit)) {
+ if ((word)current_min > (word)limit && !may_be_in_stack(limit)) {
/* Skip the rest since the memory region at limit address is */
/* not a stack (so the lowest address of the found stack would */
/* be above the limit value anyway). */
@@ -1527,6 +1675,18 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# define GC_PTHREADS_PARAMARK
# endif
+# if !defined(GC_PTHREADS_PARAMARK) && defined(DONT_USE_SIGNALANDWAIT)
+ STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0};
+ /* Events with manual reset (one for each */
+ /* mark helper). */
+
+ STATIC DWORD GC_marker_Id[MAX_MARKERS - 1] = {0};
+ /* This table is used for mapping helper */
+ /* threads ID to mark helper index (linear */
+ /* search is used since the mapping contains */
+ /* only a few entries). */
+# endif
+
/* GC_mark_thread() is the same as in pthread_support.c */
# ifdef GC_PTHREADS_PARAMARK
STATIC void * GC_mark_thread(void * id)
@@ -1545,6 +1705,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# ifdef IA64
marker_bsp[(word)id] = GC_save_regs_in_stack();
# endif
+# if !defined(GC_PTHREADS_PARAMARK) && defined(DONT_USE_SIGNALANDWAIT)
+ GC_marker_Id[(word)id] = GetCurrentThreadId();
+# endif
for (;; ++my_mark_no) {
if (my_mark_no - GC_mark_no > (word)2) {
@@ -1560,12 +1723,17 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
}
-# ifdef GC_ASSERTIONS
- GC_INNER unsigned long GC_mark_lock_holder = NO_THREAD;
+# ifndef GC_ASSERTIONS
+# define SET_MARK_LOCK_HOLDER (void)0
+# define UNSET_MARK_LOCK_HOLDER (void)0
# endif
/* GC_mark_threads[] is unused here unlike that in pthread_support.c */
+# ifndef CAN_HANDLE_FORK
+# define available_markers_m1 GC_markers_m1
+# endif
+
# ifdef GC_PTHREADS_PARAMARK
# include <pthread.h>
@@ -1573,35 +1741,57 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# define NUMERIC_THREAD_ID(id) (unsigned long)GC_PTHREAD_PTRVAL(id)
# endif
- /* start_mark_threads() is the same as in pthread_support.c except for: */
- /* - GC_markers value is adjusted already; */
- /* - thread stack is assumed to be large enough; and */
- /* - statistics about the number of marker threads is printed outside. */
- static void start_mark_threads(void)
+ /* start_mark_threads is the same as in pthread_support.c except */
+ /* for thread stack that is assumed to be large enough. */
+# ifdef CAN_HANDLE_FORK
+ static int available_markers_m1 = 0;
+# define start_mark_threads GC_start_mark_threads
+ GC_API void GC_CALL
+# else
+ static void
+# endif
+ start_mark_threads(void)
{
int i;
pthread_attr_t attr;
pthread_t new_thread;
- if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
+ GC_ASSERT(I_DONT_HOLD_LOCK());
+# ifdef CAN_HANDLE_FORK
+ if (available_markers_m1 <= 0 || GC_parallel) return;
+ /* Skip if parallel markers disabled or already started. */
+# endif
+ if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
ABORT("pthread_attr_setdetachstate failed");
- for (i = 0; i < GC_markers - 1; ++i) {
+ for (i = 0; i < available_markers_m1; ++i) {
marker_last_stack_min[i] = ADDR_LIMIT;
if (0 != pthread_create(&new_thread, &attr,
GC_mark_thread, (void *)(word)i)) {
WARN("Marker thread creation failed.\n", 0);
/* Don't try to create other marker threads. */
- GC_markers = i + 1;
- if (i == 0) GC_parallel = FALSE;
break;
}
}
+ GC_markers_m1 = i;
pthread_attr_destroy(&attr);
+ GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
}
+# ifdef GC_ASSERTIONS
+ STATIC unsigned long GC_mark_lock_holder = NO_THREAD;
+# define SET_MARK_LOCK_HOLDER \
+ (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
+# define UNSET_MARK_LOCK_HOLDER \
+ do { \
+ GC_ASSERT(GC_mark_lock_holder \
+ == NUMERIC_THREAD_ID(pthread_self())); \
+ GC_mark_lock_holder = NO_THREAD; \
+ } while (0)
+# endif /* GC_ASSERTIONS */
+
static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
@@ -1611,11 +1801,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* as in pthread_support.c except that GC_generic_lock() is not used. */
# ifdef LOCK_STATS
- AO_t GC_block_count = 0;
+ volatile AO_t GC_block_count = 0;
# endif
GC_INNER void GC_acquire_mark_lock(void)
{
+ GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self()));
if (pthread_mutex_lock(&mark_mutex) != 0) {
ABORT("pthread_mutex_lock failed");
}
@@ -1623,17 +1814,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
(void)AO_fetch_and_add1(&GC_block_count);
# endif
/* GC_generic_lock(&mark_mutex); */
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_release_mark_lock(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_mutex_unlock(&mark_mutex) != 0) {
ABORT("pthread_mutex_unlock failed");
}
@@ -1646,17 +1832,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* since the free-list link may be ignored. */
STATIC void GC_wait_builder(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_wait_for_reclaim(void)
@@ -1680,17 +1861,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_INNER void GC_wait_marker(void)
{
- GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self()));
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self());
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_notify_all_marker(void)
@@ -1702,18 +1878,6 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# else /* ! GC_PTHREADS_PARAMARK */
-# ifdef DONT_USE_SIGNALANDWAIT
- STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0};
- /* Events with manual reset (one for each */
- /* mark helper). */
-
- STATIC DWORD GC_marker_Id[MAX_MARKERS - 1] = {0};
- /* This table is used for mapping helper */
- /* threads ID to mark helper index (linear */
- /* search is used since the mapping contains */
- /* only a few entries). */
-# endif
-
# ifndef MARK_THREAD_STACK_SIZE
# define MARK_THREAD_STACK_SIZE 0 /* default value */
# endif
@@ -1735,19 +1899,18 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# endif
# ifdef DONT_USE_SIGNALANDWAIT
- /* Initialize GC_marker_cv[] and GC_marker_Id[] fully before */
- /* starting the first helper thread. */
- for (i = 0; i < GC_markers - 1; ++i) {
- GC_marker_Id[i] = GetCurrentThreadId();
+ /* Initialize GC_marker_cv[] fully before starting the */
+ /* first helper thread. */
+ for (i = 0; i < GC_markers_m1; ++i) {
if ((GC_marker_cv[i] = CreateEvent(NULL /* attrs */,
TRUE /* isManualReset */,
FALSE /* initialState */,
NULL /* name (A/W) */)) == (HANDLE)0)
- ABORT("CreateEvent() failed");
+ ABORT("CreateEvent failed");
}
# endif
- for (i = 0; i < GC_markers - 1; ++i) {
+ for (i = 0; i < GC_markers_m1; ++i) {
marker_last_stack_min[i] = ADDR_LIMIT;
# ifdef MSWINCE
/* There is no _beginthreadex() in WinCE. */
@@ -1778,23 +1941,34 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# endif
}
- /* Adjust GC_markers (and free unused resources) in case of failure. */
+ /* Adjust GC_markers_m1 (and free unused resources) if failed. */
# ifdef DONT_USE_SIGNALANDWAIT
- while ((int)GC_markers > i + 1) {
- GC_markers--;
- CloseHandle(GC_marker_cv[(int)GC_markers - 1]);
+ while (GC_markers_m1 > i) {
+ GC_markers_m1--;
+ CloseHandle(GC_marker_cv[GC_markers_m1]);
}
# else
- GC_markers = i + 1;
+ GC_markers_m1 = i;
# endif
+ GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
if (i == 0) {
- GC_parallel = FALSE;
CloseHandle(mark_cv);
CloseHandle(builder_cv);
CloseHandle(mark_mutex_event);
}
}
+# ifdef GC_ASSERTIONS
+ STATIC DWORD GC_mark_lock_holder = NO_THREAD;
+# define SET_MARK_LOCK_HOLDER \
+ (void)(GC_mark_lock_holder = GetCurrentThreadId())
+# define UNSET_MARK_LOCK_HOLDER \
+ do { \
+ GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); \
+ GC_mark_lock_holder = NO_THREAD; \
+ } while (0)
+# endif /* GC_ASSERTIONS */
+
# ifdef DONT_USE_SIGNALANDWAIT
STATIC /* volatile */ LONG GC_mark_mutex_state = 0;
/* Mutex state: 0 - unlocked, */
@@ -1808,12 +1982,13 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* #define LOCK_STATS */
# ifdef LOCK_STATS
- AO_t GC_block_count = 0;
- AO_t GC_unlocked_count = 0;
+ volatile AO_t GC_block_count = 0;
+ volatile AO_t GC_unlocked_count = 0;
# endif
GC_INNER void GC_acquire_mark_lock(void)
{
+ GC_ASSERT(GC_mark_lock_holder != GetCurrentThreadId());
# ifdef DONT_USE_SIGNALANDWAIT
if (InterlockedExchange(&GC_mark_mutex_state, 1 /* locked */) != 0)
# else
@@ -1830,7 +2005,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# endif
{
if (WaitForSingleObject(mark_mutex_event, INFINITE) == WAIT_FAILED)
- ABORT("WaitForSingleObject() failed");
+ ABORT("WaitForSingleObject failed");
}
}
# ifdef LOCK_STATS
@@ -1840,17 +2015,12 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
# endif
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = (unsigned long)GetCurrentThreadId();
-# endif
+ SET_MARK_LOCK_HOLDER;
}
GC_INNER void GC_release_mark_lock(void)
{
- GC_ASSERT(GC_mark_lock_holder == (unsigned long)GetCurrentThreadId());
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
+ UNSET_MARK_LOCK_HOLDER;
# ifdef DONT_USE_SIGNALANDWAIT
if (InterlockedExchange(&GC_mark_mutex_state, 0 /* unlocked */) < 0)
# else
@@ -1860,7 +2030,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
{
/* wake a waiter */
if (SetEvent(mark_mutex_event) == FALSE)
- ABORT("SetEvent() failed");
+ ABORT("SetEvent failed");
}
}
@@ -1879,21 +2049,21 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
if (GC_fl_builder_count == 0)
break;
if (ResetEvent(builder_cv) == FALSE)
- ABORT("ResetEvent() failed");
+ ABORT("ResetEvent failed");
GC_release_mark_lock();
if (WaitForSingleObject(builder_cv, INFINITE) == WAIT_FAILED)
- ABORT("WaitForSingleObject() failed");
+ ABORT("WaitForSingleObject failed");
}
GC_release_mark_lock();
}
GC_INNER void GC_notify_all_builder(void)
{
- GC_ASSERT(GC_mark_lock_holder == (unsigned long)GetCurrentThreadId());
+ GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId());
GC_ASSERT(builder_cv != 0);
GC_ASSERT(GC_fl_builder_count == 0);
if (SetEvent(builder_cv) == FALSE)
- ABORT("SetEvent() failed");
+ ABORT("SetEvent failed");
}
# ifdef DONT_USE_SIGNALANDWAIT
@@ -1904,7 +2074,8 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
{
HANDLE event = mark_cv;
DWORD thread_id = GetCurrentThreadId();
- int i = (int)GC_markers - 1;
+ int i = GC_markers_m1;
+
while (i-- > 0) {
if (GC_marker_Id[i] == thread_id) {
event = GC_marker_cv[i];
@@ -1913,22 +2084,23 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
}
if (ResetEvent(event) == FALSE)
- ABORT("ResetEvent() failed");
+ ABORT("ResetEvent failed");
GC_release_mark_lock();
if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED)
- ABORT("WaitForSingleObject() failed");
+ ABORT("WaitForSingleObject failed");
GC_acquire_mark_lock();
}
GC_INNER void GC_notify_all_marker(void)
{
DWORD thread_id = GetCurrentThreadId();
- int i = (int)GC_markers - 1;
+ int i = GC_markers_m1;
+
while (i-- > 0) {
/* Notify every marker ignoring self (for efficiency). */
if (SetEvent(GC_marker_Id[i] != thread_id ? GC_marker_cv[i] :
mark_cv) == FALSE)
- ABORT("SetEvent() failed");
+ ABORT("SetEvent failed");
}
}
@@ -1938,14 +2110,14 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* does not work because they are used with different checked */
/* conditions in different places (and, in addition, notifying is */
/* done after leaving critical section) and this could result in */
- /* a signal loosing between checking for a particular condition */
+ /* a signal losing between checking for a particular condition */
/* and calling WaitForSingleObject. So, we use PulseEvent() and */
/* NT SignalObjectAndWait() (which atomically sets mutex event to */
/* signaled state and starts waiting on condvar). A special */
/* case here is GC_mark_mutex_waitcnt == 1 (i.e. nobody waits for */
/* mark lock at this moment) - we don't change it (otherwise we */
- /* may loose a signal sent between decrementing */
- /* GC_mark_mutex_waitcnt and calling WaitForSingleObject()). */
+ /* may lose a signal sent between decrementing mark_mutex_waitcnt */
+ /* and calling WaitForSingleObject). */
# ifdef MSWINCE
/* SignalObjectAndWait() is missing in WinCE (for now), so you */
@@ -1968,11 +2140,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
/* We inline GC_release_mark_lock() to have atomic */
/* unlock-and-wait action here. */
- GC_ASSERT(GC_mark_lock_holder == (unsigned long)GetCurrentThreadId());
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = NO_THREAD;
-# endif
-
+ UNSET_MARK_LOCK_HOLDER;
if ((waitcnt = AO_load(&GC_mark_mutex_waitcnt)) > 1) {
(void)AO_fetch_and_sub1_release(&GC_mark_mutex_waitcnt);
} else {
@@ -1984,7 +2152,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
mark_cv /* hObjectToWaitOn */,
INFINITE /* timeout */,
FALSE /* isAlertable */) == WAIT_FAILED)
- ABORT("SignalObjectAndWait() failed");
+ ABORT("SignalObjectAndWait failed");
/* The state of mark_cv is non-signaled here again. */
if (waitcnt > 1) {
@@ -1993,11 +2161,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_ASSERT(GC_mark_mutex_waitcnt != 0);
/* Acquire mark lock */
if (WaitForSingleObject(mark_mutex_event, INFINITE) == WAIT_FAILED)
- ABORT("WaitForSingleObject() failed");
+ ABORT("WaitForSingleObject failed");
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
-# ifdef GC_ASSERTIONS
- GC_mark_lock_holder = (unsigned long)GetCurrentThreadId();
-# endif
+ SET_MARK_LOCK_HOLDER;
}
}
@@ -2005,7 +2171,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
{
GC_ASSERT(mark_cv != 0);
if (PulseEvent(mark_cv) == FALSE)
- ABORT("PulseEvent() failed");
+ ABORT("PulseEvent failed");
}
# endif /* !DONT_USE_SIGNALANDWAIT */
@@ -2067,7 +2233,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
- DWORD dwStackSize,
+ GC_WIN32_SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlags,
LPDWORD lpThreadId)
@@ -2113,7 +2279,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
ExitThread(dwExitCode);
}
-# ifndef MSWINCE
+# if !defined(MSWINCE) && !defined(CYGWIN32)
GC_API GC_uintptr_t GC_CALL GC_beginthreadex(
void *security, unsigned stack_size,
@@ -2167,7 +2333,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
_endthreadex(retval);
}
-# endif /* !MSWINCE */
+# endif /* !MSWINCE && !CYGWIN32 */
#ifdef GC_WINMAIN_REDIRECT
/* This might be useful on WinCE. Shouldn't be used with GC_DLL. */
@@ -2256,9 +2422,26 @@ GC_INNER void GC_thr_init(void)
GC_ASSERT(I_HOLD_LOCK());
if (GC_thr_initialized) return;
+
+ GC_ASSERT((word)&GC_threads % sizeof(word) == 0);
GC_main_thread = GetCurrentThreadId();
GC_thr_initialized = TRUE;
+# ifdef CAN_HANDLE_FORK
+ /* Prepare for forks if requested. */
+ if (GC_handle_fork) {
+# ifdef CAN_CALL_ATFORK
+ if (pthread_atfork(fork_prepare_proc, fork_parent_proc,
+ fork_child_proc) == 0) {
+ /* Handlers successfully registered. */
+ GC_handle_fork = 1;
+ } else
+# endif
+ /* else */ if (GC_handle_fork != -1)
+ ABORT("pthread_atfork failed");
+ }
+# endif
+
/* Add the initial thread, so we can stop it. */
# ifdef GC_ASSERTIONS
sb_result =
@@ -2267,20 +2450,21 @@ GC_INNER void GC_thr_init(void)
GC_ASSERT(sb_result == GC_SUCCESS);
# if defined(PARALLEL_MARK)
- /* Set GC_markers. */
{
char * markers_string = GETENV("GC_MARKERS");
+ int markers_m1;
+
if (markers_string != NULL) {
- GC_markers = atoi(markers_string);
- if (GC_markers > MAX_MARKERS) {
+ markers_m1 = atoi(markers_string) - 1;
+ if (markers_m1 >= MAX_MARKERS) {
WARN("Limiting number of mark threads\n", 0);
- GC_markers = MAX_MARKERS;
+ markers_m1 = MAX_MARKERS - 1;
}
} else {
# ifdef MSWINCE
/* There is no GetProcessAffinityMask() in WinCE. */
/* GC_sysinfo is already initialized. */
- GC_markers = GC_sysinfo.dwNumberOfProcessors;
+ markers_m1 = (int)GC_sysinfo.dwNumberOfProcessors - 1;
# else
# ifdef _WIN64
DWORD_PTR procMask = 0;
@@ -2297,26 +2481,27 @@ GC_INNER void GC_thr_init(void)
ncpu++;
} while ((procMask &= procMask - 1) != 0);
}
- GC_markers = ncpu;
+ markers_m1 = ncpu - 1;
# endif
# ifdef GC_MIN_MARKERS
/* This is primarily for testing on systems without getenv(). */
- if (GC_markers < GC_MIN_MARKERS)
- GC_markers = GC_MIN_MARKERS;
+ if (markers_m1 < GC_MIN_MARKERS - 1)
+ markers_m1 = GC_MIN_MARKERS - 1;
# endif
- if (GC_markers >= MAX_MARKERS)
- GC_markers = MAX_MARKERS; /* silently limit GC_markers value */
+ if (markers_m1 >= MAX_MARKERS)
+ markers_m1 = MAX_MARKERS - 1; /* silently limit the value */
}
+ available_markers_m1 = markers_m1;
}
- /* Set GC_parallel. */
+ /* Check whether parallel mode could be enabled. */
{
# if !defined(GC_PTHREADS_PARAMARK) && !defined(MSWINCE) \
&& !defined(DONT_USE_SIGNALANDWAIT)
HMODULE hK32;
/* SignalObjectAndWait() API call works only under NT. */
# endif
- if (GC_win32_dll_threads || GC_markers <= 1
+ if (GC_win32_dll_threads || available_markers_m1 <= 0
# if !defined(GC_PTHREADS_PARAMARK) && !defined(MSWINCE) \
&& !defined(DONT_USE_SIGNALANDWAIT)
|| GC_wnt == FALSE
@@ -2327,7 +2512,8 @@ GC_INNER void GC_thr_init(void)
) {
/* Disable parallel marking. */
GC_parallel = FALSE;
- GC_markers = 1;
+ GC_COND_LOG_PRINTF(
+ "Single marker thread, turning off parallel marking\n");
} else {
# ifndef GC_PTHREADS_PARAMARK
/* Initialize Win32 event objects for parallel marking. */
@@ -2341,9 +2527,8 @@ GC_INNER void GC_thr_init(void)
FALSE /* initialState */, NULL /* name */);
if (mark_mutex_event == (HANDLE)0 || builder_cv == (HANDLE)0
|| mark_cv == (HANDLE)0)
- ABORT("CreateEvent() failed");
+ ABORT("CreateEvent failed");
# endif
- GC_parallel = TRUE;
/* Disable true incremental collection, but generational is OK. */
GC_time_limit = GC_TIME_UNLIMITED;
}
@@ -2354,10 +2539,12 @@ GC_INNER void GC_thr_init(void)
GC_register_my_thread_inner(&sb, GC_main_thread);
# ifdef PARALLEL_MARK
- /* If we are using a parallel marker, actually start helper threads. */
- if (GC_parallel) start_mark_threads();
- if (GC_print_stats) {
- GC_log_printf("Started %ld mark helper threads\n", GC_markers - 1);
+# ifndef CAN_HANDLE_FORK
+ if (GC_parallel)
+# endif
+ {
+ /* If we are using a parallel marker, actually start helper threads. */
+ start_mark_threads();
}
# endif
}
@@ -2374,18 +2561,17 @@ GC_INNER void GC_thr_init(void)
{
int result;
GC_thread t;
+ DCL_LOCK_STATE;
+ GC_ASSERT(!GC_win32_dll_threads);
# ifdef DEBUG_THREADS
GC_log_printf("thread %p(0x%lx) is joining thread %p\n",
GC_PTHREAD_PTRVAL(pthread_self()),
(long)GetCurrentThreadId(), GC_PTHREAD_PTRVAL(pthread_id));
# endif
- if (!EXPECT(parallel_initialized, TRUE))
- GC_init_parallel();
-
/* Thread being joined might not have registered itself yet. */
- /* After the join,thread id may have been recycled. */
+ /* After the join, thread id may have been recycled. */
/* FIXME: It would be better if this worked more like */
/* pthread_support.c. */
# ifndef GC_WIN32_PTHREADS
@@ -2398,15 +2584,12 @@ GC_INNER void GC_thr_init(void)
# ifdef GC_WIN32_PTHREADS
/* win32_pthreads id are unique */
t = GC_lookup_pthread(pthread_id);
+ if (NULL == t) ABORT("Thread not registered");
# endif
-
- if (!GC_win32_dll_threads) {
- DCL_LOCK_STATE;
-
- LOCK();
- GC_delete_gc_thread(t);
- UNLOCK();
- } /* otherwise DllMain handles it. */
+ LOCK();
+ GC_delete_gc_thread_no_free(t);
+ GC_INTERNAL_FREE(t);
+ UNLOCK();
# ifdef DEBUG_THREADS
GC_log_printf("thread %p(0x%lx) completed join with thread %p\n",
@@ -2422,14 +2605,13 @@ GC_INNER void GC_thr_init(void)
GC_PTHREAD_CREATE_CONST pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
+ int result;
+ struct start_info * si;
+
if (!EXPECT(parallel_initialized, TRUE))
GC_init_parallel();
/* make sure GC is initialized (i.e. main thread is attached) */
- if (GC_win32_dll_threads) {
- return pthread_create(new_thread, attr, start_routine, arg);
- } else {
- int result;
- struct start_info * si;
+ GC_ASSERT(!GC_win32_dll_threads);
/* This is otherwise saved only in an area mmapped by the thread */
/* library, which isn't visible to the collector. */
@@ -2456,7 +2638,6 @@ GC_INNER void GC_thr_init(void)
GC_free(si);
}
return(result);
- }
}
STATIC void * GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base *sb,
@@ -2493,7 +2674,7 @@ GC_INNER void GC_thr_init(void)
start = si -> start_routine;
start_arg = si -> arg;
- GC_free(si); /* was allocated uncollectable */
+ GC_free(si); /* was allocated uncollectible */
pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
result = (*start)(start_arg);
@@ -2525,7 +2706,9 @@ GC_INNER void GC_thr_init(void)
# endif
LOCK();
+ GC_wait_for_gc_completion(FALSE);
# if defined(THREAD_LOCAL_ALLOC)
+ GC_ASSERT(GC_getspecific(GC_thread_key) == &me->tlfs);
GC_destroy_thread_local(&(me->tlfs));
# endif
if (me -> flags & DETACHED) {
@@ -2534,6 +2717,10 @@ GC_INNER void GC_thr_init(void)
/* deallocate it as part of join */
me -> flags |= FINISHED;
}
+# if defined(THREAD_LOCAL_ALLOC)
+ /* It is required to call remove_specific defined in specific.c. */
+ GC_remove_specific(GC_thread_key);
+# endif
UNLOCK();
}
@@ -2543,8 +2730,6 @@ GC_INNER void GC_thr_init(void)
GC_API int GC_pthread_sigmask(int how, const sigset_t *set,
sigset_t *oset)
{
- if (!EXPECT(parallel_initialized, TRUE))
- GC_init_parallel();
return pthread_sigmask(how, set, oset);
}
# endif /* !GC_NO_PTHREAD_SIGMASK */
@@ -2555,27 +2740,26 @@ GC_INNER void GC_thr_init(void)
GC_thread t;
DCL_LOCK_STATE;
- if (!EXPECT(parallel_initialized, TRUE))
- GC_init_parallel();
+ GC_ASSERT(!GC_win32_dll_threads);
LOCK();
t = GC_lookup_pthread(thread);
UNLOCK();
result = pthread_detach(thread);
if (result == 0) {
+ if (NULL == t) ABORT("Thread not registered");
LOCK();
t -> flags |= DETACHED;
/* Here the pthread thread id may have been recycled. */
if ((t -> flags & FINISHED) != 0) {
- GC_delete_gc_thread(t);
+ GC_delete_gc_thread_no_free(t);
+ GC_INTERNAL_FREE(t);
}
UNLOCK();
}
return result;
}
-#else /* !GC_PTHREADS */
-
-# ifndef GC_NO_THREADS_DISCOVERY
+#elif !defined(GC_NO_THREADS_DISCOVERY)
/* We avoid acquiring locks here, since this doesn't seem to be */
/* preemptible. This may run with an uninitialized collector, in */
/* which case we don't do much. This implies that no threads other */
@@ -2583,15 +2767,15 @@ GC_INNER void GC_thr_init(void)
/* collector. (The alternative of initializing the collector here */
/* seems dangerous, since DllMain is limited in what it can do.) */
-# ifdef GC_INSIDE_DLL
- /* Export only if needed by client. */
- GC_API
-# else
-# define GC_DllMain DllMain
-# endif
- BOOL WINAPI GC_DllMain(HINSTANCE inst GC_ATTR_UNUSED, ULONG reason,
- LPVOID reserved GC_ATTR_UNUSED)
- {
+# ifdef GC_INSIDE_DLL
+ /* Export only if needed by client. */
+ GC_API
+# else
+# define GC_DllMain DllMain
+# endif
+ BOOL WINAPI GC_DllMain(HINSTANCE inst GC_ATTR_UNUSED, ULONG reason,
+ LPVOID reserved GC_ATTR_UNUSED)
+ {
DWORD thread_id;
static int entry_count = 0;
@@ -2642,7 +2826,7 @@ GC_INNER void GC_thr_init(void)
for (i = 0; i <= my_max; ++i) {
if (AO_load(&(dll_thread_table[i].tm.in_use)))
- GC_delete_gc_thread(dll_thread_table + i);
+ GC_delete_gc_thread_no_free(&dll_thread_table[i]);
}
GC_deinit();
DeleteCriticalSection(&GC_allocate_ml);
@@ -2650,10 +2834,8 @@ GC_INNER void GC_thr_init(void)
break;
}
return TRUE;
- }
-# endif /* !GC_NO_THREADS_DISCOVERY */
-
-#endif /* !GC_PTHREADS */
+ }
+#endif /* !GC_NO_THREADS_DISCOVERY && !GC_PTHREADS */
/* Perform all initializations, including those that */
/* may require allocation. */
diff --git a/windows-untested/vc60/gc.dsp b/windows-untested/vc60/gc.dsp
index b3169eb7..762a2220 100644
--- a/windows-untested/vc60/gc.dsp
+++ b/windows-untested/vc60/gc.dsp
@@ -7,19 +7,19 @@
CFG=gc - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "gc.mak".
-!MESSAGE
+!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "gc.mak" CFG="gc - Win32 Debug"
-!MESSAGE
+!MESSAGE
!MESSAGE Possible choices for configuration are:
-!MESSAGE
+!MESSAGE
!MESSAGE "gc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "gc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
+!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
@@ -43,7 +43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GC_EXPORTS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_BUILD" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
@@ -75,7 +75,7 @@ PostBuild_Cmds=del $(OutDir)\..\lib\gc.exp
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GC_EXPORTS" /YX /FD /GZ /c
-# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_BUILD" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "GC_DLL" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
@@ -93,7 +93,7 @@ SOURCE="$(InputPath)"
PostBuild_Cmds=del $(OutDir)\..\lib\gcd.exp
# End Special Build Tool
-!ENDIF
+!ENDIF
# Begin Target
diff --git a/windows-untested/vc60/libgc.dsp b/windows-untested/vc60/libgc.dsp
index 40cb4d65..f7d77df5 100644
--- a/windows-untested/vc60/libgc.dsp
+++ b/windows-untested/vc60/libgc.dsp
@@ -7,19 +7,19 @@
CFG=libgc - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "libgc.mak".
-!MESSAGE
+!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "libgc.mak" CFG="libgc - Win32 Debug"
-!MESSAGE
+!MESSAGE
!MESSAGE Possible choices for configuration are:
-!MESSAGE
+!MESSAGE
!MESSAGE "libgc - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "libgc - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE
+!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
@@ -41,7 +41,7 @@ RSC=rc.exe
# PROP Intermediate_Dir "..\..\..\obj\Release\libgc"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_BUILD" /Yu"stdafx.h" /Fd"..\..\..\lib\libgc.pdb" /FD /c
+# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgc.pdb" /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@@ -70,7 +70,7 @@ PostBuild_Cmds=del $(OutDir)\$(TargetName).idb
# PROP Intermediate_Dir "..\..\..\obj\Debug\libgc"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_BUILD" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcd.pdb" /FD /GZ /c
+# ADD CPP /nologo /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcd.pdb" /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@@ -86,7 +86,7 @@ SOURCE="$(InputPath)"
PostBuild_Cmds=del $(OutDir)\$(TargetName).idb
# End Special Build Tool
-!ENDIF
+!ENDIF
# Begin Target
diff --git a/windows-untested/vc60/libgcmt.dsp b/windows-untested/vc60/libgcmt.dsp
index 14b6fbe2..72d3658c 100644
--- a/windows-untested/vc60/libgcmt.dsp
+++ b/windows-untested/vc60/libgcmt.dsp
@@ -7,19 +7,19 @@
CFG=libgcmt - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "libgcmt.mak".
-!MESSAGE
+!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
+!MESSAGE
!MESSAGE NMAKE /f "libgcmt.mak" CFG="libgcmt - Win32 Debug"
-!MESSAGE
+!MESSAGE
!MESSAGE Possible choices for configuration are:
-!MESSAGE
+!MESSAGE
!MESSAGE "libgcmt - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "libgcmt - Win32 Debug" (based on "Win32 (x86) Static Library")
-!MESSAGE
+!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
@@ -41,7 +41,7 @@ RSC=rc.exe
# PROP Intermediate_Dir "..\..\..\obj\Release\libgcmt"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "GC_BUILD" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmt.pdb" /Zl /FD /c
+# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /FI"stdafx.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmt.pdb" /Zl /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@@ -70,7 +70,7 @@ PostBuild_Cmds=del $(OutDir)\$(TargetName).idb
# PROP Intermediate_Dir "..\..\..\obj\Debug\libgcmt"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "GC_BUILD" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmtd.pdb" /Zl /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I "..\..\include" /FI"stdafx.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /D "GC_THREADS" /Yu"stdafx.h" /Fd"..\..\..\lib\libgcmtd.pdb" /Zl /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@@ -86,7 +86,7 @@ SOURCE="$(InputPath)"
PostBuild_Cmds=del $(OutDir)\$(TargetName).idb
# End Special Build Tool
-!ENDIF
+!ENDIF
# Begin Target
diff --git a/windows-untested/vc70/gc.vcproj b/windows-untested/vc70/gc.vcproj
index c143e76b..9f18300e 100644
--- a/windows-untested/vc70/gc.vcproj
+++ b/windows-untested/vc70/gc.vcproj
@@ -22,7 +22,7 @@
Name="VCCLCompilerTool"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;GC_THREADS"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;GC_THREADS"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"
@@ -89,7 +89,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;GC_THREADS"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;GC_THREADS"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="3"
diff --git a/windows-untested/vc70/libgc.vcproj b/windows-untested/vc70/libgc.vcproj
index 10deed7c..56847eb9 100644
--- a/windows-untested/vc70/libgc.vcproj
+++ b/windows-untested/vc70/libgc.vcproj
@@ -22,7 +22,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG,_LIB,WIN32,GC_BUILD"
+ PreprocessorDefinitions="_DEBUG,_LIB,WIN32"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
@@ -70,7 +70,7 @@
Name="VCCLCompilerTool"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG,_LIB,WIN32,GC_BUILD"
+ PreprocessorDefinitions="NDEBUG,_LIB,WIN32"
StringPooling="TRUE"
RuntimeLibrary="4"
EnableFunctionLevelLinking="TRUE"
diff --git a/windows-untested/vc70/libgcmt.vcproj b/windows-untested/vc70/libgcmt.vcproj
index 446180de..ab574007 100644
--- a/windows-untested/vc70/libgcmt.vcproj
+++ b/windows-untested/vc70/libgcmt.vcproj
@@ -22,7 +22,7 @@
Name="VCCLCompilerTool"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG,_LIB,GC_BUILD,WIN32,GC_THREADS"
+ PreprocessorDefinitions="NDEBUG,_LIB,WIN32,GC_THREADS"
StringPooling="TRUE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="TRUE"
@@ -71,7 +71,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG,_LIB,GC_BUILD,WIN32,GC_THREADS"
+ PreprocessorDefinitions="_DEBUG,_LIB,WIN32,GC_THREADS"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="3"
diff --git a/windows-untested/vc71/gc.vcproj b/windows-untested/vc71/gc.vcproj
index d33e9a3a..af5598f8 100644
--- a/windows-untested/vc71/gc.vcproj
+++ b/windows-untested/vc71/gc.vcproj
@@ -23,7 +23,7 @@
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;GC_THREADS"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;GC_THREADS"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"
@@ -97,7 +97,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;GC_THREADS"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;GC_THREADS"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="3"
@@ -171,7 +171,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -180,7 +180,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -193,7 +193,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -202,7 +202,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -215,7 +215,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -224,7 +224,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -237,7 +237,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -246,7 +246,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -259,7 +259,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -268,7 +268,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -281,7 +281,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -290,7 +290,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -303,7 +303,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -312,7 +312,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -325,7 +325,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -334,7 +334,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -347,7 +347,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -356,7 +356,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -369,7 +369,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -378,7 +378,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -391,7 +391,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -400,7 +400,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -413,7 +413,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -422,7 +422,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -435,7 +435,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -444,7 +444,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -457,7 +457,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -466,7 +466,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -479,7 +479,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -488,7 +488,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -501,7 +501,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -510,7 +510,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -523,7 +523,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -532,7 +532,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -545,7 +545,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -554,7 +554,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -567,7 +567,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -576,7 +576,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -589,7 +589,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -598,7 +598,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -611,7 +611,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -620,7 +620,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -633,7 +633,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -642,7 +642,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -655,7 +655,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -664,7 +664,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -677,7 +677,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
UsePrecompiledHeader="1"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -687,7 +687,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
UsePrecompiledHeader="1"
ForcedIncludeFiles=""/>
@@ -701,7 +701,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -710,7 +710,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -723,7 +723,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -732,7 +732,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
@@ -745,7 +745,7 @@
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
ForcedIncludeFiles=""/>
</FileConfiguration>
<FileConfiguration
@@ -754,7 +754,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
- PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_BUILD;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
+ PreprocessorDefinitions="_DEBUG;_WINDOWS;_USRDLL;GC_DLL;WIN32;_MBCS;GC_THREADS;$(NoInherit)"
BasicRuntimeChecks="3"
ForcedIncludeFiles=""/>
</FileConfiguration>
diff --git a/windows-untested/vc71/libgc.vcproj b/windows-untested/vc71/libgc.vcproj
index 6c5bfdce..f0cc9086 100644
--- a/windows-untested/vc71/libgc.vcproj
+++ b/windows-untested/vc71/libgc.vcproj
@@ -23,7 +23,7 @@
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG;_LIB;WIN32;GC_BUILD"
+ PreprocessorDefinitions="NDEBUG;_LIB;WIN32"
StringPooling="TRUE"
RuntimeLibrary="4"
EnableFunctionLevelLinking="TRUE"
@@ -78,7 +78,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG;_LIB;WIN32;GC_BUILD"
+ PreprocessorDefinitions="_DEBUG;_LIB;WIN32"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
diff --git a/windows-untested/vc71/libgcmt.vcproj b/windows-untested/vc71/libgcmt.vcproj
index 96c526be..1005eb97 100644
--- a/windows-untested/vc71/libgcmt.vcproj
+++ b/windows-untested/vc71/libgcmt.vcproj
@@ -22,7 +22,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="_DEBUG;_LIB;GC_BUILD;WIN32;GC_THREADS"
+ PreprocessorDefinitions="_DEBUG;_LIB;WIN32;GC_THREADS"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="3"
@@ -77,7 +77,7 @@
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
- PreprocessorDefinitions="NDEBUG;_LIB;GC_BUILD;WIN32;GC_THREADS"
+ PreprocessorDefinitions="NDEBUG;_LIB;WIN32;GC_THREADS"
StringPooling="TRUE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="TRUE"